Commit 40bcea7b authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'tip/perf/core' of...

Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into perf/core
parents 492f73a3 14a8fd7c
...@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via ...@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via
Synopsis of kprobe_events Synopsis of kprobe_events
------------------------- -------------------------
p[:[GRP/]EVENT] SYMBOL[+offs]|MEMADDR [FETCHARGS] : Set a probe p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
r[:[GRP/]EVENT] SYMBOL[+0] [FETCHARGS] : Set a return probe r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
-:[GRP/]EVENT : Clear a probe -:[GRP/]EVENT : Clear a probe
GRP : Group name. If omitted, use "kprobes" for it. GRP : Group name. If omitted, use "kprobes" for it.
EVENT : Event name. If omitted, the event name is generated EVENT : Event name. If omitted, the event name is generated
based on SYMBOL+offs or MEMADDR. based on SYM+offs or MEMADDR.
SYMBOL[+offs] : Symbol+offset where the probe is inserted. MOD : Module name which has given SYM.
SYM[+offs] : Symbol+offset where the probe is inserted.
MEMADDR : Address where the probe is inserted. MEMADDR : Address where the probe is inserted.
FETCHARGS : Arguments. Each probe can have up to 128 args. FETCHARGS : Arguments. Each probe can have up to 128 args.
......
...@@ -101,6 +101,14 @@ ...@@ -101,6 +101,14 @@
#define P4_CONFIG_HT_SHIFT 63 #define P4_CONFIG_HT_SHIFT 63
#define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT) #define P4_CONFIG_HT (1ULL << P4_CONFIG_HT_SHIFT)
/*
* If an event has alias it should be marked
* with a special bit. (Don't forget to check
* P4_PEBS_CONFIG_MASK and related bits on
* modification.)
*/
#define P4_CONFIG_ALIASABLE (1 << 9)
/* /*
* The bits we allow to pass for RAW events * The bits we allow to pass for RAW events
*/ */
...@@ -123,6 +131,31 @@ ...@@ -123,6 +131,31 @@
(p4_config_pack_escr(P4_CONFIG_MASK_ESCR)) | \ (p4_config_pack_escr(P4_CONFIG_MASK_ESCR)) | \
(p4_config_pack_cccr(P4_CONFIG_MASK_CCCR)) (p4_config_pack_cccr(P4_CONFIG_MASK_CCCR))
/*
* In case of event aliasing we need to preserve some
* caller bits otherwise the mapping won't be complete.
*/
#define P4_CONFIG_EVENT_ALIAS_MASK \
(p4_config_pack_escr(P4_CONFIG_MASK_ESCR) | \
p4_config_pack_cccr(P4_CCCR_EDGE | \
P4_CCCR_THRESHOLD_MASK | \
P4_CCCR_COMPLEMENT | \
P4_CCCR_COMPARE))
#define P4_CONFIG_EVENT_ALIAS_IMMUTABLE_BITS \
((P4_CONFIG_HT) | \
p4_config_pack_escr(P4_ESCR_T0_OS | \
P4_ESCR_T0_USR | \
P4_ESCR_T1_OS | \
P4_ESCR_T1_USR) | \
p4_config_pack_cccr(P4_CCCR_OVF | \
P4_CCCR_CASCADE | \
P4_CCCR_FORCE_OVF | \
P4_CCCR_THREAD_ANY | \
P4_CCCR_OVF_PMI_T0 | \
P4_CCCR_OVF_PMI_T1 | \
P4_CONFIG_ALIASABLE))
static inline bool p4_is_event_cascaded(u64 config) static inline bool p4_is_event_cascaded(u64 config)
{ {
u32 cccr = p4_config_unpack_cccr(config); u32 cccr = p4_config_unpack_cccr(config);
......
...@@ -274,7 +274,6 @@ struct x86_pmu { ...@@ -274,7 +274,6 @@ struct x86_pmu {
void (*enable_all)(int added); void (*enable_all)(int added);
void (*enable)(struct perf_event *); void (*enable)(struct perf_event *);
void (*disable)(struct perf_event *); void (*disable)(struct perf_event *);
void (*hw_watchdog_set_attr)(struct perf_event_attr *attr);
int (*hw_config)(struct perf_event *event); int (*hw_config)(struct perf_event *event);
int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign); int (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
unsigned eventsel; unsigned eventsel;
...@@ -360,12 +359,6 @@ static u64 __read_mostly hw_cache_extra_regs ...@@ -360,12 +359,6 @@ static u64 __read_mostly hw_cache_extra_regs
[PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX]; [PERF_COUNT_HW_CACHE_RESULT_MAX];
void hw_nmi_watchdog_set_attr(struct perf_event_attr *wd_attr)
{
if (x86_pmu.hw_watchdog_set_attr)
x86_pmu.hw_watchdog_set_attr(wd_attr);
}
/* /*
* Propagate event elapsed time into the generic event. * Propagate event elapsed time into the generic event.
* Can only be executed on the CPU where the event is active. * Can only be executed on the CPU where the event is active.
......
...@@ -570,11 +570,92 @@ static __initconst const u64 p4_hw_cache_event_ids ...@@ -570,11 +570,92 @@ static __initconst const u64 p4_hw_cache_event_ids
}, },
}; };
/*
* Because of Netburst being quite restricted in now
* many same events can run simultaneously, we use
* event aliases, ie different events which have the
* same functionallity but use non-intersected resources
* (ESCR/CCCR/couter registers). This allow us to run
* two or more semi-same events together. It is done
* transparently to a user space.
*
* Never set any cusom internal bits such as P4_CONFIG_HT,
* P4_CONFIG_ALIASABLE or bits for P4_PEBS_METRIC, they are
* either up-to-dated automatically either not appliable
* at all.
*
* And be really carefull choosing aliases!
*/
struct p4_event_alias {
u64 orig;
u64 alter;
} p4_event_aliases[] = {
{
/*
* Non-halted cycles can be substituted with
* non-sleeping cycles (see Intel SDM Vol3b for
* details).
*/
.orig =
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) |
P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)),
.alter =
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_EXECUTION_EVENT) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS0)|
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS1)|
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS2)|
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS3)|
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS0) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS1) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS2) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS3))|
p4_config_pack_cccr(P4_CCCR_THRESHOLD(15) | P4_CCCR_COMPLEMENT |
P4_CCCR_COMPARE),
},
};
static u64 p4_get_alias_event(u64 config)
{
u64 config_match;
int i;
/*
* Probably we're lucky and don't have to do
* matching over all config bits.
*/
if (!(config & P4_CONFIG_ALIASABLE))
return 0;
config_match = config & P4_CONFIG_EVENT_ALIAS_MASK;
/*
* If an event was previously swapped to the alter config
* we should swap it back otherwise contnention on registers
* will return back.
*/
for (i = 0; i < ARRAY_SIZE(p4_event_aliases); i++) {
if (config_match == p4_event_aliases[i].orig) {
config_match = p4_event_aliases[i].alter;
break;
} else if (config_match == p4_event_aliases[i].alter) {
config_match = p4_event_aliases[i].orig;
break;
}
}
if (i >= ARRAY_SIZE(p4_event_aliases))
return 0;
return config_match |
(config & P4_CONFIG_EVENT_ALIAS_IMMUTABLE_BITS);
}
static u64 p4_general_events[PERF_COUNT_HW_MAX] = { static u64 p4_general_events[PERF_COUNT_HW_MAX] = {
/* non-halted CPU clocks */ /* non-halted CPU clocks */
[PERF_COUNT_HW_CPU_CYCLES] = [PERF_COUNT_HW_CPU_CYCLES] =
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) | p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_GLOBAL_POWER_EVENTS) |
P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)), P4_ESCR_EMASK_BIT(P4_EVENT_GLOBAL_POWER_EVENTS, RUNNING)) |
P4_CONFIG_ALIASABLE,
/* /*
* retired instructions * retired instructions
...@@ -719,31 +800,6 @@ static int p4_validate_raw_event(struct perf_event *event) ...@@ -719,31 +800,6 @@ static int p4_validate_raw_event(struct perf_event *event)
return 0; return 0;
} }
static void p4_hw_watchdog_set_attr(struct perf_event_attr *wd_attr)
{
/*
* Watchdog ticks are special on Netburst, we use
* that named "non-sleeping" ticks as recommended
* by Intel SDM Vol3b.
*/
WARN_ON_ONCE(wd_attr->type != PERF_TYPE_HARDWARE ||
wd_attr->config != PERF_COUNT_HW_CPU_CYCLES);
wd_attr->type = PERF_TYPE_RAW;
wd_attr->config =
p4_config_pack_escr(P4_ESCR_EVENT(P4_EVENT_EXECUTION_EVENT) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS0) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS1) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS2) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, NBOGUS3) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS0) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS1) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS2) |
P4_ESCR_EMASK_BIT(P4_EVENT_EXECUTION_EVENT, BOGUS3)) |
p4_config_pack_cccr(P4_CCCR_THRESHOLD(15) | P4_CCCR_COMPLEMENT |
P4_CCCR_COMPARE);
}
static int p4_hw_config(struct perf_event *event) static int p4_hw_config(struct perf_event *event)
{ {
int cpu = get_cpu(); int cpu = get_cpu();
...@@ -1159,6 +1215,8 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign ...@@ -1159,6 +1215,8 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
struct p4_event_bind *bind; struct p4_event_bind *bind;
unsigned int i, thread, num; unsigned int i, thread, num;
int cntr_idx, escr_idx; int cntr_idx, escr_idx;
u64 config_alias;
int pass;
bitmap_zero(used_mask, X86_PMC_IDX_MAX); bitmap_zero(used_mask, X86_PMC_IDX_MAX);
bitmap_zero(escr_mask, P4_ESCR_MSR_TABLE_SIZE); bitmap_zero(escr_mask, P4_ESCR_MSR_TABLE_SIZE);
...@@ -1167,6 +1225,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign ...@@ -1167,6 +1225,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
hwc = &cpuc->event_list[i]->hw; hwc = &cpuc->event_list[i]->hw;
thread = p4_ht_thread(cpu); thread = p4_ht_thread(cpu);
pass = 0;
again:
/*
* Aliases are swappable so we may hit circular
* lock if both original config and alias need
* resources (MSR registers) which already busy.
*/
if (pass > 2)
goto done;
bind = p4_config_get_bind(hwc->config); bind = p4_config_get_bind(hwc->config);
escr_idx = p4_get_escr_idx(bind->escr_msr[thread]); escr_idx = p4_get_escr_idx(bind->escr_msr[thread]);
if (unlikely(escr_idx == -1)) if (unlikely(escr_idx == -1))
...@@ -1180,8 +1249,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign ...@@ -1180,8 +1249,17 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
} }
cntr_idx = p4_next_cntr(thread, used_mask, bind); cntr_idx = p4_next_cntr(thread, used_mask, bind);
if (cntr_idx == -1 || test_bit(escr_idx, escr_mask)) if (cntr_idx == -1 || test_bit(escr_idx, escr_mask)) {
/*
* Probably an event alias is still available.
*/
config_alias = p4_get_alias_event(hwc->config);
if (!config_alias)
goto done; goto done;
hwc->config = config_alias;
pass++;
goto again;
}
p4_pmu_swap_config_ts(hwc, cpu); p4_pmu_swap_config_ts(hwc, cpu);
if (assign) if (assign)
...@@ -1218,7 +1296,6 @@ static __initconst const struct x86_pmu p4_pmu = { ...@@ -1218,7 +1296,6 @@ static __initconst const struct x86_pmu p4_pmu = {
.cntval_bits = ARCH_P4_CNTRVAL_BITS, .cntval_bits = ARCH_P4_CNTRVAL_BITS,
.cntval_mask = ARCH_P4_CNTRVAL_MASK, .cntval_mask = ARCH_P4_CNTRVAL_MASK,
.max_period = (1ULL << (ARCH_P4_CNTRVAL_BITS - 1)) - 1, .max_period = (1ULL << (ARCH_P4_CNTRVAL_BITS - 1)) - 1,
.hw_watchdog_set_attr = p4_hw_watchdog_set_attr,
.hw_config = p4_hw_config, .hw_config = p4_hw_config,
.schedule_events = p4_pmu_schedule_events, .schedule_events = p4_pmu_schedule_events,
/* /*
......
...@@ -76,6 +76,7 @@ struct trace_iterator { ...@@ -76,6 +76,7 @@ struct trace_iterator {
struct trace_entry *ent; struct trace_entry *ent;
unsigned long lost_events; unsigned long lost_events;
int leftover; int leftover;
int ent_size;
int cpu; int cpu;
u64 ts; u64 ts;
......
...@@ -1255,19 +1255,29 @@ static int __kprobes in_kprobes_functions(unsigned long addr) ...@@ -1255,19 +1255,29 @@ static int __kprobes in_kprobes_functions(unsigned long addr)
/* /*
* If we have a symbol_name argument, look it up and add the offset field * If we have a symbol_name argument, look it up and add the offset field
* to it. This way, we can specify a relative address to a symbol. * to it. This way, we can specify a relative address to a symbol.
* This returns encoded errors if it fails to look up symbol or invalid
* combination of parameters.
*/ */
static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p) static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p)
{ {
kprobe_opcode_t *addr = p->addr; kprobe_opcode_t *addr = p->addr;
if ((p->symbol_name && p->addr) ||
(!p->symbol_name && !p->addr))
goto invalid;
if (p->symbol_name) { if (p->symbol_name) {
if (addr)
return NULL;
kprobe_lookup_name(p->symbol_name, addr); kprobe_lookup_name(p->symbol_name, addr);
if (!addr)
return ERR_PTR(-ENOENT);
} }
if (!addr) addr = (kprobe_opcode_t *)(((char *)addr) + p->offset);
return NULL; if (addr)
return (kprobe_opcode_t *)(((char *)addr) + p->offset); return addr;
invalid:
return ERR_PTR(-EINVAL);
} }
/* Check passed kprobe is valid and return kprobe in kprobe_table. */ /* Check passed kprobe is valid and return kprobe in kprobe_table. */
...@@ -1311,8 +1321,8 @@ int __kprobes register_kprobe(struct kprobe *p) ...@@ -1311,8 +1321,8 @@ int __kprobes register_kprobe(struct kprobe *p)
kprobe_opcode_t *addr; kprobe_opcode_t *addr;
addr = kprobe_addr(p); addr = kprobe_addr(p);
if (!addr) if (IS_ERR(addr))
return -EINVAL; return PTR_ERR(addr);
p->addr = addr; p->addr = addr;
ret = check_kprobe_rereg(p); ret = check_kprobe_rereg(p);
...@@ -1335,6 +1345,8 @@ int __kprobes register_kprobe(struct kprobe *p) ...@@ -1335,6 +1345,8 @@ int __kprobes register_kprobe(struct kprobe *p)
*/ */
probed_mod = __module_text_address((unsigned long) p->addr); probed_mod = __module_text_address((unsigned long) p->addr);
if (probed_mod) { if (probed_mod) {
/* Return -ENOENT if fail. */
ret = -ENOENT;
/* /*
* We must hold a refcount of the probed module while updating * We must hold a refcount of the probed module while updating
* its code to prohibit unexpected unloading. * its code to prohibit unexpected unloading.
...@@ -1351,6 +1363,7 @@ int __kprobes register_kprobe(struct kprobe *p) ...@@ -1351,6 +1363,7 @@ int __kprobes register_kprobe(struct kprobe *p)
module_put(probed_mod); module_put(probed_mod);
goto fail_with_jump_label; goto fail_with_jump_label;
} }
/* ret will be updated by following code */
} }
preempt_enable(); preempt_enable();
jump_label_unlock(); jump_label_unlock();
...@@ -1399,7 +1412,7 @@ int __kprobes register_kprobe(struct kprobe *p) ...@@ -1399,7 +1412,7 @@ int __kprobes register_kprobe(struct kprobe *p)
fail_with_jump_label: fail_with_jump_label:
preempt_enable(); preempt_enable();
jump_label_unlock(); jump_label_unlock();
return -EINVAL; return ret;
} }
EXPORT_SYMBOL_GPL(register_kprobe); EXPORT_SYMBOL_GPL(register_kprobe);
...@@ -1686,8 +1699,8 @@ int __kprobes register_kretprobe(struct kretprobe *rp) ...@@ -1686,8 +1699,8 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
if (kretprobe_blacklist_size) { if (kretprobe_blacklist_size) {
addr = kprobe_addr(&rp->kp); addr = kprobe_addr(&rp->kp);
if (!addr) if (IS_ERR(addr))
return -EINVAL; return PTR_ERR(addr);
for (i = 0; kretprobe_blacklist[i].name != NULL; i++) { for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
if (kretprobe_blacklist[i].addr == addr) if (kretprobe_blacklist[i].addr == addr)
......
...@@ -88,6 +88,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly = { ...@@ -88,6 +88,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly = {
static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end; static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub; ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
static struct ftrace_ops global_ops; static struct ftrace_ops global_ops;
...@@ -146,9 +147,11 @@ void clear_ftrace_function(void) ...@@ -146,9 +147,11 @@ void clear_ftrace_function(void)
{ {
ftrace_trace_function = ftrace_stub; ftrace_trace_function = ftrace_stub;
__ftrace_trace_function = ftrace_stub; __ftrace_trace_function = ftrace_stub;
__ftrace_trace_function_delay = ftrace_stub;
ftrace_pid_function = ftrace_stub; ftrace_pid_function = ftrace_stub;
} }
#undef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST #ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
/* /*
* For those archs that do not test ftrace_trace_stop in their * For those archs that do not test ftrace_trace_stop in their
...@@ -207,8 +210,13 @@ static void update_ftrace_function(void) ...@@ -207,8 +210,13 @@ static void update_ftrace_function(void)
#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST #ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
ftrace_trace_function = func; ftrace_trace_function = func;
#else
#ifdef CONFIG_DYNAMIC_FTRACE
/* do not update till all functions have been modified */
__ftrace_trace_function_delay = func;
#else #else
__ftrace_trace_function = func; __ftrace_trace_function = func;
#endif
ftrace_trace_function = ftrace_test_stop_func; ftrace_trace_function = ftrace_test_stop_func;
#endif #endif
} }
...@@ -1170,8 +1178,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash) ...@@ -1170,8 +1178,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
return NULL; return NULL;
} }
static void
ftrace_hash_rec_disable(struct ftrace_ops *ops, int filter_hash);
static void
ftrace_hash_rec_enable(struct ftrace_ops *ops, int filter_hash);
static int static int
ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) ftrace_hash_move(struct ftrace_ops *ops, int enable,
struct ftrace_hash **dst, struct ftrace_hash *src)
{ {
struct ftrace_func_entry *entry; struct ftrace_func_entry *entry;
struct hlist_node *tp, *tn; struct hlist_node *tp, *tn;
...@@ -1181,8 +1195,15 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) ...@@ -1181,8 +1195,15 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
unsigned long key; unsigned long key;
int size = src->count; int size = src->count;
int bits = 0; int bits = 0;
int ret;
int i; int i;
/*
* Remove the current set, update the hash and add
* them back.
*/
ftrace_hash_rec_disable(ops, enable);
/* /*
* If the new source is empty, just free dst and assign it * If the new source is empty, just free dst and assign it
* the empty_hash. * the empty_hash.
...@@ -1203,9 +1224,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) ...@@ -1203,9 +1224,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
if (bits > FTRACE_HASH_MAX_BITS) if (bits > FTRACE_HASH_MAX_BITS)
bits = FTRACE_HASH_MAX_BITS; bits = FTRACE_HASH_MAX_BITS;
ret = -ENOMEM;
new_hash = alloc_ftrace_hash(bits); new_hash = alloc_ftrace_hash(bits);
if (!new_hash) if (!new_hash)
return -ENOMEM; goto out;
size = 1 << src->size_bits; size = 1 << src->size_bits;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
...@@ -1224,7 +1246,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src) ...@@ -1224,7 +1246,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
rcu_assign_pointer(*dst, new_hash); rcu_assign_pointer(*dst, new_hash);
free_ftrace_hash_rcu(old_hash); free_ftrace_hash_rcu(old_hash);
return 0; ret = 0;
out:
/*
* Enable regardless of ret:
* On success, we enable the new hash.
* On failure, we re-enable the original hash.
*/
ftrace_hash_rec_enable(ops, enable);
return ret;
} }
/* /*
...@@ -1584,6 +1615,12 @@ static int __ftrace_modify_code(void *data) ...@@ -1584,6 +1615,12 @@ static int __ftrace_modify_code(void *data)
{ {
int *command = data; int *command = data;
/*
* Do not call function tracer while we update the code.
* We are in stop machine, no worrying about races.
*/
function_trace_stop++;
if (*command & FTRACE_ENABLE_CALLS) if (*command & FTRACE_ENABLE_CALLS)
ftrace_replace_code(1); ftrace_replace_code(1);
else if (*command & FTRACE_DISABLE_CALLS) else if (*command & FTRACE_DISABLE_CALLS)
...@@ -1597,6 +1634,18 @@ static int __ftrace_modify_code(void *data) ...@@ -1597,6 +1634,18 @@ static int __ftrace_modify_code(void *data)
else if (*command & FTRACE_STOP_FUNC_RET) else if (*command & FTRACE_STOP_FUNC_RET)
ftrace_disable_ftrace_graph_caller(); ftrace_disable_ftrace_graph_caller();
#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
/*
* For archs that call ftrace_test_stop_func(), we must
* wait till after we update all the function callers
* before we update the callback. This keeps different
* ops that record different functions from corrupting
* each other.
*/
__ftrace_trace_function = __ftrace_trace_function_delay;
#endif
function_trace_stop--;
return 0; return 0;
} }
...@@ -2865,7 +2914,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len, ...@@ -2865,7 +2914,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
ftrace_match_records(hash, buf, len); ftrace_match_records(hash, buf, len);
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
ret = ftrace_hash_move(orig_hash, hash); ret = ftrace_hash_move(ops, enable, orig_hash, hash);
if (!ret && ops->flags & FTRACE_OPS_FL_ENABLED
&& ftrace_enabled)
ftrace_run_update_code(FTRACE_ENABLE_CALLS);
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
mutex_unlock(&ftrace_regex_lock); mutex_unlock(&ftrace_regex_lock);
...@@ -3048,18 +3101,12 @@ ftrace_regex_release(struct inode *inode, struct file *file) ...@@ -3048,18 +3101,12 @@ ftrace_regex_release(struct inode *inode, struct file *file)
orig_hash = &iter->ops->notrace_hash; orig_hash = &iter->ops->notrace_hash;
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
/* ret = ftrace_hash_move(iter->ops, filter_hash,
* Remove the current set, update the hash and add orig_hash, iter->hash);
* them back. if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED)
*/
ftrace_hash_rec_disable(iter->ops, filter_hash);
ret = ftrace_hash_move(orig_hash, iter->hash);
if (!ret) {
ftrace_hash_rec_enable(iter->ops, filter_hash);
if (iter->ops->flags & FTRACE_OPS_FL_ENABLED
&& ftrace_enabled) && ftrace_enabled)
ftrace_run_update_code(FTRACE_ENABLE_CALLS); ftrace_run_update_code(FTRACE_ENABLE_CALLS);
}
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
} }
free_ftrace_hash(iter->hash); free_ftrace_hash(iter->hash);
...@@ -3338,7 +3385,7 @@ static int ftrace_process_locs(struct module *mod, ...@@ -3338,7 +3385,7 @@ static int ftrace_process_locs(struct module *mod,
{ {
unsigned long *p; unsigned long *p;
unsigned long addr; unsigned long addr;
unsigned long flags; unsigned long flags = 0; /* Shut up gcc */
mutex_lock(&ftrace_lock); mutex_lock(&ftrace_lock);
p = start; p = start;
...@@ -3356,11 +3403,17 @@ static int ftrace_process_locs(struct module *mod, ...@@ -3356,11 +3403,17 @@ static int ftrace_process_locs(struct module *mod,
} }
/* /*
* Disable interrupts to prevent interrupts from executing * We only need to disable interrupts on start up
* code that is being modified. * because we are modifying code that an interrupt
* may execute, and the modification is not atomic.
* But for modules, nothing runs the code we modify
* until we are finished with it, and there's no
* reason to cause large interrupt latencies while we do it.
*/ */
if (!mod)
local_irq_save(flags); local_irq_save(flags);
ftrace_update_code(mod); ftrace_update_code(mod);
if (!mod)
local_irq_restore(flags); local_irq_restore(flags);
mutex_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
......
...@@ -1248,6 +1248,15 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, ...@@ -1248,6 +1248,15 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data,
} }
#ifdef CONFIG_STACKTRACE #ifdef CONFIG_STACKTRACE
#define FTRACE_STACK_MAX_ENTRIES (PAGE_SIZE / sizeof(unsigned long))
struct ftrace_stack {
unsigned long calls[FTRACE_STACK_MAX_ENTRIES];
};
static DEFINE_PER_CPU(struct ftrace_stack, ftrace_stack);
static DEFINE_PER_CPU(int, ftrace_stack_reserve);
static void __ftrace_trace_stack(struct ring_buffer *buffer, static void __ftrace_trace_stack(struct ring_buffer *buffer,
unsigned long flags, unsigned long flags,
int skip, int pc, struct pt_regs *regs) int skip, int pc, struct pt_regs *regs)
...@@ -1256,25 +1265,77 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer, ...@@ -1256,25 +1265,77 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct stack_entry *entry; struct stack_entry *entry;
struct stack_trace trace; struct stack_trace trace;
int use_stack;
int size = FTRACE_STACK_ENTRIES;
trace.nr_entries = 0;
trace.skip = skip;
/*
* Since events can happen in NMIs there's no safe way to
* use the per cpu ftrace_stacks. We reserve it and if an interrupt
* or NMI comes in, it will just have to use the default
* FTRACE_STACK_SIZE.
*/
preempt_disable_notrace();
use_stack = ++__get_cpu_var(ftrace_stack_reserve);
/*
* We don't need any atomic variables, just a barrier.
* If an interrupt comes in, we don't care, because it would
* have exited and put the counter back to what we want.
* We just need a barrier to keep gcc from moving things
* around.
*/
barrier();
if (use_stack == 1) {
trace.entries = &__get_cpu_var(ftrace_stack).calls[0];
trace.max_entries = FTRACE_STACK_MAX_ENTRIES;
if (regs)
save_stack_trace_regs(regs, &trace);
else
save_stack_trace(&trace);
if (trace.nr_entries > size)
size = trace.nr_entries;
} else
/* From now on, use_stack is a boolean */
use_stack = 0;
size *= sizeof(unsigned long);
event = trace_buffer_lock_reserve(buffer, TRACE_STACK, event = trace_buffer_lock_reserve(buffer, TRACE_STACK,
sizeof(*entry), flags, pc); sizeof(*entry) + size, flags, pc);
if (!event) if (!event)
return; goto out;
entry = ring_buffer_event_data(event); entry = ring_buffer_event_data(event);
memset(&entry->caller, 0, sizeof(entry->caller));
trace.nr_entries = 0; memset(&entry->caller, 0, size);
if (use_stack)
memcpy(&entry->caller, trace.entries,
trace.nr_entries * sizeof(unsigned long));
else {
trace.max_entries = FTRACE_STACK_ENTRIES; trace.max_entries = FTRACE_STACK_ENTRIES;
trace.skip = skip;
trace.entries = entry->caller; trace.entries = entry->caller;
if (regs) if (regs)
save_stack_trace_regs(regs, &trace); save_stack_trace_regs(regs, &trace);
else else
save_stack_trace(&trace); save_stack_trace(&trace);
}
entry->size = trace.nr_entries;
if (!filter_check_discard(call, entry, buffer, event)) if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event); ring_buffer_unlock_commit(buffer, event);
out:
/* Again, don't let gcc optimize things here */
barrier();
__get_cpu_var(ftrace_stack_reserve)--;
preempt_enable_notrace();
} }
void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags, void ftrace_trace_stack_regs(struct ring_buffer *buffer, unsigned long flags,
...@@ -1562,7 +1623,12 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts, ...@@ -1562,7 +1623,12 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
ftrace_enable_cpu(); ftrace_enable_cpu();
return event ? ring_buffer_event_data(event) : NULL; if (event) {
iter->ent_size = ring_buffer_event_length(event);
return ring_buffer_event_data(event);
}
iter->ent_size = 0;
return NULL;
} }
static struct trace_entry * static struct trace_entry *
......
...@@ -278,6 +278,29 @@ struct tracer { ...@@ -278,6 +278,29 @@ struct tracer {
}; };
/* Only current can touch trace_recursion */
#define trace_recursion_inc() do { (current)->trace_recursion++; } while (0)
#define trace_recursion_dec() do { (current)->trace_recursion--; } while (0)
/* Ring buffer has the 10 LSB bits to count */
#define trace_recursion_buffer() ((current)->trace_recursion & 0x3ff)
/* for function tracing recursion */
#define TRACE_INTERNAL_BIT (1<<11)
#define TRACE_GLOBAL_BIT (1<<12)
/*
* Abuse of the trace_recursion.
* As we need a way to maintain state if we are tracing the function
* graph in irq because we want to trace a particular function that
* was called in irq context but we have irq tracing off. Since this
* can only be modified by current, we can reuse trace_recursion.
*/
#define TRACE_IRQ_BIT (1<<13)
#define trace_recursion_set(bit) do { (current)->trace_recursion |= (bit); } while (0)
#define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(bit); } while (0)
#define trace_recursion_test(bit) ((current)->trace_recursion & (bit))
#define TRACE_PIPE_ALL_CPU -1 #define TRACE_PIPE_ALL_CPU -1
int tracer_init(struct tracer *t, struct trace_array *tr); int tracer_init(struct tracer *t, struct trace_array *tr);
...@@ -516,9 +539,19 @@ static inline int ftrace_graph_addr(unsigned long addr) ...@@ -516,9 +539,19 @@ static inline int ftrace_graph_addr(unsigned long addr)
return 1; return 1;
for (i = 0; i < ftrace_graph_count; i++) { for (i = 0; i < ftrace_graph_count; i++) {
if (addr == ftrace_graph_funcs[i]) if (addr == ftrace_graph_funcs[i]) {
/*
* If no irqs are to be traced, but a set_graph_function
* is set, and called by an interrupt handler, we still
* want to trace it.
*/
if (in_irq())
trace_recursion_set(TRACE_IRQ_BIT);
else
trace_recursion_clear(TRACE_IRQ_BIT);
return 1; return 1;
} }
}
return 0; return 0;
} }
...@@ -795,19 +828,4 @@ extern const char *__stop___trace_bprintk_fmt[]; ...@@ -795,19 +828,4 @@ extern const char *__stop___trace_bprintk_fmt[];
FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
#include "trace_entries.h" #include "trace_entries.h"
/* Only current can touch trace_recursion */
#define trace_recursion_inc() do { (current)->trace_recursion++; } while (0)
#define trace_recursion_dec() do { (current)->trace_recursion--; } while (0)
/* Ring buffer has the 10 LSB bits to count */
#define trace_recursion_buffer() ((current)->trace_recursion & 0x3ff)
/* for function tracing recursion */
#define TRACE_INTERNAL_BIT (1<<11)
#define TRACE_GLOBAL_BIT (1<<12)
#define trace_recursion_set(bit) do { (current)->trace_recursion |= (bit); } while (0)
#define trace_recursion_clear(bit) do { (current)->trace_recursion &= ~(bit); } while (0)
#define trace_recursion_test(bit) ((current)->trace_recursion & (bit))
#endif /* _LINUX_KERNEL_TRACE_H */ #endif /* _LINUX_KERNEL_TRACE_H */
...@@ -161,7 +161,8 @@ FTRACE_ENTRY(kernel_stack, stack_entry, ...@@ -161,7 +161,8 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
TRACE_STACK, TRACE_STACK,
F_STRUCT( F_STRUCT(
__array( unsigned long, caller, FTRACE_STACK_ENTRIES ) __field( int, size )
__dynamic_array(unsigned long, caller )
), ),
F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n" F_printk("\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n\t=> (%08lx)\n"
......
...@@ -227,7 +227,7 @@ int __trace_graph_entry(struct trace_array *tr, ...@@ -227,7 +227,7 @@ int __trace_graph_entry(struct trace_array *tr,
static inline int ftrace_graph_ignore_irqs(void) static inline int ftrace_graph_ignore_irqs(void)
{ {
if (!ftrace_graph_skip_irqs) if (!ftrace_graph_skip_irqs || trace_recursion_test(TRACE_IRQ_BIT))
return 0; return 0;
return in_irq(); return in_irq();
......
...@@ -343,6 +343,14 @@ DEFINE_BASIC_FETCH_FUNCS(deref) ...@@ -343,6 +343,14 @@ DEFINE_BASIC_FETCH_FUNCS(deref)
DEFINE_FETCH_deref(string) DEFINE_FETCH_deref(string)
DEFINE_FETCH_deref(string_size) DEFINE_FETCH_deref(string_size)
static __kprobes void update_deref_fetch_param(struct deref_fetch_param *data)
{
if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
update_deref_fetch_param(data->orig.data);
else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
update_symbol_cache(data->orig.data);
}
static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data) static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
{ {
if (CHECK_FETCH_FUNCS(deref, data->orig.fn)) if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
...@@ -376,6 +384,19 @@ DEFINE_BASIC_FETCH_FUNCS(bitfield) ...@@ -376,6 +384,19 @@ DEFINE_BASIC_FETCH_FUNCS(bitfield)
#define fetch_bitfield_string NULL #define fetch_bitfield_string NULL
#define fetch_bitfield_string_size NULL #define fetch_bitfield_string_size NULL
static __kprobes void
update_bitfield_fetch_param(struct bitfield_fetch_param *data)
{
/*
* Don't check the bitfield itself, because this must be the
* last fetch function.
*/
if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
update_deref_fetch_param(data->orig.data);
else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
update_symbol_cache(data->orig.data);
}
static __kprobes void static __kprobes void
free_bitfield_fetch_param(struct bitfield_fetch_param *data) free_bitfield_fetch_param(struct bitfield_fetch_param *data)
{ {
...@@ -389,6 +410,7 @@ free_bitfield_fetch_param(struct bitfield_fetch_param *data) ...@@ -389,6 +410,7 @@ free_bitfield_fetch_param(struct bitfield_fetch_param *data)
free_symbol_cache(data->orig.data); free_symbol_cache(data->orig.data);
kfree(data); kfree(data);
} }
/* Default (unsigned long) fetch type */ /* Default (unsigned long) fetch type */
#define __DEFAULT_FETCH_TYPE(t) u##t #define __DEFAULT_FETCH_TYPE(t) u##t
#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t) #define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
...@@ -536,6 +558,7 @@ struct probe_arg { ...@@ -536,6 +558,7 @@ struct probe_arg {
/* Flags for trace_probe */ /* Flags for trace_probe */
#define TP_FLAG_TRACE 1 #define TP_FLAG_TRACE 1
#define TP_FLAG_PROFILE 2 #define TP_FLAG_PROFILE 2
#define TP_FLAG_REGISTERED 4
struct trace_probe { struct trace_probe {
struct list_head list; struct list_head list;
...@@ -555,16 +578,49 @@ struct trace_probe { ...@@ -555,16 +578,49 @@ struct trace_probe {
(sizeof(struct probe_arg) * (n))) (sizeof(struct probe_arg) * (n)))
static __kprobes int probe_is_return(struct trace_probe *tp) static __kprobes int trace_probe_is_return(struct trace_probe *tp)
{ {
return tp->rp.handler != NULL; return tp->rp.handler != NULL;
} }
static __kprobes const char *probe_symbol(struct trace_probe *tp) static __kprobes const char *trace_probe_symbol(struct trace_probe *tp)
{ {
return tp->symbol ? tp->symbol : "unknown"; return tp->symbol ? tp->symbol : "unknown";
} }
static __kprobes unsigned long trace_probe_offset(struct trace_probe *tp)
{
return tp->rp.kp.offset;
}
static __kprobes bool trace_probe_is_enabled(struct trace_probe *tp)
{
return !!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE));
}
static __kprobes bool trace_probe_is_registered(struct trace_probe *tp)
{
return !!(tp->flags & TP_FLAG_REGISTERED);
}
static __kprobes bool trace_probe_has_gone(struct trace_probe *tp)
{
return !!(kprobe_gone(&tp->rp.kp));
}
static __kprobes bool trace_probe_within_module(struct trace_probe *tp,
struct module *mod)
{
int len = strlen(mod->name);
const char *name = trace_probe_symbol(tp);
return strncmp(mod->name, name, len) == 0 && name[len] == ':';
}
static __kprobes bool trace_probe_is_on_module(struct trace_probe *tp)
{
return !!strchr(trace_probe_symbol(tp), ':');
}
static int register_probe_event(struct trace_probe *tp); static int register_probe_event(struct trace_probe *tp);
static void unregister_probe_event(struct trace_probe *tp); static void unregister_probe_event(struct trace_probe *tp);
...@@ -646,6 +702,16 @@ static struct trace_probe *alloc_trace_probe(const char *group, ...@@ -646,6 +702,16 @@ static struct trace_probe *alloc_trace_probe(const char *group,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
static void update_probe_arg(struct probe_arg *arg)
{
if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn))
update_bitfield_fetch_param(arg->fetch.data);
else if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
update_deref_fetch_param(arg->fetch.data);
else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
update_symbol_cache(arg->fetch.data);
}
static void free_probe_arg(struct probe_arg *arg) static void free_probe_arg(struct probe_arg *arg)
{ {
if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn)) if (CHECK_FETCH_FUNCS(bitfield, arg->fetch.fn))
...@@ -671,7 +737,7 @@ static void free_trace_probe(struct trace_probe *tp) ...@@ -671,7 +737,7 @@ static void free_trace_probe(struct trace_probe *tp)
kfree(tp); kfree(tp);
} }
static struct trace_probe *find_probe_event(const char *event, static struct trace_probe *find_trace_probe(const char *event,
const char *group) const char *group)
{ {
struct trace_probe *tp; struct trace_probe *tp;
...@@ -683,13 +749,96 @@ static struct trace_probe *find_probe_event(const char *event, ...@@ -683,13 +749,96 @@ static struct trace_probe *find_probe_event(const char *event,
return NULL; return NULL;
} }
/* Unregister a trace_probe and probe_event: call with locking probe_lock */ /* Enable trace_probe - @flag must be TP_FLAG_TRACE or TP_FLAG_PROFILE */
static void unregister_trace_probe(struct trace_probe *tp) static int enable_trace_probe(struct trace_probe *tp, int flag)
{
int ret = 0;
tp->flags |= flag;
if (trace_probe_is_enabled(tp) && trace_probe_is_registered(tp) &&
!trace_probe_has_gone(tp)) {
if (trace_probe_is_return(tp))
ret = enable_kretprobe(&tp->rp);
else
ret = enable_kprobe(&tp->rp.kp);
}
return ret;
}
/* Disable trace_probe - @flag must be TP_FLAG_TRACE or TP_FLAG_PROFILE */
static void disable_trace_probe(struct trace_probe *tp, int flag)
{
tp->flags &= ~flag;
if (!trace_probe_is_enabled(tp) && trace_probe_is_registered(tp)) {
if (trace_probe_is_return(tp))
disable_kretprobe(&tp->rp);
else
disable_kprobe(&tp->rp.kp);
}
}
/* Internal register function - just handle k*probes and flags */
static int __register_trace_probe(struct trace_probe *tp)
{ {
if (probe_is_return(tp)) int i, ret;
if (trace_probe_is_registered(tp))
return -EINVAL;
for (i = 0; i < tp->nr_args; i++)
update_probe_arg(&tp->args[i]);
/* Set/clear disabled flag according to tp->flag */
if (trace_probe_is_enabled(tp))
tp->rp.kp.flags &= ~KPROBE_FLAG_DISABLED;
else
tp->rp.kp.flags |= KPROBE_FLAG_DISABLED;
if (trace_probe_is_return(tp))
ret = register_kretprobe(&tp->rp);
else
ret = register_kprobe(&tp->rp.kp);
if (ret == 0)
tp->flags |= TP_FLAG_REGISTERED;
else {
pr_warning("Could not insert probe at %s+%lu: %d\n",
trace_probe_symbol(tp), trace_probe_offset(tp), ret);
if (ret == -ENOENT && trace_probe_is_on_module(tp)) {
pr_warning("This probe might be able to register after"
"target module is loaded. Continue.\n");
ret = 0;
} else if (ret == -EILSEQ) {
pr_warning("Probing address(0x%p) is not an "
"instruction boundary.\n",
tp->rp.kp.addr);
ret = -EINVAL;
}
}
return ret;
}
/* Internal unregister function - just handle k*probes and flags */
static void __unregister_trace_probe(struct trace_probe *tp)
{
if (trace_probe_is_registered(tp)) {
if (trace_probe_is_return(tp))
unregister_kretprobe(&tp->rp); unregister_kretprobe(&tp->rp);
else else
unregister_kprobe(&tp->rp.kp); unregister_kprobe(&tp->rp.kp);
tp->flags &= ~TP_FLAG_REGISTERED;
/* Cleanup kprobe for reuse */
if (tp->rp.kp.symbol_name)
tp->rp.kp.addr = NULL;
}
}
/* Unregister a trace_probe and probe_event: call with locking probe_lock */
static void unregister_trace_probe(struct trace_probe *tp)
{
__unregister_trace_probe(tp);
list_del(&tp->list); list_del(&tp->list);
unregister_probe_event(tp); unregister_probe_event(tp);
} }
...@@ -702,41 +851,65 @@ static int register_trace_probe(struct trace_probe *tp) ...@@ -702,41 +851,65 @@ static int register_trace_probe(struct trace_probe *tp)
mutex_lock(&probe_lock); mutex_lock(&probe_lock);
/* register as an event */ /* Delete old (same name) event if exist */
old_tp = find_probe_event(tp->call.name, tp->call.class->system); old_tp = find_trace_probe(tp->call.name, tp->call.class->system);
if (old_tp) { if (old_tp) {
/* delete old event */
unregister_trace_probe(old_tp); unregister_trace_probe(old_tp);
free_trace_probe(old_tp); free_trace_probe(old_tp);
} }
/* Register new event */
ret = register_probe_event(tp); ret = register_probe_event(tp);
if (ret) { if (ret) {
pr_warning("Failed to register probe event(%d)\n", ret); pr_warning("Failed to register probe event(%d)\n", ret);
goto end; goto end;
} }
tp->rp.kp.flags |= KPROBE_FLAG_DISABLED; /* Register k*probe */
if (probe_is_return(tp)) ret = __register_trace_probe(tp);
ret = register_kretprobe(&tp->rp); if (ret < 0)
else
ret = register_kprobe(&tp->rp.kp);
if (ret) {
pr_warning("Could not insert probe(%d)\n", ret);
if (ret == -EILSEQ) {
pr_warning("Probing address(0x%p) is not an "
"instruction boundary.\n",
tp->rp.kp.addr);
ret = -EINVAL;
}
unregister_probe_event(tp); unregister_probe_event(tp);
} else else
list_add_tail(&tp->list, &probe_list); list_add_tail(&tp->list, &probe_list);
end: end:
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
return ret; return ret;
} }
/* Module notifier call back, checking event on the module */
static int trace_probe_module_callback(struct notifier_block *nb,
unsigned long val, void *data)
{
struct module *mod = data;
struct trace_probe *tp;
int ret;
if (val != MODULE_STATE_COMING)
return NOTIFY_DONE;
/* Update probes on coming module */
mutex_lock(&probe_lock);
list_for_each_entry(tp, &probe_list, list) {
if (trace_probe_within_module(tp, mod)) {
__unregister_trace_probe(tp);
ret = __register_trace_probe(tp);
if (ret)
pr_warning("Failed to re-register probe %s on"
"%s: %d\n",
tp->call.name, mod->name, ret);
}
}
mutex_unlock(&probe_lock);
return NOTIFY_DONE;
}
static struct notifier_block trace_probe_module_nb = {
.notifier_call = trace_probe_module_callback,
.priority = 1 /* Invoked after kprobe module callback */
};
/* Split symbol and offset. */ /* Split symbol and offset. */
static int split_symbol_offset(char *symbol, unsigned long *offset) static int split_symbol_offset(char *symbol, unsigned long *offset)
{ {
...@@ -962,8 +1135,8 @@ static int create_trace_probe(int argc, char **argv) ...@@ -962,8 +1135,8 @@ static int create_trace_probe(int argc, char **argv)
{ {
/* /*
* Argument syntax: * Argument syntax:
* - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS] * - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR [FETCHARGS]
* - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS] * - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0] [FETCHARGS]
* Fetch args: * Fetch args:
* $retval : fetch return value * $retval : fetch return value
* $stack : fetch stack address * $stack : fetch stack address
...@@ -1025,7 +1198,7 @@ static int create_trace_probe(int argc, char **argv) ...@@ -1025,7 +1198,7 @@ static int create_trace_probe(int argc, char **argv)
return -EINVAL; return -EINVAL;
} }
mutex_lock(&probe_lock); mutex_lock(&probe_lock);
tp = find_probe_event(event, group); tp = find_trace_probe(event, group);
if (!tp) { if (!tp) {
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
pr_info("Event %s/%s doesn't exist.\n", group, event); pr_info("Event %s/%s doesn't exist.\n", group, event);
...@@ -1144,7 +1317,7 @@ static int create_trace_probe(int argc, char **argv) ...@@ -1144,7 +1317,7 @@ static int create_trace_probe(int argc, char **argv)
return ret; return ret;
} }
static void cleanup_all_probes(void) static void release_all_trace_probes(void)
{ {
struct trace_probe *tp; struct trace_probe *tp;
...@@ -1158,7 +1331,6 @@ static void cleanup_all_probes(void) ...@@ -1158,7 +1331,6 @@ static void cleanup_all_probes(void)
mutex_unlock(&probe_lock); mutex_unlock(&probe_lock);
} }
/* Probes listing interfaces */ /* Probes listing interfaces */
static void *probes_seq_start(struct seq_file *m, loff_t *pos) static void *probes_seq_start(struct seq_file *m, loff_t *pos)
{ {
...@@ -1181,15 +1353,16 @@ static int probes_seq_show(struct seq_file *m, void *v) ...@@ -1181,15 +1353,16 @@ static int probes_seq_show(struct seq_file *m, void *v)
struct trace_probe *tp = v; struct trace_probe *tp = v;
int i; int i;
seq_printf(m, "%c", probe_is_return(tp) ? 'r' : 'p'); seq_printf(m, "%c", trace_probe_is_return(tp) ? 'r' : 'p');
seq_printf(m, ":%s/%s", tp->call.class->system, tp->call.name); seq_printf(m, ":%s/%s", tp->call.class->system, tp->call.name);
if (!tp->symbol) if (!tp->symbol)
seq_printf(m, " 0x%p", tp->rp.kp.addr); seq_printf(m, " 0x%p", tp->rp.kp.addr);
else if (tp->rp.kp.offset) else if (tp->rp.kp.offset)
seq_printf(m, " %s+%u", probe_symbol(tp), tp->rp.kp.offset); seq_printf(m, " %s+%u", trace_probe_symbol(tp),
tp->rp.kp.offset);
else else
seq_printf(m, " %s", probe_symbol(tp)); seq_printf(m, " %s", trace_probe_symbol(tp));
for (i = 0; i < tp->nr_args; i++) for (i = 0; i < tp->nr_args; i++)
seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm); seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
...@@ -1209,7 +1382,7 @@ static int probes_open(struct inode *inode, struct file *file) ...@@ -1209,7 +1382,7 @@ static int probes_open(struct inode *inode, struct file *file)
{ {
if ((file->f_mode & FMODE_WRITE) && if ((file->f_mode & FMODE_WRITE) &&
(file->f_flags & O_TRUNC)) (file->f_flags & O_TRUNC))
cleanup_all_probes(); release_all_trace_probes();
return seq_open(file, &probes_seq_op); return seq_open(file, &probes_seq_op);
} }
...@@ -1513,30 +1686,6 @@ print_kretprobe_event(struct trace_iterator *iter, int flags, ...@@ -1513,30 +1686,6 @@ print_kretprobe_event(struct trace_iterator *iter, int flags,
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
static int probe_event_enable(struct ftrace_event_call *call)
{
struct trace_probe *tp = (struct trace_probe *)call->data;
tp->flags |= TP_FLAG_TRACE;
if (probe_is_return(tp))
return enable_kretprobe(&tp->rp);
else
return enable_kprobe(&tp->rp.kp);
}
static void probe_event_disable(struct ftrace_event_call *call)
{
struct trace_probe *tp = (struct trace_probe *)call->data;
tp->flags &= ~TP_FLAG_TRACE;
if (!(tp->flags & (TP_FLAG_TRACE | TP_FLAG_PROFILE))) {
if (probe_is_return(tp))
disable_kretprobe(&tp->rp);
else
disable_kprobe(&tp->rp.kp);
}
}
#undef DEFINE_FIELD #undef DEFINE_FIELD
#define DEFINE_FIELD(type, item, name, is_signed) \ #define DEFINE_FIELD(type, item, name, is_signed) \
do { \ do { \
...@@ -1598,7 +1747,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len) ...@@ -1598,7 +1747,7 @@ static int __set_print_fmt(struct trace_probe *tp, char *buf, int len)
const char *fmt, *arg; const char *fmt, *arg;
if (!probe_is_return(tp)) { if (!trace_probe_is_return(tp)) {
fmt = "(%lx)"; fmt = "(%lx)";
arg = "REC->" FIELD_STRING_IP; arg = "REC->" FIELD_STRING_IP;
} else { } else {
...@@ -1715,49 +1864,25 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri, ...@@ -1715,49 +1864,25 @@ static __kprobes void kretprobe_perf_func(struct kretprobe_instance *ri,
head = this_cpu_ptr(call->perf_events); head = this_cpu_ptr(call->perf_events);
perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head); perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head);
} }
static int probe_perf_enable(struct ftrace_event_call *call)
{
struct trace_probe *tp = (struct trace_probe *)call->data;
tp->flags |= TP_FLAG_PROFILE;
if (probe_is_return(tp))
return enable_kretprobe(&tp->rp);
else
return enable_kprobe(&tp->rp.kp);
}
static void probe_perf_disable(struct ftrace_event_call *call)
{
struct trace_probe *tp = (struct trace_probe *)call->data;
tp->flags &= ~TP_FLAG_PROFILE;
if (!(tp->flags & TP_FLAG_TRACE)) {
if (probe_is_return(tp))
disable_kretprobe(&tp->rp);
else
disable_kprobe(&tp->rp.kp);
}
}
#endif /* CONFIG_PERF_EVENTS */ #endif /* CONFIG_PERF_EVENTS */
static __kprobes static __kprobes
int kprobe_register(struct ftrace_event_call *event, enum trace_reg type) int kprobe_register(struct ftrace_event_call *event, enum trace_reg type)
{ {
struct trace_probe *tp = (struct trace_probe *)event->data;
switch (type) { switch (type) {
case TRACE_REG_REGISTER: case TRACE_REG_REGISTER:
return probe_event_enable(event); return enable_trace_probe(tp, TP_FLAG_TRACE);
case TRACE_REG_UNREGISTER: case TRACE_REG_UNREGISTER:
probe_event_disable(event); disable_trace_probe(tp, TP_FLAG_TRACE);
return 0; return 0;
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS
case TRACE_REG_PERF_REGISTER: case TRACE_REG_PERF_REGISTER:
return probe_perf_enable(event); return enable_trace_probe(tp, TP_FLAG_PROFILE);
case TRACE_REG_PERF_UNREGISTER: case TRACE_REG_PERF_UNREGISTER:
probe_perf_disable(event); disable_trace_probe(tp, TP_FLAG_PROFILE);
return 0; return 0;
#endif #endif
} }
...@@ -1807,7 +1932,7 @@ static int register_probe_event(struct trace_probe *tp) ...@@ -1807,7 +1932,7 @@ static int register_probe_event(struct trace_probe *tp)
/* Initialize ftrace_event_call */ /* Initialize ftrace_event_call */
INIT_LIST_HEAD(&call->class->fields); INIT_LIST_HEAD(&call->class->fields);
if (probe_is_return(tp)) { if (trace_probe_is_return(tp)) {
call->event.funcs = &kretprobe_funcs; call->event.funcs = &kretprobe_funcs;
call->class->define_fields = kretprobe_event_define_fields; call->class->define_fields = kretprobe_event_define_fields;
} else { } else {
...@@ -1846,6 +1971,9 @@ static __init int init_kprobe_trace(void) ...@@ -1846,6 +1971,9 @@ static __init int init_kprobe_trace(void)
struct dentry *d_tracer; struct dentry *d_tracer;
struct dentry *entry; struct dentry *entry;
if (register_module_notifier(&trace_probe_module_nb))
return -EINVAL;
d_tracer = tracing_init_dentry(); d_tracer = tracing_init_dentry();
if (!d_tracer) if (!d_tracer)
return 0; return 0;
...@@ -1899,12 +2027,12 @@ static __init int kprobe_trace_self_tests_init(void) ...@@ -1899,12 +2027,12 @@ static __init int kprobe_trace_self_tests_init(void)
warn++; warn++;
} else { } else {
/* Enable trace point */ /* Enable trace point */
tp = find_probe_event("testprobe", KPROBE_EVENT_SYSTEM); tp = find_trace_probe("testprobe", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tp == NULL)) { if (WARN_ON_ONCE(tp == NULL)) {
pr_warning("error on getting new probe.\n"); pr_warning("error on getting new probe.\n");
warn++; warn++;
} else } else
probe_event_enable(&tp->call); enable_trace_probe(tp, TP_FLAG_TRACE);
} }
ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target " ret = command_trace_probe("r:testprobe2 kprobe_trace_selftest_target "
...@@ -1914,12 +2042,12 @@ static __init int kprobe_trace_self_tests_init(void) ...@@ -1914,12 +2042,12 @@ static __init int kprobe_trace_self_tests_init(void)
warn++; warn++;
} else { } else {
/* Enable trace point */ /* Enable trace point */
tp = find_probe_event("testprobe2", KPROBE_EVENT_SYSTEM); tp = find_trace_probe("testprobe2", KPROBE_EVENT_SYSTEM);
if (WARN_ON_ONCE(tp == NULL)) { if (WARN_ON_ONCE(tp == NULL)) {
pr_warning("error on getting new probe.\n"); pr_warning("error on getting new probe.\n");
warn++; warn++;
} else } else
probe_event_enable(&tp->call); enable_trace_probe(tp, TP_FLAG_TRACE);
} }
if (warn) if (warn)
...@@ -1940,7 +2068,7 @@ static __init int kprobe_trace_self_tests_init(void) ...@@ -1940,7 +2068,7 @@ static __init int kprobe_trace_self_tests_init(void)
} }
end: end:
cleanup_all_probes(); release_all_trace_probes();
if (warn) if (warn)
pr_cont("NG: Some tests are failed. Please check them.\n"); pr_cont("NG: Some tests are failed. Please check them.\n");
else else
......
...@@ -1107,19 +1107,20 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter, ...@@ -1107,19 +1107,20 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
{ {
struct stack_entry *field; struct stack_entry *field;
struct trace_seq *s = &iter->seq; struct trace_seq *s = &iter->seq;
int i; unsigned long *p;
unsigned long *end;
trace_assign_type(field, iter->ent); trace_assign_type(field, iter->ent);
end = (unsigned long *)((long)iter->ent + iter->ent_size);
if (!trace_seq_puts(s, "<stack trace>\n")) if (!trace_seq_puts(s, "<stack trace>\n"))
goto partial; goto partial;
for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
if (!field->caller[i] || (field->caller[i] == ULONG_MAX)) for (p = field->caller; p && *p != ULONG_MAX && p < end; p++) {
break;
if (!trace_seq_puts(s, " => ")) if (!trace_seq_puts(s, " => "))
goto partial; goto partial;
if (!seq_print_ip_sym(s, field->caller[i], flags)) if (!seq_print_ip_sym(s, *p, flags))
goto partial; goto partial;
if (!trace_seq_puts(s, "\n")) if (!trace_seq_puts(s, "\n"))
goto partial; goto partial;
......
...@@ -200,7 +200,6 @@ static int is_softlockup(unsigned long touch_ts) ...@@ -200,7 +200,6 @@ static int is_softlockup(unsigned long touch_ts)
} }
#ifdef CONFIG_HARDLOCKUP_DETECTOR #ifdef CONFIG_HARDLOCKUP_DETECTOR
void __weak hw_nmi_watchdog_set_attr(struct perf_event_attr *wd_attr) { }
static struct perf_event_attr wd_hw_attr = { static struct perf_event_attr wd_hw_attr = {
.type = PERF_TYPE_HARDWARE, .type = PERF_TYPE_HARDWARE,
...@@ -372,7 +371,6 @@ static int watchdog_nmi_enable(int cpu) ...@@ -372,7 +371,6 @@ static int watchdog_nmi_enable(int cpu)
wd_attr = &wd_hw_attr; wd_attr = &wd_hw_attr;
wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh); wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);
hw_nmi_watchdog_set_attr(wd_attr);
/* Try to register using hardware perf events */ /* Try to register using hardware perf events */
event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL); event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
......
...@@ -34,9 +34,11 @@ OPTIONS ...@@ -34,9 +34,11 @@ OPTIONS
Specify vmlinux path which has debuginfo (Dwarf binary). Specify vmlinux path which has debuginfo (Dwarf binary).
-m:: -m::
--module=MODNAME:: --module=MODNAME|PATH::
Specify module name in which perf-probe searches probe points Specify module name in which perf-probe searches probe points
or lines. or lines. If a path of module file is passed, perf-probe
treat it as an offline module (this means you can add a probe on
a module which has not been loaded yet).
-s:: -s::
--source=PATH:: --source=PATH::
......
...@@ -279,6 +279,7 @@ LIB_H += util/thread.h ...@@ -279,6 +279,7 @@ LIB_H += util/thread.h
LIB_H += util/thread_map.h LIB_H += util/thread_map.h
LIB_H += util/trace-event.h LIB_H += util/trace-event.h
LIB_H += util/probe-finder.h LIB_H += util/probe-finder.h
LIB_H += util/dwarf-aux.h
LIB_H += util/probe-event.h LIB_H += util/probe-event.h
LIB_H += util/pstack.h LIB_H += util/pstack.h
LIB_H += util/cpumap.h LIB_H += util/cpumap.h
...@@ -435,6 +436,7 @@ else ...@@ -435,6 +436,7 @@ else
BASIC_CFLAGS += -DDWARF_SUPPORT BASIC_CFLAGS += -DDWARF_SUPPORT
EXTLIBS += -lelf -ldw EXTLIBS += -lelf -ldw
LIB_OBJS += $(OUTPUT)util/probe-finder.o LIB_OBJS += $(OUTPUT)util/probe-finder.o
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
endif # PERF_HAVE_DWARF_REGS endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF endif # NO_DWARF
......
...@@ -242,7 +242,8 @@ static const struct option options[] = { ...@@ -242,7 +242,8 @@ static const struct option options[] = {
OPT_STRING('s', "source", &symbol_conf.source_prefix, OPT_STRING('s', "source", &symbol_conf.source_prefix,
"directory", "path to kernel source"), "directory", "path to kernel source"),
OPT_STRING('m', "module", &params.target_module, OPT_STRING('m', "module", &params.target_module,
"modname", "target module name"), "modname|path",
"target module name (for online) or path (for offline)"),
#endif #endif
OPT__DRY_RUN(&probe_event_dry_run), OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", &params.max_probe_points, OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
......
/*
* dwarf-aux.c : libdw auxiliary interfaces
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <stdbool.h>
#include "util.h"
#include "debug.h"
#include "dwarf-aux.h"
/**
* cu_find_realpath - Find the realpath of the target file
* @cu_die: A DIE(dwarf information entry) of CU(compilation Unit)
* @fname: The tail filename of the target file
*
* Find the real(long) path of @fname in @cu_die.
*/
const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
{
Dwarf_Files *files;
size_t nfiles, i;
const char *src = NULL;
int ret;
if (!fname)
return NULL;
ret = dwarf_getsrcfiles(cu_die, &files, &nfiles);
if (ret != 0)
return NULL;
for (i = 0; i < nfiles; i++) {
src = dwarf_filesrc(files, i, NULL, NULL);
if (strtailcmp(src, fname) == 0)
break;
}
if (i == nfiles)
return NULL;
return src;
}
/**
* cu_get_comp_dir - Get the path of compilation directory
* @cu_die: a CU DIE
*
* Get the path of compilation directory of given @cu_die.
* Since this depends on DW_AT_comp_dir, older gcc will not
* embedded it. In that case, this returns NULL.
*/
const char *cu_get_comp_dir(Dwarf_Die *cu_die)
{
Dwarf_Attribute attr;
if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
return NULL;
return dwarf_formstring(&attr);
}
/**
* cu_find_lineinfo - Get a line number and file name for given address
* @cu_die: a CU DIE
* @addr: An address
* @fname: a pointer which returns the file name string
* @lineno: a pointer which returns the line number
*
* Find a line number and file name for @addr in @cu_die.
*/
int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr,
const char **fname, int *lineno)
{
Dwarf_Line *line;
Dwarf_Addr laddr;
line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr);
if (line && dwarf_lineaddr(line, &laddr) == 0 &&
addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
*fname = dwarf_linesrc(line, NULL, NULL);
if (!*fname)
/* line number is useless without filename */
*lineno = 0;
}
return *lineno ?: -ENOENT;
}
/**
* die_compare_name - Compare diename and tname
* @dw_die: a DIE
* @tname: a string of target name
*
* Compare the name of @dw_die and @tname. Return false if @dw_die has no name.
*/
bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
name = dwarf_diename(dw_die);
return name ? (strcmp(tname, name) == 0) : false;
}
/**
* die_get_call_lineno - Get callsite line number of inline-function instance
* @in_die: a DIE of an inlined function instance
*
* Get call-site line number of @in_die. This means from where the inline
* function is called.
*/
int die_get_call_lineno(Dwarf_Die *in_die)
{
Dwarf_Attribute attr;
Dwarf_Word ret;
if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
return -ENOENT;
dwarf_formudata(&attr, &ret);
return (int)ret;
}
/**
* die_get_type - Get type DIE
* @vr_die: a DIE of a variable
* @die_mem: where to store a type DIE
*
* Get a DIE of the type of given variable (@vr_die), and store
* it to die_mem. Return NULL if fails to get a type DIE.
*/
Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
Dwarf_Attribute attr;
if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
dwarf_formref_die(&attr, die_mem))
return die_mem;
else
return NULL;
}
/* Get a type die, but skip qualifiers */
static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
int tag;
do {
vr_die = die_get_type(vr_die, die_mem);
if (!vr_die)
break;
tag = dwarf_tag(vr_die);
} while (tag == DW_TAG_const_type ||
tag == DW_TAG_restrict_type ||
tag == DW_TAG_volatile_type ||
tag == DW_TAG_shared_type);
return vr_die;
}
/**
* die_get_real_type - Get a type die, but skip qualifiers and typedef
* @vr_die: a DIE of a variable
* @die_mem: where to store a type DIE
*
* Get a DIE of the type of given variable (@vr_die), and store
* it to die_mem. Return NULL if fails to get a type DIE.
* If the type is qualifiers (e.g. const) or typedef, this skips it
* and tries to find real type (structure or basic types, e.g. int).
*/
Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
do {
vr_die = __die_get_real_type(vr_die, die_mem);
} while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
return vr_die;
}
/* Get attribute and translate it as a udata */
static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name,
Dwarf_Word *result)
{
Dwarf_Attribute attr;
if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
dwarf_formudata(&attr, result) != 0)
return -ENOENT;
return 0;
}
/**
* die_is_signed_type - Check whether a type DIE is signed or not
* @tp_die: a DIE of a type
*
* Get the encoding of @tp_die and return true if the encoding
* is signed.
*/
bool die_is_signed_type(Dwarf_Die *tp_die)
{
Dwarf_Word ret;
if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret))
return false;
return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
ret == DW_ATE_signed_fixed);
}
/**
* die_get_data_member_location - Get the data-member offset
* @mb_die: a DIE of a member of a data structure
* @offs: The offset of the member in the data structure
*
* Get the offset of @mb_die in the data structure including @mb_die, and
* stores result offset to @offs. If any error occurs this returns errno.
*/
int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
{
Dwarf_Attribute attr;
Dwarf_Op *expr;
size_t nexpr;
int ret;
if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
return -ENOENT;
if (dwarf_formudata(&attr, offs) != 0) {
/* DW_AT_data_member_location should be DW_OP_plus_uconst */
ret = dwarf_getlocation(&attr, &expr, &nexpr);
if (ret < 0 || nexpr == 0)
return -ENOENT;
if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
expr[0].atom, nexpr);
return -ENOTSUP;
}
*offs = (Dwarf_Word)expr[0].number;
}
return 0;
}
/**
* die_find_child - Generic DIE search function in DIE tree
* @rt_die: a root DIE
* @callback: a callback function
* @data: a user data passed to the callback function
* @die_mem: a buffer for result DIE
*
* Trace DIE tree from @rt_die and call @callback for each child DIE.
* If @callback returns DIE_FIND_CB_END, this stores the DIE into
* @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE,
* this continues to trace the tree. Optionally, @callback can return
* DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only
* the children and trace only the siblings respectively.
* Returns NULL if @callback can't find any appropriate DIE.
*/
Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
int (*callback)(Dwarf_Die *, void *),
void *data, Dwarf_Die *die_mem)
{
Dwarf_Die child_die;
int ret;
ret = dwarf_child(rt_die, die_mem);
if (ret != 0)
return NULL;
do {
ret = callback(die_mem, data);
if (ret == DIE_FIND_CB_END)
return die_mem;
if ((ret & DIE_FIND_CB_CHILD) &&
die_find_child(die_mem, callback, data, &child_die)) {
memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
return die_mem;
}
} while ((ret & DIE_FIND_CB_SIBLING) &&
dwarf_siblingof(die_mem, die_mem) == 0);
return NULL;
}
struct __addr_die_search_param {
Dwarf_Addr addr;
Dwarf_Die *die_mem;
};
/* die_find callback for non-inlined function search */
static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
{
struct __addr_die_search_param *ad = data;
if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
dwarf_haspc(fn_die, ad->addr)) {
memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
}
/**
* die_find_realfunc - Search a non-inlined function at given address
* @cu_die: a CU DIE which including @addr
* @addr: target address
* @die_mem: a buffer for result DIE
*
* Search a non-inlined function DIE which includes @addr. Stores the
* DIE to @die_mem and returns it if found. Returns NULl if failed.
*/
Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
struct __addr_die_search_param ad;
ad.addr = addr;
ad.die_mem = die_mem;
/* dwarf_getscopes can't find subprogram. */
if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
return NULL;
else
return die_mem;
}
/* die_find callback for inline function search */
static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
{
Dwarf_Addr *addr = data;
if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
dwarf_haspc(die_mem, *addr))
return DIE_FIND_CB_END;
return DIE_FIND_CB_CONTINUE;
}
/**
* die_find_inlinefunc - Search an inlined function at given address
* @cu_die: a CU DIE which including @addr
* @addr: target address
* @die_mem: a buffer for result DIE
*
* Search an inlined function DIE which includes @addr. Stores the
* DIE to @die_mem and returns it if found. Returns NULl if failed.
* If several inlined functions are expanded recursively, this trace
* it and returns deepest one.
*/
Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
Dwarf_Die tmp_die;
sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die);
if (!sp_die)
return NULL;
/* Inlined function could be recursive. Trace it until fail */
while (sp_die) {
memcpy(die_mem, sp_die, sizeof(Dwarf_Die));
sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr,
&tmp_die);
}
return die_mem;
}
/* Line walker internal parameters */
struct __line_walk_param {
const char *fname;
line_walk_callback_t callback;
void *data;
int retval;
};
static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
struct __line_walk_param *lw = data;
Dwarf_Addr addr;
int lineno;
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
lineno = die_get_call_lineno(in_die);
if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
lw->retval = lw->callback(lw->fname, lineno, addr,
lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_END;
}
}
return DIE_FIND_CB_SIBLING;
}
/* Walk on lines of blocks included in given DIE */
static int __die_walk_funclines(Dwarf_Die *sp_die,
line_walk_callback_t callback, void *data)
{
struct __line_walk_param lw = {
.callback = callback,
.data = data,
.retval = 0,
};
Dwarf_Die die_mem;
Dwarf_Addr addr;
int lineno;
/* Handle function declaration line */
lw.fname = dwarf_decl_file(sp_die);
if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
dwarf_entrypc(sp_die, &addr) == 0) {
lw.retval = callback(lw.fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
}
die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
done:
return lw.retval;
}
static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
struct __line_walk_param *lw = data;
lw->retval = __die_walk_funclines(sp_die, lw->callback, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
return DWARF_CB_OK;
}
/**
* die_walk_lines - Walk on lines inside given DIE
* @rt_die: a root DIE (CU or subprogram)
* @callback: callback routine
* @data: user data
*
* Walk on all lines inside given @rt_die and call @callback on each line.
* If the @rt_die is a function, walk only on the lines inside the function,
* otherwise @rt_die must be a CU DIE.
* Note that this walks not only dwarf line list, but also function entries
* and inline call-site.
*/
int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
Dwarf_Addr addr;
const char *fname;
int lineno, ret = 0;
Dwarf_Die die_mem, *cu_die;
size_t nlines, i;
/* Get the CU die */
if (dwarf_tag(rt_die) == DW_TAG_subprogram)
cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL);
else
cu_die = rt_die;
if (!cu_die) {
pr_debug2("Failed to get CU from subprogram\n");
return -EINVAL;
}
/* Get lines list in the CU */
if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
pr_debug2("Failed to get source lines on this CU.\n");
return -ENOENT;
}
pr_debug2("Get %zd lines from this CU\n", nlines);
/* Walk on the lines on lines list */
for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
if (line == NULL ||
dwarf_lineno(line, &lineno) != 0 ||
dwarf_lineaddr(line, &addr) != 0) {
pr_debug2("Failed to get line info. "
"Possible error in debuginfo.\n");
continue;
}
/* Filter lines based on address */
if (rt_die != cu_die)
/*
* Address filtering
* The line is included in given function, and
* no inline block includes it.
*/
if (!dwarf_haspc(rt_die, addr) ||
die_find_inlinefunc(rt_die, addr, &die_mem))
continue;
/* Get source line */
fname = dwarf_linesrc(line, NULL, NULL);
ret = callback(fname, lineno, addr, data);
if (ret != 0)
return ret;
}
/*
* Dwarf lines doesn't include function declarations and inlined
* subroutines. We have to check functions list or given function.
*/
if (rt_die != cu_die)
ret = __die_walk_funclines(rt_die, callback, data);
else {
struct __line_walk_param param = {
.callback = callback,
.data = data,
.retval = 0,
};
dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
ret = param.retval;
}
return ret;
}
struct __find_variable_param {
const char *name;
Dwarf_Addr addr;
};
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
{
struct __find_variable_param *fvp = data;
int tag;
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
die_compare_name(die_mem, fvp->name))
return DIE_FIND_CB_END;
if (dwarf_haspc(die_mem, fvp->addr))
return DIE_FIND_CB_CONTINUE;
else
return DIE_FIND_CB_SIBLING;
}
/**
* die_find_variable_at - Find a given name variable at given address
* @sp_die: a function DIE
* @name: variable name
* @addr: address
* @die_mem: a buffer for result DIE
*
* Find a variable DIE called @name at @addr in @sp_die.
*/
Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
Dwarf_Addr addr, Dwarf_Die *die_mem)
{
struct __find_variable_param fvp = { .name = name, .addr = addr};
return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
die_mem);
}
static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
{
const char *name = data;
if ((dwarf_tag(die_mem) == DW_TAG_member) &&
die_compare_name(die_mem, name))
return DIE_FIND_CB_END;
return DIE_FIND_CB_SIBLING;
}
/**
* die_find_member - Find a given name member in a data structure
* @st_die: a data structure type DIE
* @name: member name
* @die_mem: a buffer for result DIE
*
* Find a member DIE called @name in @st_die.
*/
Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
Dwarf_Die *die_mem)
{
return die_find_child(st_die, __die_find_member_cb, (void *)name,
die_mem);
}
/**
* die_get_typename - Get the name of given variable DIE
* @vr_die: a variable DIE
* @buf: a buffer for result type name
* @len: a max-length of @buf
*
* Get the name of @vr_die and stores it to @buf. Return the actual length
* of type name if succeeded. Return -E2BIG if @len is not enough long, and
* Return -ENOENT if failed to find type name.
* Note that the result will stores typedef name if possible, and stores
* "*(function_type)" if the type is a function pointer.
*/
int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
{
Dwarf_Die type;
int tag, ret, ret2;
const char *tmp = "";
if (__die_get_real_type(vr_die, &type) == NULL)
return -ENOENT;
tag = dwarf_tag(&type);
if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
tmp = "*";
else if (tag == DW_TAG_subroutine_type) {
/* Function pointer */
ret = snprintf(buf, len, "(function_type)");
return (ret >= len) ? -E2BIG : ret;
} else {
if (!dwarf_diename(&type))
return -ENOENT;
if (tag == DW_TAG_union_type)
tmp = "union ";
else if (tag == DW_TAG_structure_type)
tmp = "struct ";
/* Write a base name */
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
return (ret >= len) ? -E2BIG : ret;
}
ret = die_get_typename(&type, buf, len);
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
}
return ret;
}
/**
* die_get_varname - Get the name and type of given variable DIE
* @vr_die: a variable DIE
* @buf: a buffer for type and variable name
* @len: the max-length of @buf
*
* Get the name and type of @vr_die and stores it in @buf as "type\tname".
*/
int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
{
int ret, ret2;
ret = die_get_typename(vr_die, buf, len);
if (ret < 0) {
pr_debug("Failed to get type, make it unknown.\n");
ret = snprintf(buf, len, "(unknown_type)");
}
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "\t%s",
dwarf_diename(vr_die));
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
}
return ret;
}
#ifndef _DWARF_AUX_H
#define _DWARF_AUX_H
/*
* dwarf-aux.h : libdw auxiliary interfaces
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <dwarf.h>
#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>
#include <elfutils/version.h>
/* Find the realpath of the target file */
extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname);
/* Get DW_AT_comp_dir (should be NULL with older gcc) */
extern const char *cu_get_comp_dir(Dwarf_Die *cu_die);
/* Get a line number and file name for given address */
extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
const char **fname, int *lineno);
/* Compare diename and tname */
extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname);
/* Get callsite line number of inline-function instance */
extern int die_get_call_lineno(Dwarf_Die *in_die);
/* Get type die */
extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem);
/* Get a type die, but skip qualifiers and typedef */
extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem);
/* Check whether the DIE is signed or not */
extern bool die_is_signed_type(Dwarf_Die *tp_die);
/* Get data_member_location offset */
extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs);
/* Return values for die_find_child() callbacks */
enum {
DIE_FIND_CB_END = 0, /* End of Search */
DIE_FIND_CB_CHILD = 1, /* Search only children */
DIE_FIND_CB_SIBLING = 2, /* Search only siblings */
DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */
};
/* Search child DIEs */
extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
int (*callback)(Dwarf_Die *, void *),
void *data, Dwarf_Die *die_mem);
/* Search a non-inlined function including given address */
extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
/* Search an inlined function including given address */
extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
/* Walker on lines (Note: line number will not be sorted) */
typedef int (* line_walk_callback_t) (const char *fname, int lineno,
Dwarf_Addr addr, void *data);
/*
* Walk on lines inside given DIE. If the DIE is a subprogram, walk only on
* the lines inside the subprogram, otherwise the DIE must be a CU DIE.
*/
extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback,
void *data);
/* Find a variable called 'name' at given address */
extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
Dwarf_Addr addr, Dwarf_Die *die_mem);
/* Find a member called 'name' */
extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
Dwarf_Die *die_mem);
/* Get the name of given variable DIE */
extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len);
/* Get the name and type of given variable DIE, stored as "type\tname" */
extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len);
#endif
...@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module) ...@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module)
struct rb_node *nd; struct rb_node *nd;
struct map_groups *grp = &machine.kmaps; struct map_groups *grp = &machine.kmaps;
/* A file path -- this is an offline module */
if (module && strchr(module, '/'))
return machine__new_module(&machine, 0, module);
if (!module) if (!module)
module = "kernel"; module = "kernel";
...@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module) ...@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module)
} }
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
static int open_vmlinux(const char *module) /* Open new debuginfo of given module */
static struct debuginfo *open_debuginfo(const char *module)
{ {
const char *path = kernel_get_module_path(module); const char *path;
/* A file path -- this is an offline module */
if (module && strchr(module, '/'))
path = module;
else {
path = kernel_get_module_path(module);
if (!path) { if (!path) {
pr_err("Failed to find path of %s module.\n", pr_err("Failed to find path of %s module.\n",
module ?: "kernel"); module ?: "kernel");
return -ENOENT; return NULL;
}
} }
pr_debug("Try to open %s\n", path); return debuginfo__new(path);
return open(path, O_RDONLY);
} }
/* /*
...@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, ...@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
struct map *map; struct map *map;
u64 addr; u64 addr;
int ret = -ENOENT; int ret = -ENOENT;
struct debuginfo *dinfo;
sym = __find_kernel_function_by_name(tp->symbol, &map); sym = __find_kernel_function_by_name(tp->symbol, &map);
if (sym) { if (sym) {
addr = map->unmap_ip(map, sym->start + tp->offset); addr = map->unmap_ip(map, sym->start + tp->offset);
pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
tp->offset, addr); tp->offset, addr);
ret = find_perf_probe_point((unsigned long)addr, pp);
dinfo = debuginfo__new_online_kernel(addr);
if (dinfo) {
ret = debuginfo__find_probe_point(dinfo,
(unsigned long)addr, pp);
debuginfo__delete(dinfo);
} else {
pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n",
addr);
ret = -ENOENT;
}
} }
if (ret <= 0) { if (ret <= 0) {
pr_debug("Failed to find corresponding probes from " pr_debug("Failed to find corresponding probes from "
...@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, ...@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
return 0; return 0;
} }
static int add_module_to_probe_trace_events(struct probe_trace_event *tevs,
int ntevs, const char *module)
{
int i, ret = 0;
char *tmp;
if (!module)
return 0;
tmp = strrchr(module, '/');
if (tmp) {
/* This is a module path -- get the module name */
module = strdup(tmp + 1);
if (!module)
return -ENOMEM;
tmp = strchr(module, '.');
if (tmp)
*tmp = '\0';
tmp = (char *)module; /* For free() */
}
for (i = 0; i < ntevs; i++) {
tevs[i].point.module = strdup(module);
if (!tevs[i].point.module) {
ret = -ENOMEM;
break;
}
}
if (tmp)
free(tmp);
return ret;
}
/* Try to find perf_probe_event with debuginfo */ /* Try to find perf_probe_event with debuginfo */
static int try_to_find_probe_trace_events(struct perf_probe_event *pev, static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs, struct probe_trace_event **tevs,
int max_tevs, const char *module) int max_tevs, const char *module)
{ {
bool need_dwarf = perf_probe_event_need_dwarf(pev); bool need_dwarf = perf_probe_event_need_dwarf(pev);
int fd, ntevs; struct debuginfo *dinfo = open_debuginfo(module);
int ntevs, ret = 0;
fd = open_vmlinux(module); if (!dinfo) {
if (fd < 0) {
if (need_dwarf) { if (need_dwarf) {
pr_warning("Failed to open debuginfo file.\n"); pr_warning("Failed to open debuginfo file.\n");
return fd; return -ENOENT;
} }
pr_debug("Could not open vmlinux. Try to use symbols.\n"); pr_debug("Could not open debuginfo. Try to use symbols.\n");
return 0; return 0;
} }
/* Searching trace events corresponding to probe event */ /* Searching trace events corresponding to a probe event */
ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
debuginfo__delete(dinfo);
if (ntevs > 0) { /* Succeeded to find trace events */ if (ntevs > 0) { /* Succeeded to find trace events */
pr_debug("find %d probe_trace_events.\n", ntevs); pr_debug("find %d probe_trace_events.\n", ntevs);
return ntevs; if (module)
ret = add_module_to_probe_trace_events(*tevs, ntevs,
module);
return ret < 0 ? ret : ntevs;
} }
if (ntevs == 0) { /* No error but failed to find probe point. */ if (ntevs == 0) { /* No error but failed to find probe point. */
...@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module) ...@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module)
{ {
int l = 1; int l = 1;
struct line_node *ln; struct line_node *ln;
struct debuginfo *dinfo;
FILE *fp; FILE *fp;
int fd, ret; int ret;
char *tmp; char *tmp;
/* Search a line range */ /* Search a line range */
...@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module) ...@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module)
if (ret < 0) if (ret < 0)
return ret; return ret;
fd = open_vmlinux(module); dinfo = open_debuginfo(module);
if (fd < 0) { if (!dinfo) {
pr_warning("Failed to open debuginfo file.\n"); pr_warning("Failed to open debuginfo file.\n");
return fd; return -ENOENT;
} }
ret = find_line_range(fd, lr); ret = debuginfo__find_line_range(dinfo, lr);
debuginfo__delete(dinfo);
if (ret == 0) { if (ret == 0) {
pr_warning("Specified source line is not found.\n"); pr_warning("Specified source line is not found.\n");
return -ENOENT; return -ENOENT;
...@@ -448,7 +513,8 @@ int show_line_range(struct line_range *lr, const char *module) ...@@ -448,7 +513,8 @@ int show_line_range(struct line_range *lr, const char *module)
return ret; return ret;
} }
static int show_available_vars_at(int fd, struct perf_probe_event *pev, static int show_available_vars_at(struct debuginfo *dinfo,
struct perf_probe_event *pev,
int max_vls, struct strfilter *_filter, int max_vls, struct strfilter *_filter,
bool externs) bool externs)
{ {
...@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, ...@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
return -EINVAL; return -EINVAL;
pr_debug("Searching variables at %s\n", buf); pr_debug("Searching variables at %s\n", buf);
ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); ret = debuginfo__find_available_vars_at(dinfo, pev, &vls,
max_vls, externs);
if (ret <= 0) { if (ret <= 0) {
pr_err("Failed to find variables at %s (%d)\n", buf, ret); pr_err("Failed to find variables at %s (%d)\n", buf, ret);
goto end; goto end;
...@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, ...@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
int max_vls, const char *module, int max_vls, const char *module,
struct strfilter *_filter, bool externs) struct strfilter *_filter, bool externs)
{ {
int i, fd, ret = 0; int i, ret = 0;
struct debuginfo *dinfo;
ret = init_vmlinux(); ret = init_vmlinux();
if (ret < 0) if (ret < 0)
return ret; return ret;
dinfo = open_debuginfo(module);
if (!dinfo) {
pr_warning("Failed to open debuginfo file.\n");
return -ENOENT;
}
setup_pager(); setup_pager();
for (i = 0; i < npevs && ret >= 0; i++) { for (i = 0; i < npevs && ret >= 0; i++)
fd = open_vmlinux(module); ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter,
if (fd < 0) {
pr_warning("Failed to open debug information file.\n");
ret = fd;
break;
}
ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter,
externs); externs);
}
debuginfo__delete(dinfo);
return ret; return ret;
} }
...@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd, ...@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd,
tp->retprobe = (pr == 'r'); tp->retprobe = (pr == 'r');
/* Scan function name and offset */ /* Scan module name(if there), function name and offset */
ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, p = strchr(argv[1], ':');
if (p) {
tp->module = strndup(argv[1], p - argv[1]);
p++;
} else
p = argv[1];
ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol,
&tp->offset); &tp->offset);
if (ret == 1) if (ret == 1)
tp->offset = 0; tp->offset = 0;
...@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) ...@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
if (buf == NULL) if (buf == NULL)
return NULL; return NULL;
len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu",
tp->retprobe ? 'r' : 'p', tp->retprobe ? 'r' : 'p',
tev->group, tev->event, tev->group, tev->event,
tp->module ?: "", tp->module ? ":" : "",
tp->symbol, tp->offset); tp->symbol, tp->offset);
if (len <= 0) if (len <= 0)
goto error; goto error;
...@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) ...@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
free(tev->group); free(tev->group);
if (tev->point.symbol) if (tev->point.symbol)
free(tev->point.symbol); free(tev->point.symbol);
if (tev->point.module)
free(tev->point.module);
for (i = 0; i < tev->nargs; i++) { for (i = 0; i < tev->nargs; i++) {
if (tev->args[i].name) if (tev->args[i].name)
free(tev->args[i].name); free(tev->args[i].name);
...@@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, ...@@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
/* Convert perf_probe_event with debuginfo */ /* Convert perf_probe_event with debuginfo */
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
if (ret != 0) if (ret != 0)
return ret; return ret; /* Found in debuginfo or got an error */
/* Allocate trace event buffer */ /* Allocate trace event buffer */
tev = *tevs = zalloc(sizeof(struct probe_trace_event)); tev = *tevs = zalloc(sizeof(struct probe_trace_event));
...@@ -1742,6 +1820,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, ...@@ -1742,6 +1820,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
ret = -ENOMEM; ret = -ENOMEM;
goto error; goto error;
} }
tev->point.module = strdup(module);
if (tev->point.module == NULL) {
ret = -ENOMEM;
goto error;
}
tev->point.offset = pev->point.offset; tev->point.offset = pev->point.offset;
tev->point.retprobe = pev->point.retprobe; tev->point.retprobe = pev->point.retprobe;
tev->nargs = pev->nargs; tev->nargs = pev->nargs;
......
...@@ -10,6 +10,7 @@ extern bool probe_event_dry_run; ...@@ -10,6 +10,7 @@ extern bool probe_event_dry_run;
/* kprobe-tracer tracing point */ /* kprobe-tracer tracing point */
struct probe_trace_point { struct probe_trace_point {
char *symbol; /* Base symbol */ char *symbol; /* Base symbol */
char *module; /* Module name */
unsigned long offset; /* Offset from symbol */ unsigned long offset; /* Offset from symbol */
bool retprobe; /* Return probe flag */ bool retprobe; /* Return probe flag */
}; };
......
...@@ -43,21 +43,6 @@ ...@@ -43,21 +43,6 @@
/* Kprobe tracer basic type is up to u64 */ /* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64 #define MAX_BASIC_TYPE_BITS 64
/*
* Compare the tail of two strings.
* Return 0 if whole of either string is same as another's tail part.
*/
static int strtailcmp(const char *s1, const char *s2)
{
int i1 = strlen(s1);
int i2 = strlen(s2);
while (--i1 >= 0 && --i2 >= 0) {
if (s1[i1] != s2[i2])
return s1[i1] - s2[i2];
}
return 0;
}
/* Line number list operations */ /* Line number list operations */
/* Add a line to line number list */ /* Add a line to line number list */
...@@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = { ...@@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = {
}; };
/* Get a Dwarf from offline image */ /* Get a Dwarf from offline image */
static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) static int debuginfo__init_offline_dwarf(struct debuginfo *self,
const char *path)
{ {
Dwfl_Module *mod; Dwfl_Module *mod;
Dwarf *dbg = NULL; int fd;
if (!dwflp) fd = open(path, O_RDONLY);
return NULL; if (fd < 0)
return fd;
*dwflp = dwfl_begin(&offline_callbacks); self->dwfl = dwfl_begin(&offline_callbacks);
if (!*dwflp) if (!self->dwfl)
return NULL; goto error;
mod = dwfl_report_offline(*dwflp, "", "", fd); mod = dwfl_report_offline(self->dwfl, "", "", fd);
if (!mod) if (!mod)
goto error; goto error;
dbg = dwfl_module_getdwarf(mod, bias); self->dbg = dwfl_module_getdwarf(mod, &self->bias);
if (!dbg) { if (!self->dbg)
goto error;
return 0;
error: error:
dwfl_end(*dwflp); if (self->dwfl)
*dwflp = NULL; dwfl_end(self->dwfl);
} else
return dbg; close(fd);
memset(self, 0, sizeof(*self));
return -ENOENT;
} }
#if _ELFUTILS_PREREQ(0, 148) #if _ELFUTILS_PREREQ(0, 148)
...@@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = { ...@@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = {
}; };
/* Get a Dwarf from live kernel image */ /* Get a Dwarf from live kernel image */
static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
Dwarf_Addr *bias) Dwarf_Addr addr)
{ {
Dwarf *dbg; self->dwfl = dwfl_begin(&kernel_callbacks);
if (!self->dwfl)
if (!dwflp) return -EINVAL;
return NULL;
*dwflp = dwfl_begin(&kernel_callbacks);
if (!*dwflp)
return NULL;
/* Load the kernel dwarves: Don't care the result here */ /* Load the kernel dwarves: Don't care the result here */
dwfl_linux_kernel_report_kernel(*dwflp); dwfl_linux_kernel_report_kernel(self->dwfl);
dwfl_linux_kernel_report_modules(*dwflp); dwfl_linux_kernel_report_modules(self->dwfl);
dbg = dwfl_addrdwarf(*dwflp, addr, bias); self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias);
/* Here, check whether we could get a real dwarf */ /* Here, check whether we could get a real dwarf */
if (!dbg) { if (!self->dbg) {
pr_debug("Failed to find kernel dwarf at %lx\n", pr_debug("Failed to find kernel dwarf at %lx\n",
(unsigned long)addr); (unsigned long)addr);
dwfl_end(*dwflp); dwfl_end(self->dwfl);
*dwflp = NULL; memset(self, 0, sizeof(*self));
return -ENOENT;
} }
return dbg;
return 0;
} }
#else #else
/* With older elfutils, this just support kernel module... */ /* With older elfutils, this just support kernel module... */
static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp, static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
Dwarf_Addr *bias) Dwarf_Addr addr __used)
{ {
int fd;
const char *path = kernel_get_module_path("kernel"); const char *path = kernel_get_module_path("kernel");
if (!path) { if (!path) {
pr_err("Failed to find vmlinux path\n"); pr_err("Failed to find vmlinux path\n");
return NULL; return -ENOENT;
} }
pr_debug2("Use file %s for debuginfo\n", path); pr_debug2("Use file %s for debuginfo\n", path);
fd = open(path, O_RDONLY); return debuginfo__init_offline_dwarf(self, path);
if (fd < 0)
return NULL;
return dwfl_init_offline_dwarf(fd, dwflp, bias);
} }
#endif #endif
/* Dwarf wrappers */ struct debuginfo *debuginfo__new(const char *path)
/* Find the realpath of the target file. */
static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
{ {
Dwarf_Files *files; struct debuginfo *self = zalloc(sizeof(struct debuginfo));
size_t nfiles, i; if (!self)
const char *src = NULL;
int ret;
if (!fname)
return NULL; return NULL;
ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); if (debuginfo__init_offline_dwarf(self, path) < 0) {
if (ret != 0) free(self);
return NULL; self = NULL;
for (i = 0; i < nfiles; i++) {
src = dwarf_filesrc(files, i, NULL, NULL);
if (strtailcmp(src, fname) == 0)
break;
} }
if (i == nfiles)
return NULL;
return src;
}
/* Get DW_AT_comp_dir (should be NULL with older gcc) */ return self;
static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
{
Dwarf_Attribute attr;
if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
return NULL;
return dwarf_formstring(&attr);
} }
/* Get a line number and file name for given address */ struct debuginfo *debuginfo__new_online_kernel(unsigned long addr)
static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr,
const char **fname, int *lineno)
{ {
Dwarf_Line *line; struct debuginfo *self = zalloc(sizeof(struct debuginfo));
Dwarf_Addr laddr; if (!self)
line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr);
if (line && dwarf_lineaddr(line, &laddr) == 0 &&
addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) {
*fname = dwarf_linesrc(line, NULL, NULL);
if (!*fname)
/* line number is useless without filename */
*lineno = 0;
}
return *lineno ?: -ENOENT;
}
/* Compare diename and tname */
static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
{
const char *name;
name = dwarf_diename(dw_die);
return name ? (strcmp(tname, name) == 0) : false;
}
/* Get callsite line number of inline-function instance */
static int die_get_call_lineno(Dwarf_Die *in_die)
{
Dwarf_Attribute attr;
Dwarf_Word ret;
if (!dwarf_attr(in_die, DW_AT_call_line, &attr))
return -ENOENT;
dwarf_formudata(&attr, &ret);
return (int)ret;
}
/* Get type die */
static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
Dwarf_Attribute attr;
if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) &&
dwarf_formref_die(&attr, die_mem))
return die_mem;
else
return NULL; return NULL;
}
/* Get a type die, but skip qualifiers */ if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) {
static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) free(self);
{ self = NULL;
int tag;
do {
vr_die = die_get_type(vr_die, die_mem);
if (!vr_die)
break;
tag = dwarf_tag(vr_die);
} while (tag == DW_TAG_const_type ||
tag == DW_TAG_restrict_type ||
tag == DW_TAG_volatile_type ||
tag == DW_TAG_shared_type);
return vr_die;
}
/* Get a type die, but skip qualifiers and typedef */
static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
{
do {
vr_die = __die_get_real_type(vr_die, die_mem);
} while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef);
return vr_die;
}
static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name,
Dwarf_Word *result)
{
Dwarf_Attribute attr;
if (dwarf_attr(tp_die, attr_name, &attr) == NULL ||
dwarf_formudata(&attr, result) != 0)
return -ENOENT;
return 0;
}
static bool die_is_signed_type(Dwarf_Die *tp_die)
{
Dwarf_Word ret;
if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret))
return false;
return (ret == DW_ATE_signed_char || ret == DW_ATE_signed ||
ret == DW_ATE_signed_fixed);
}
static int die_get_byte_size(Dwarf_Die *tp_die)
{
Dwarf_Word ret;
if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret))
return 0;
return (int)ret;
}
static int die_get_bit_size(Dwarf_Die *tp_die)
{
Dwarf_Word ret;
if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret))
return 0;
return (int)ret;
}
static int die_get_bit_offset(Dwarf_Die *tp_die)
{
Dwarf_Word ret;
if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret))
return 0;
return (int)ret;
}
/* Get data_member_location offset */
static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
{
Dwarf_Attribute attr;
Dwarf_Op *expr;
size_t nexpr;
int ret;
if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
return -ENOENT;
if (dwarf_formudata(&attr, offs) != 0) {
/* DW_AT_data_member_location should be DW_OP_plus_uconst */
ret = dwarf_getlocation(&attr, &expr, &nexpr);
if (ret < 0 || nexpr == 0)
return -ENOENT;
if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
expr[0].atom, nexpr);
return -ENOTSUP;
}
*offs = (Dwarf_Word)expr[0].number;
}
return 0;
}
/* Return values for die_find callbacks */
enum {
DIE_FIND_CB_FOUND = 0, /* End of Search */
DIE_FIND_CB_CHILD = 1, /* Search only children */
DIE_FIND_CB_SIBLING = 2, /* Search only siblings */
DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */
};
/* Search a child die */
static Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
int (*callback)(Dwarf_Die *, void *),
void *data, Dwarf_Die *die_mem)
{
Dwarf_Die child_die;
int ret;
ret = dwarf_child(rt_die, die_mem);
if (ret != 0)
return NULL;
do {
ret = callback(die_mem, data);
if (ret == DIE_FIND_CB_FOUND)
return die_mem;
if ((ret & DIE_FIND_CB_CHILD) &&
die_find_child(die_mem, callback, data, &child_die)) {
memcpy(die_mem, &child_die, sizeof(Dwarf_Die));
return die_mem;
} }
} while ((ret & DIE_FIND_CB_SIBLING) &&
dwarf_siblingof(die_mem, die_mem) == 0);
return NULL; return self;
} }
struct __addr_die_search_param { void debuginfo__delete(struct debuginfo *self)
Dwarf_Addr addr;
Dwarf_Die *die_mem;
};
static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
{ {
struct __addr_die_search_param *ad = data; if (self) {
if (self->dwfl)
if (dwarf_tag(fn_die) == DW_TAG_subprogram && dwfl_end(self->dwfl);
dwarf_haspc(fn_die, ad->addr)) { free(self);
memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
return DWARF_CB_ABORT;
} }
return DWARF_CB_OK;
}
/* Search a real subprogram including this line, */
static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
struct __addr_die_search_param ad;
ad.addr = addr;
ad.die_mem = die_mem;
/* dwarf_getscopes can't find subprogram. */
if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0))
return NULL;
else
return die_mem;
}
/* die_find callback for inline function search */
static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data)
{
Dwarf_Addr *addr = data;
if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine &&
dwarf_haspc(die_mem, *addr))
return DIE_FIND_CB_FOUND;
return DIE_FIND_CB_CONTINUE;
}
/* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */
static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
Dwarf_Die tmp_die;
sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die);
if (!sp_die)
return NULL;
/* Inlined function could be recursive. Trace it until fail */
while (sp_die) {
memcpy(die_mem, sp_die, sizeof(Dwarf_Die));
sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr,
&tmp_die);
}
return die_mem;
}
/* Walker on lines (Note: line number will not be sorted) */
typedef int (* line_walk_handler_t) (const char *fname, int lineno,
Dwarf_Addr addr, void *data);
struct __line_walk_param {
const char *fname;
line_walk_handler_t handler;
void *data;
int retval;
};
static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data)
{
struct __line_walk_param *lw = data;
Dwarf_Addr addr;
int lineno;
if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) {
lineno = die_get_call_lineno(in_die);
if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) {
lw->retval = lw->handler(lw->fname, lineno, addr,
lw->data);
if (lw->retval != 0)
return DIE_FIND_CB_FOUND;
}
}
return DIE_FIND_CB_SIBLING;
}
/* Walk on lines of blocks included in given DIE */
static int __die_walk_funclines(Dwarf_Die *sp_die,
line_walk_handler_t handler, void *data)
{
struct __line_walk_param lw = {
.handler = handler,
.data = data,
.retval = 0,
};
Dwarf_Die die_mem;
Dwarf_Addr addr;
int lineno;
/* Handle function declaration line */
lw.fname = dwarf_decl_file(sp_die);
if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 &&
dwarf_entrypc(sp_die, &addr) == 0) {
lw.retval = handler(lw.fname, lineno, addr, data);
if (lw.retval != 0)
goto done;
}
die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem);
done:
return lw.retval;
}
static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data)
{
struct __line_walk_param *lw = data;
lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data);
if (lw->retval != 0)
return DWARF_CB_ABORT;
return DWARF_CB_OK;
}
/*
* Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on
* the lines inside the subprogram, otherwise PDIE must be a CU DIE.
*/
static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler,
void *data)
{
Dwarf_Lines *lines;
Dwarf_Line *line;
Dwarf_Addr addr;
const char *fname;
int lineno, ret = 0;
Dwarf_Die die_mem, *cu_die;
size_t nlines, i;
/* Get the CU die */
if (dwarf_tag(pdie) == DW_TAG_subprogram)
cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL);
else
cu_die = pdie;
if (!cu_die) {
pr_debug2("Failed to get CU from subprogram\n");
return -EINVAL;
}
/* Get lines list in the CU */
if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) {
pr_debug2("Failed to get source lines on this CU.\n");
return -ENOENT;
}
pr_debug2("Get %zd lines from this CU\n", nlines);
/* Walk on the lines on lines list */
for (i = 0; i < nlines; i++) {
line = dwarf_onesrcline(lines, i);
if (line == NULL ||
dwarf_lineno(line, &lineno) != 0 ||
dwarf_lineaddr(line, &addr) != 0) {
pr_debug2("Failed to get line info. "
"Possible error in debuginfo.\n");
continue;
}
/* Filter lines based on address */
if (pdie != cu_die)
/*
* Address filtering
* The line is included in given function, and
* no inline block includes it.
*/
if (!dwarf_haspc(pdie, addr) ||
die_find_inlinefunc(pdie, addr, &die_mem))
continue;
/* Get source line */
fname = dwarf_linesrc(line, NULL, NULL);
ret = handler(fname, lineno, addr, data);
if (ret != 0)
return ret;
}
/*
* Dwarf lines doesn't include function declarations and inlined
* subroutines. We have to check functions list or given function.
*/
if (pdie != cu_die)
ret = __die_walk_funclines(pdie, handler, data);
else {
struct __line_walk_param param = {
.handler = handler,
.data = data,
.retval = 0,
};
dwarf_getfuncs(cu_die, __die_walk_culines_cb, &param, 0);
ret = param.retval;
}
return ret;
}
struct __find_variable_param {
const char *name;
Dwarf_Addr addr;
};
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
{
struct __find_variable_param *fvp = data;
int tag;
tag = dwarf_tag(die_mem);
if ((tag == DW_TAG_formal_parameter ||
tag == DW_TAG_variable) &&
die_compare_name(die_mem, fvp->name))
return DIE_FIND_CB_FOUND;
if (dwarf_haspc(die_mem, fvp->addr))
return DIE_FIND_CB_CONTINUE;
else
return DIE_FIND_CB_SIBLING;
}
/* Find a variable called 'name' at given address */
static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name,
Dwarf_Addr addr, Dwarf_Die *die_mem)
{
struct __find_variable_param fvp = { .name = name, .addr = addr};
return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
die_mem);
}
static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
{
const char *name = data;
if ((dwarf_tag(die_mem) == DW_TAG_member) &&
die_compare_name(die_mem, name))
return DIE_FIND_CB_FOUND;
return DIE_FIND_CB_SIBLING;
}
/* Find a member called 'name' */
static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
Dwarf_Die *die_mem)
{
return die_find_child(st_die, __die_find_member_cb, (void *)name,
die_mem);
}
/* Get the name of given variable DIE */
static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
{
Dwarf_Die type;
int tag, ret, ret2;
const char *tmp = "";
if (__die_get_real_type(vr_die, &type) == NULL)
return -ENOENT;
tag = dwarf_tag(&type);
if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)
tmp = "*";
else if (tag == DW_TAG_subroutine_type) {
/* Function pointer */
ret = snprintf(buf, len, "(function_type)");
return (ret >= len) ? -E2BIG : ret;
} else {
if (!dwarf_diename(&type))
return -ENOENT;
if (tag == DW_TAG_union_type)
tmp = "union ";
else if (tag == DW_TAG_structure_type)
tmp = "struct ";
/* Write a base name */
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
return (ret >= len) ? -E2BIG : ret;
}
ret = die_get_typename(&type, buf, len);
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
}
return ret;
}
/* Get the name and type of given variable DIE, stored as "type\tname" */
static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
{
int ret, ret2;
ret = die_get_typename(vr_die, buf, len);
if (ret < 0) {
pr_debug("Failed to get type, make it unknown.\n");
ret = snprintf(buf, len, "(unknown_type)");
}
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "\t%s",
dwarf_diename(vr_die));
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
}
return ret;
} }
/* /*
...@@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, ...@@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die,
struct probe_trace_arg_ref **ref_ptr = &tvar->ref; struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
Dwarf_Die type; Dwarf_Die type;
char buf[16]; char buf[16];
int bsize, boffs, total;
int ret; int ret;
/* TODO: check all types */ /* TODO: check all types */
...@@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die, ...@@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die,
return (tvar->type == NULL) ? -ENOMEM : 0; return (tvar->type == NULL) ? -ENOMEM : 0;
} }
if (die_get_bit_size(vr_die) != 0) { bsize = dwarf_bitsize(vr_die);
if (bsize > 0) {
/* This is a bitfield */ /* This is a bitfield */
ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), boffs = dwarf_bitoffset(vr_die);
die_get_bit_offset(vr_die), total = dwarf_bytesize(vr_die);
BYTES_TO_BITS(die_get_byte_size(vr_die))); if (boffs < 0 || total < 0)
return -ENOENT;
ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs,
BYTES_TO_BITS(total));
goto formatted; goto formatted;
} }
...@@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die, ...@@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die,
return (tvar->type == NULL) ? -ENOMEM : 0; return (tvar->type == NULL) ? -ENOMEM : 0;
} }
ret = BYTES_TO_BITS(die_get_byte_size(&type)); ret = dwarf_bytesize(&type);
if (!ret) if (ret <= 0)
/* No size ... try to use default type */ /* No size ... try to use default type */
return 0; return 0;
ret = BYTES_TO_BITS(ret);
/* Check the bitwidth */ /* Check the bitwidth */
if (ret > MAX_BASIC_TYPE_BITS) { if (ret > MAX_BASIC_TYPE_BITS) {
...@@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, ...@@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
else else
*ref_ptr = ref; *ref_ptr = ref;
} }
ref->offset += die_get_byte_size(&type) * field->index; ref->offset += dwarf_bytesize(&type) * field->index;
if (!field->next) if (!field->next)
/* Save vr_die for converting types */ /* Save vr_die for converting types */
memcpy(die_mem, vr_die, sizeof(*die_mem)); memcpy(die_mem, vr_die, sizeof(*die_mem));
...@@ -1245,8 +728,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) ...@@ -1245,8 +728,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
/* If no real subprogram, find a real one */ /* If no real subprogram, find a real one */
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
sp_die = die_find_real_subprogram(&pf->cu_die, sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem);
pf->addr, &die_mem);
if (!sp_die) { if (!sp_die) {
pr_warning("Failed to find probe point in any " pr_warning("Failed to find probe point in any "
"functions.\n"); "functions.\n");
...@@ -1504,28 +986,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) ...@@ -1504,28 +986,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
} }
/* Find probe points from debuginfo */ /* Find probe points from debuginfo */
static int find_probes(int fd, struct probe_finder *pf) static int debuginfo__find_probes(struct debuginfo *self,
struct probe_finder *pf)
{ {
struct perf_probe_point *pp = &pf->pev->point; struct perf_probe_point *pp = &pf->pev->point;
Dwarf_Off off, noff; Dwarf_Off off, noff;
size_t cuhl; size_t cuhl;
Dwarf_Die *diep; Dwarf_Die *diep;
Dwarf *dbg = NULL;
Dwfl *dwfl;
Dwarf_Addr bias; /* Currently ignored */
int ret = 0; int ret = 0;
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No debug information found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
close(fd); /* Without dwfl_end(), fd isn't closed. */
return -EBADF;
}
#if _ELFUTILS_PREREQ(0, 142) #if _ELFUTILS_PREREQ(0, 142)
/* Get the call frame information from this dwarf */ /* Get the call frame information from this dwarf */
pf->cfi = dwarf_getcfi(dbg); pf->cfi = dwarf_getcfi(self->dbg);
#endif #endif
off = 0; off = 0;
...@@ -1544,7 +1016,8 @@ static int find_probes(int fd, struct probe_finder *pf) ...@@ -1544,7 +1016,8 @@ static int find_probes(int fd, struct probe_finder *pf)
.data = pf, .data = pf,
}; };
dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); dwarf_getpubnames(self->dbg, pubname_search_cb,
&pubname_param, 0);
if (pubname_param.found) { if (pubname_param.found) {
ret = probe_point_search_cb(&pf->sp_die, &probe_param); ret = probe_point_search_cb(&pf->sp_die, &probe_param);
if (ret) if (ret)
...@@ -1553,9 +1026,9 @@ static int find_probes(int fd, struct probe_finder *pf) ...@@ -1553,9 +1026,9 @@ static int find_probes(int fd, struct probe_finder *pf)
} }
/* Loop on CUs (Compilation Unit) */ /* Loop on CUs (Compilation Unit) */
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
/* Get the DIE(Debugging Information Entry) of this CU */ /* Get the DIE(Debugging Information Entry) of this CU */
diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die);
if (!diep) if (!diep)
continue; continue;
...@@ -1582,8 +1055,6 @@ static int find_probes(int fd, struct probe_finder *pf) ...@@ -1582,8 +1055,6 @@ static int find_probes(int fd, struct probe_finder *pf)
found: found:
line_list__free(&pf->lcache); line_list__free(&pf->lcache);
if (dwfl)
dwfl_end(dwfl);
return ret; return ret;
} }
...@@ -1629,7 +1100,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) ...@@ -1629,7 +1100,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
} }
/* Find probe_trace_events specified by perf_probe_event from debuginfo */ /* Find probe_trace_events specified by perf_probe_event from debuginfo */
int find_probe_trace_events(int fd, struct perf_probe_event *pev, int debuginfo__find_trace_events(struct debuginfo *self,
struct perf_probe_event *pev,
struct probe_trace_event **tevs, int max_tevs) struct probe_trace_event **tevs, int max_tevs)
{ {
struct trace_event_finder tf = { struct trace_event_finder tf = {
...@@ -1645,7 +1117,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev, ...@@ -1645,7 +1117,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev,
tf.tevs = *tevs; tf.tevs = *tevs;
tf.ntevs = 0; tf.ntevs = 0;
ret = find_probes(fd, &tf.pf); ret = debuginfo__find_probes(self, &tf.pf);
if (ret < 0) { if (ret < 0) {
free(*tevs); free(*tevs);
*tevs = NULL; *tevs = NULL;
...@@ -1739,9 +1211,10 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) ...@@ -1739,9 +1211,10 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
} }
/* Find available variables at given probe point */ /* Find available variables at given probe point */
int find_available_vars_at(int fd, struct perf_probe_event *pev, int debuginfo__find_available_vars_at(struct debuginfo *self,
struct variable_list **vls, int max_vls, struct perf_probe_event *pev,
bool externs) struct variable_list **vls,
int max_vls, bool externs)
{ {
struct available_var_finder af = { struct available_var_finder af = {
.pf = {.pev = pev, .callback = add_available_vars}, .pf = {.pev = pev, .callback = add_available_vars},
...@@ -1756,7 +1229,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, ...@@ -1756,7 +1229,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
af.vls = *vls; af.vls = *vls;
af.nvls = 0; af.nvls = 0;
ret = find_probes(fd, &af.pf); ret = debuginfo__find_probes(self, &af.pf);
if (ret < 0) { if (ret < 0) {
/* Free vlist for error */ /* Free vlist for error */
while (af.nvls--) { while (af.nvls--) {
...@@ -1774,28 +1247,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, ...@@ -1774,28 +1247,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev,
} }
/* Reverse search */ /* Reverse search */
int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr,
struct perf_probe_point *ppt)
{ {
Dwarf_Die cudie, spdie, indie; Dwarf_Die cudie, spdie, indie;
Dwarf *dbg = NULL; Dwarf_Addr _addr, baseaddr;
Dwfl *dwfl = NULL;
Dwarf_Addr _addr, baseaddr, bias = 0;
const char *fname = NULL, *func = NULL, *tmp; const char *fname = NULL, *func = NULL, *tmp;
int baseline = 0, lineno = 0, ret = 0; int baseline = 0, lineno = 0, ret = 0;
/* Open the live linux kernel */
dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
if (!dbg) {
pr_warning("No debug information found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
ret = -EINVAL;
goto end;
}
/* Adjust address with bias */ /* Adjust address with bias */
addr += bias; addr += self->bias;
/* Find cu die */ /* Find cu die */
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) {
pr_warning("Failed to find debug information for address %lx\n", pr_warning("Failed to find debug information for address %lx\n",
addr); addr);
ret = -EINVAL; ret = -EINVAL;
...@@ -1807,7 +1271,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) ...@@ -1807,7 +1271,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
/* Don't care whether it failed or not */ /* Don't care whether it failed or not */
/* Find a corresponding function (name, baseline and baseaddr) */ /* Find a corresponding function (name, baseline and baseaddr) */
if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
/* Get function entry information */ /* Get function entry information */
tmp = dwarf_diename(&spdie); tmp = dwarf_diename(&spdie);
if (!tmp || if (!tmp ||
...@@ -1871,8 +1335,6 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) ...@@ -1871,8 +1335,6 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
} }
} }
end: end:
if (dwfl)
dwfl_end(dwfl);
if (ret == 0 && (fname || func)) if (ret == 0 && (fname || func))
ret = 1; /* Found a point */ ret = 1; /* Found a point */
return ret; return ret;
...@@ -1982,26 +1444,15 @@ static int find_line_range_by_func(struct line_finder *lf) ...@@ -1982,26 +1444,15 @@ static int find_line_range_by_func(struct line_finder *lf)
return param.retval; return param.retval;
} }
int find_line_range(int fd, struct line_range *lr) int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr)
{ {
struct line_finder lf = {.lr = lr, .found = 0}; struct line_finder lf = {.lr = lr, .found = 0};
int ret = 0; int ret = 0;
Dwarf_Off off = 0, noff; Dwarf_Off off = 0, noff;
size_t cuhl; size_t cuhl;
Dwarf_Die *diep; Dwarf_Die *diep;
Dwarf *dbg = NULL;
Dwfl *dwfl;
Dwarf_Addr bias; /* Currently ignored */
const char *comp_dir; const char *comp_dir;
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
if (!dbg) {
pr_warning("No debug information found in the vmlinux - "
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
close(fd); /* Without dwfl_end(), fd isn't closed. */
return -EBADF;
}
/* Fastpath: lookup by function name from .debug_pubnames section */ /* Fastpath: lookup by function name from .debug_pubnames section */
if (lr->function) { if (lr->function) {
struct pubname_callback_param pubname_param = { struct pubname_callback_param pubname_param = {
...@@ -2010,7 +1461,8 @@ int find_line_range(int fd, struct line_range *lr) ...@@ -2010,7 +1461,8 @@ int find_line_range(int fd, struct line_range *lr)
struct dwarf_callback_param line_range_param = { struct dwarf_callback_param line_range_param = {
.data = (void *)&lf, .retval = 0}; .data = (void *)&lf, .retval = 0};
dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); dwarf_getpubnames(self->dbg, pubname_search_cb,
&pubname_param, 0);
if (pubname_param.found) { if (pubname_param.found) {
line_range_search_cb(&lf.sp_die, &line_range_param); line_range_search_cb(&lf.sp_die, &line_range_param);
if (lf.found) if (lf.found)
...@@ -2020,11 +1472,12 @@ int find_line_range(int fd, struct line_range *lr) ...@@ -2020,11 +1472,12 @@ int find_line_range(int fd, struct line_range *lr)
/* Loop on CUs (Compilation Unit) */ /* Loop on CUs (Compilation Unit) */
while (!lf.found && ret >= 0) { while (!lf.found && ret >= 0) {
if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) if (dwarf_nextcu(self->dbg, off, &noff, &cuhl,
NULL, NULL, NULL) != 0)
break; break;
/* Get the DIE(Debugging Information Entry) of this CU */ /* Get the DIE(Debugging Information Entry) of this CU */
diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die);
if (!diep) if (!diep)
continue; continue;
...@@ -2058,7 +1511,6 @@ int find_line_range(int fd, struct line_range *lr) ...@@ -2058,7 +1511,6 @@ int find_line_range(int fd, struct line_range *lr)
} }
pr_debug("path: %s\n", lr->path); pr_debug("path: %s\n", lr->path);
dwfl_end(dwfl);
return (ret < 0) ? ret : lf.found; return (ret < 0) ? ret : lf.found;
} }
...@@ -16,27 +16,42 @@ static inline int is_c_varname(const char *name) ...@@ -16,27 +16,42 @@ static inline int is_c_varname(const char *name)
} }
#ifdef DWARF_SUPPORT #ifdef DWARF_SUPPORT
#include "dwarf-aux.h"
/* TODO: export debuginfo data structure even if no dwarf support */
/* debug information structure */
struct debuginfo {
Dwarf *dbg;
Dwfl *dwfl;
Dwarf_Addr bias;
};
extern struct debuginfo *debuginfo__new(const char *path);
extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr);
extern void debuginfo__delete(struct debuginfo *self);
/* Find probe_trace_events specified by perf_probe_event from debuginfo */ /* Find probe_trace_events specified by perf_probe_event from debuginfo */
extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, extern int debuginfo__find_trace_events(struct debuginfo *self,
struct perf_probe_event *pev,
struct probe_trace_event **tevs, struct probe_trace_event **tevs,
int max_tevs); int max_tevs);
/* Find a perf_probe_point from debuginfo */ /* Find a perf_probe_point from debuginfo */
extern int find_perf_probe_point(unsigned long addr, extern int debuginfo__find_probe_point(struct debuginfo *self,
unsigned long addr,
struct perf_probe_point *ppt); struct perf_probe_point *ppt);
/* Find a line range */ /* Find a line range */
extern int find_line_range(int fd, struct line_range *lr); extern int debuginfo__find_line_range(struct debuginfo *self,
struct line_range *lr);
/* Find available variables */ /* Find available variables */
extern int find_available_vars_at(int fd, struct perf_probe_event *pev, extern int debuginfo__find_available_vars_at(struct debuginfo *self,
struct variable_list **vls, int max_points, struct perf_probe_event *pev,
bool externs); struct variable_list **vls,
int max_points, bool externs);
#include <dwarf.h>
#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>
#include <elfutils/version.h>
struct probe_finder { struct probe_finder {
struct perf_probe_event *pev; /* Target probe event */ struct perf_probe_event *pev; /* Target probe event */
......
...@@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat) ...@@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat)
{ {
return __match_glob(str, pat, true); return __match_glob(str, pat, true);
} }
/**
* strtailcmp - Compare the tail of two strings
* @s1: 1st string to be compared
* @s2: 2nd string to be compared
*
* Return 0 if whole of either string is same as another's tail part.
*/
int strtailcmp(const char *s1, const char *s2)
{
int i1 = strlen(s1);
int i2 = strlen(s2);
while (--i1 >= 0 && --i2 >= 0) {
if (s1[i1] != s2[i2])
return s1[i1] - s2[i2];
}
return 0;
}
...@@ -183,106 +183,59 @@ int bigendian(void) ...@@ -183,106 +183,59 @@ int bigendian(void)
return *ptr == 0x01020304; return *ptr == 0x01020304;
} }
static unsigned long long copy_file_fd(int fd) /* unfortunately, you can not stat debugfs or proc files for size */
static void record_file(const char *file, size_t hdr_sz)
{ {
unsigned long long size = 0; unsigned long long size = 0;
char buf[BUFSIZ]; char buf[BUFSIZ], *sizep;
int r; off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR);
int r, fd;
do {
r = read(fd, buf, BUFSIZ);
if (r > 0) {
size += r;
write_or_die(buf, r);
}
} while (r > 0);
return size;
}
static unsigned long long copy_file(const char *file)
{
unsigned long long size = 0;
int fd;
fd = open(file, O_RDONLY); fd = open(file, O_RDONLY);
if (fd < 0) if (fd < 0)
die("Can't read '%s'", file); die("Can't read '%s'", file);
size = copy_file_fd(fd);
close(fd);
return size; /* put in zeros for file size, then fill true size later */
} write_or_die(&size, hdr_sz);
static unsigned long get_size_fd(int fd)
{
unsigned long long size = 0;
char buf[BUFSIZ];
int r;
do { do {
r = read(fd, buf, BUFSIZ); r = read(fd, buf, BUFSIZ);
if (r > 0) if (r > 0) {
size += r; size += r;
write_or_die(buf, r);
}
} while (r > 0); } while (r > 0);
lseek(fd, 0, SEEK_SET);
return size;
}
static unsigned long get_size(const char *file)
{
unsigned long long size = 0;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0)
die("Can't read '%s'", file);
size = get_size_fd(fd);
close(fd); close(fd);
return size; /* ugh, handle big-endian hdr_size == 4 */
sizep = (char*)&size;
if (bigendian())
sizep += sizeof(u64) - hdr_sz;
if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
die("writing to %s", output_file);
} }
static void read_header_files(void) static void read_header_files(void)
{ {
unsigned long long size, check_size;
char *path; char *path;
int fd; struct stat st;
path = get_tracing_file("events/header_page"); path = get_tracing_file("events/header_page");
fd = open(path, O_RDONLY); if (stat(path, &st) < 0)
if (fd < 0)
die("can't read '%s'", path); die("can't read '%s'", path);
/* unfortunately, you can not stat debugfs files for size */
size = get_size_fd(fd);
write_or_die("header_page", 12); write_or_die("header_page", 12);
write_or_die(&size, 8); record_file(path, 8);
check_size = copy_file_fd(fd);
close(fd);
if (size != check_size)
die("wrong size for '%s' size=%lld read=%lld",
path, size, check_size);
put_tracing_file(path); put_tracing_file(path);
path = get_tracing_file("events/header_event"); path = get_tracing_file("events/header_event");
fd = open(path, O_RDONLY); if (stat(path, &st) < 0)
if (fd < 0)
die("can't read '%s'", path); die("can't read '%s'", path);
size = get_size_fd(fd);
write_or_die("header_event", 13); write_or_die("header_event", 13);
write_or_die(&size, 8); record_file(path, 8);
check_size = copy_file_fd(fd);
if (size != check_size)
die("wrong size for '%s'", path);
put_tracing_file(path); put_tracing_file(path);
close(fd);
} }
static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
...@@ -298,7 +251,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) ...@@ -298,7 +251,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps)
static void copy_event_system(const char *sys, struct tracepoint_path *tps) static void copy_event_system(const char *sys, struct tracepoint_path *tps)
{ {
unsigned long long size, check_size;
struct dirent *dent; struct dirent *dent;
struct stat st; struct stat st;
char *format; char *format;
...@@ -338,14 +290,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) ...@@ -338,14 +290,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps)
sprintf(format, "%s/%s/format", sys, dent->d_name); sprintf(format, "%s/%s/format", sys, dent->d_name);
ret = stat(format, &st); ret = stat(format, &st);
if (ret >= 0) { if (ret >= 0)
/* unfortunately, you can not stat debugfs files for size */ record_file(format, 8);
size = get_size(format);
write_or_die(&size, 8);
check_size = copy_file(format);
if (size != check_size)
die("error in size of file '%s'", format);
}
free(format); free(format);
} }
...@@ -426,7 +372,7 @@ static void read_event_files(struct tracepoint_path *tps) ...@@ -426,7 +372,7 @@ static void read_event_files(struct tracepoint_path *tps)
static void read_proc_kallsyms(void) static void read_proc_kallsyms(void)
{ {
unsigned int size, check_size; unsigned int size;
const char *path = "/proc/kallsyms"; const char *path = "/proc/kallsyms";
struct stat st; struct stat st;
int ret; int ret;
...@@ -438,17 +384,12 @@ static void read_proc_kallsyms(void) ...@@ -438,17 +384,12 @@ static void read_proc_kallsyms(void)
write_or_die(&size, 4); write_or_die(&size, 4);
return; return;
} }
size = get_size(path); record_file(path, 4);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
die("error in size of file '%s'", path);
} }
static void read_ftrace_printk(void) static void read_ftrace_printk(void)
{ {
unsigned int size, check_size; unsigned int size;
char *path; char *path;
struct stat st; struct stat st;
int ret; int ret;
...@@ -461,11 +402,8 @@ static void read_ftrace_printk(void) ...@@ -461,11 +402,8 @@ static void read_ftrace_printk(void)
write_or_die(&size, 4); write_or_die(&size, 4);
goto out; goto out;
} }
size = get_size(path); record_file(path, 4);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
die("error in size of file '%s'", path);
out: out:
put_tracing_file(path); put_tracing_file(path);
} }
......
...@@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp); ...@@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp);
void argv_free(char **argv); void argv_free(char **argv);
bool strglobmatch(const char *str, const char *pat); bool strglobmatch(const char *str, const char *pat);
bool strlazymatch(const char *str, const char *pat); bool strlazymatch(const char *str, const char *pat);
int strtailcmp(const char *s1, const char *s2);
unsigned long convert_unit(unsigned long value, char *unit); unsigned long convert_unit(unsigned long value, char *unit);
int readn(int fd, void *buf, size_t size); int readn(int fd, void *buf, size_t size);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment