mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge branch 'tip/perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into perf/core
This commit is contained in:
@@ -22,14 +22,15 @@ current_tracer. Instead of that, add probe points via
|
||||
|
||||
Synopsis of kprobe_events
|
||||
-------------------------
|
||||
p[:[GRP/]EVENT] SYMBOL[+offs]|MEMADDR [FETCHARGS] : Set a probe
|
||||
r[:[GRP/]EVENT] SYMBOL[+0] [FETCHARGS] : Set a return probe
|
||||
p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
|
||||
r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
|
||||
-:[GRP/]EVENT : Clear a probe
|
||||
|
||||
GRP : Group name. If omitted, use "kprobes" for it.
|
||||
EVENT : Event name. If omitted, the event name is generated
|
||||
based on SYMBOL+offs or MEMADDR.
|
||||
SYMBOL[+offs] : Symbol+offset where the probe is inserted.
|
||||
based on SYM+offs or MEMADDR.
|
||||
MOD : Module name which has given SYM.
|
||||
SYM[+offs] : Symbol+offset where the probe is inserted.
|
||||
MEMADDR : Address where the probe is inserted.
|
||||
|
||||
FETCHARGS : Arguments. Each probe can have up to 128 args.
|
||||
|
||||
@@ -101,6 +101,14 @@
|
||||
#define P4_CONFIG_HT_SHIFT 63
|
||||
#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
|
||||
*/
|
||||
@@ -123,6 +131,31 @@
|
||||
(p4_config_pack_escr(P4_CONFIG_MASK_ESCR)) | \
|
||||
(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)
|
||||
{
|
||||
u32 cccr = p4_config_unpack_cccr(config);
|
||||
|
||||
@@ -274,7 +274,6 @@ struct x86_pmu {
|
||||
void (*enable_all)(int added);
|
||||
void (*enable)(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 (*schedule_events)(struct cpu_hw_events *cpuc, int n, int *assign);
|
||||
unsigned eventsel;
|
||||
@@ -360,12 +359,6 @@ static u64 __read_mostly hw_cache_extra_regs
|
||||
[PERF_COUNT_HW_CACHE_OP_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.
|
||||
* 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
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* 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] = {
|
||||
/* non-halted CPU clocks */
|
||||
[PERF_COUNT_HW_CPU_CYCLES] =
|
||||
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
|
||||
@@ -719,31 +800,6 @@ static int p4_validate_raw_event(struct perf_event *event)
|
||||
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)
|
||||
{
|
||||
int cpu = get_cpu();
|
||||
@@ -1159,6 +1215,8 @@ static int p4_pmu_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign
|
||||
struct p4_event_bind *bind;
|
||||
unsigned int i, thread, num;
|
||||
int cntr_idx, escr_idx;
|
||||
u64 config_alias;
|
||||
int pass;
|
||||
|
||||
bitmap_zero(used_mask, X86_PMC_IDX_MAX);
|
||||
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
|
||||
|
||||
hwc = &cpuc->event_list[i]->hw;
|
||||
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);
|
||||
escr_idx = p4_get_escr_idx(bind->escr_msr[thread]);
|
||||
if (unlikely(escr_idx == -1))
|
||||
@@ -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);
|
||||
if (cntr_idx == -1 || test_bit(escr_idx, escr_mask))
|
||||
goto done;
|
||||
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;
|
||||
hwc->config = config_alias;
|
||||
pass++;
|
||||
goto again;
|
||||
}
|
||||
|
||||
p4_pmu_swap_config_ts(hwc, cpu);
|
||||
if (assign)
|
||||
@@ -1218,7 +1296,6 @@ static __initconst const struct x86_pmu p4_pmu = {
|
||||
.cntval_bits = ARCH_P4_CNTRVAL_BITS,
|
||||
.cntval_mask = ARCH_P4_CNTRVAL_MASK,
|
||||
.max_period = (1ULL << (ARCH_P4_CNTRVAL_BITS - 1)) - 1,
|
||||
.hw_watchdog_set_attr = p4_hw_watchdog_set_attr,
|
||||
.hw_config = p4_hw_config,
|
||||
.schedule_events = p4_pmu_schedule_events,
|
||||
/*
|
||||
|
||||
@@ -76,6 +76,7 @@ struct trace_iterator {
|
||||
struct trace_entry *ent;
|
||||
unsigned long lost_events;
|
||||
int leftover;
|
||||
int ent_size;
|
||||
int cpu;
|
||||
u64 ts;
|
||||
|
||||
|
||||
@@ -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
|
||||
* 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)
|
||||
{
|
||||
kprobe_opcode_t *addr = p->addr;
|
||||
|
||||
if ((p->symbol_name && p->addr) ||
|
||||
(!p->symbol_name && !p->addr))
|
||||
goto invalid;
|
||||
|
||||
if (p->symbol_name) {
|
||||
if (addr)
|
||||
return NULL;
|
||||
kprobe_lookup_name(p->symbol_name, addr);
|
||||
if (!addr)
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
if (!addr)
|
||||
return NULL;
|
||||
return (kprobe_opcode_t *)(((char *)addr) + p->offset);
|
||||
addr = (kprobe_opcode_t *)(((char *)addr) + p->offset);
|
||||
if (addr)
|
||||
return addr;
|
||||
|
||||
invalid:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* Check passed kprobe is valid and return kprobe in kprobe_table. */
|
||||
@@ -1311,8 +1321,8 @@ int __kprobes register_kprobe(struct kprobe *p)
|
||||
kprobe_opcode_t *addr;
|
||||
|
||||
addr = kprobe_addr(p);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
p->addr = addr;
|
||||
|
||||
ret = check_kprobe_rereg(p);
|
||||
@@ -1335,6 +1345,8 @@ int __kprobes register_kprobe(struct kprobe *p)
|
||||
*/
|
||||
probed_mod = __module_text_address((unsigned long) p->addr);
|
||||
if (probed_mod) {
|
||||
/* Return -ENOENT if fail. */
|
||||
ret = -ENOENT;
|
||||
/*
|
||||
* We must hold a refcount of the probed module while updating
|
||||
* its code to prohibit unexpected unloading.
|
||||
@@ -1351,6 +1363,7 @@ int __kprobes register_kprobe(struct kprobe *p)
|
||||
module_put(probed_mod);
|
||||
goto fail_with_jump_label;
|
||||
}
|
||||
/* ret will be updated by following code */
|
||||
}
|
||||
preempt_enable();
|
||||
jump_label_unlock();
|
||||
@@ -1399,7 +1412,7 @@ out:
|
||||
fail_with_jump_label:
|
||||
preempt_enable();
|
||||
jump_label_unlock();
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_kprobe);
|
||||
|
||||
@@ -1686,8 +1699,8 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
|
||||
|
||||
if (kretprobe_blacklist_size) {
|
||||
addr = kprobe_addr(&rp->kp);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
if (IS_ERR(addr))
|
||||
return PTR_ERR(addr);
|
||||
|
||||
for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
|
||||
if (kretprobe_blacklist[i].addr == addr)
|
||||
|
||||
@@ -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_ops_list __read_mostly = &ftrace_list_end;
|
||||
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_pid_function __read_mostly = ftrace_stub;
|
||||
static struct ftrace_ops global_ops;
|
||||
@@ -146,9 +147,11 @@ void clear_ftrace_function(void)
|
||||
{
|
||||
ftrace_trace_function = ftrace_stub;
|
||||
__ftrace_trace_function = ftrace_stub;
|
||||
__ftrace_trace_function_delay = ftrace_stub;
|
||||
ftrace_pid_function = ftrace_stub;
|
||||
}
|
||||
|
||||
#undef 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
|
||||
@@ -207,8 +210,13 @@ static void update_ftrace_function(void)
|
||||
|
||||
#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
|
||||
ftrace_trace_function = func;
|
||||
#else
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
/* do not update till all functions have been modified */
|
||||
__ftrace_trace_function_delay = func;
|
||||
#else
|
||||
__ftrace_trace_function = func;
|
||||
#endif
|
||||
ftrace_trace_function = ftrace_test_stop_func;
|
||||
#endif
|
||||
}
|
||||
@@ -1170,8 +1178,14 @@ alloc_and_copy_ftrace_hash(int size_bits, struct ftrace_hash *hash)
|
||||
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
|
||||
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 hlist_node *tp, *tn;
|
||||
@@ -1181,8 +1195,15 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
|
||||
unsigned long key;
|
||||
int size = src->count;
|
||||
int bits = 0;
|
||||
int ret;
|
||||
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
|
||||
* the empty_hash.
|
||||
@@ -1203,9 +1224,10 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
|
||||
if (bits > FTRACE_HASH_MAX_BITS)
|
||||
bits = FTRACE_HASH_MAX_BITS;
|
||||
|
||||
ret = -ENOMEM;
|
||||
new_hash = alloc_ftrace_hash(bits);
|
||||
if (!new_hash)
|
||||
return -ENOMEM;
|
||||
goto out;
|
||||
|
||||
size = 1 << src->size_bits;
|
||||
for (i = 0; i < size; i++) {
|
||||
@@ -1224,7 +1246,16 @@ ftrace_hash_move(struct ftrace_hash **dst, struct ftrace_hash *src)
|
||||
rcu_assign_pointer(*dst, new_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)
|
||||
{
|
||||
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)
|
||||
ftrace_replace_code(1);
|
||||
else if (*command & FTRACE_DISABLE_CALLS)
|
||||
@@ -1597,6 +1634,18 @@ static int __ftrace_modify_code(void *data)
|
||||
else if (*command & FTRACE_STOP_FUNC_RET)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -2865,7 +2914,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
|
||||
ftrace_match_records(hash, buf, len);
|
||||
|
||||
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_regex_lock);
|
||||
@@ -3048,18 +3101,12 @@ ftrace_regex_release(struct inode *inode, struct file *file)
|
||||
orig_hash = &iter->ops->notrace_hash;
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
/*
|
||||
* Remove the current set, update the hash and add
|
||||
* them back.
|
||||
*/
|
||||
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_run_update_code(FTRACE_ENABLE_CALLS);
|
||||
}
|
||||
ret = ftrace_hash_move(iter->ops, filter_hash,
|
||||
orig_hash, iter->hash);
|
||||
if (!ret && (iter->ops->flags & FTRACE_OPS_FL_ENABLED)
|
||||
&& ftrace_enabled)
|
||||
ftrace_run_update_code(FTRACE_ENABLE_CALLS);
|
||||
|
||||
mutex_unlock(&ftrace_lock);
|
||||
}
|
||||
free_ftrace_hash(iter->hash);
|
||||
@@ -3338,7 +3385,7 @@ static int ftrace_process_locs(struct module *mod,
|
||||
{
|
||||
unsigned long *p;
|
||||
unsigned long addr;
|
||||
unsigned long flags;
|
||||
unsigned long flags = 0; /* Shut up gcc */
|
||||
|
||||
mutex_lock(&ftrace_lock);
|
||||
p = start;
|
||||
@@ -3356,12 +3403,18 @@ static int ftrace_process_locs(struct module *mod,
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable interrupts to prevent interrupts from executing
|
||||
* code that is being modified.
|
||||
* We only need to disable interrupts on start up
|
||||
* 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.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
if (!mod)
|
||||
local_irq_save(flags);
|
||||
ftrace_update_code(mod);
|
||||
local_irq_restore(flags);
|
||||
if (!mod)
|
||||
local_irq_restore(flags);
|
||||
mutex_unlock(&ftrace_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1248,6 +1248,15 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data,
|
||||
}
|
||||
|
||||
#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,
|
||||
unsigned long flags,
|
||||
int skip, int pc, struct pt_regs *regs)
|
||||
@@ -1256,25 +1265,77 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
|
||||
struct ring_buffer_event *event;
|
||||
struct stack_entry *entry;
|
||||
struct stack_trace trace;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_STACK,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
memset(&entry->caller, 0, sizeof(entry->caller));
|
||||
int use_stack;
|
||||
int size = FTRACE_STACK_ENTRIES;
|
||||
|
||||
trace.nr_entries = 0;
|
||||
trace.max_entries = FTRACE_STACK_ENTRIES;
|
||||
trace.skip = skip;
|
||||
trace.entries = entry->caller;
|
||||
|
||||
if (regs)
|
||||
save_stack_trace_regs(regs, &trace);
|
||||
else
|
||||
save_stack_trace(&trace);
|
||||
/*
|
||||
* 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,
|
||||
sizeof(*entry) + size, flags, pc);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
|
||||
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.entries = entry->caller;
|
||||
if (regs)
|
||||
save_stack_trace_regs(regs, &trace);
|
||||
else
|
||||
save_stack_trace(&trace);
|
||||
}
|
||||
|
||||
entry->size = trace.nr_entries;
|
||||
|
||||
if (!filter_check_discard(call, entry, 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,
|
||||
@@ -1562,7 +1623,12 @@ peek_next_entry(struct trace_iterator *iter, int cpu, u64 *ts,
|
||||
|
||||
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 *
|
||||
|
||||
@@ -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
|
||||
|
||||
int tracer_init(struct tracer *t, struct trace_array *tr);
|
||||
@@ -516,8 +539,18 @@ static inline int ftrace_graph_addr(unsigned long addr)
|
||||
return 1;
|
||||
|
||||
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 0;
|
||||
@@ -795,19 +828,4 @@ extern const char *__stop___trace_bprintk_fmt[];
|
||||
FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print))
|
||||
#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 */
|
||||
|
||||
@@ -161,7 +161,8 @@ FTRACE_ENTRY(kernel_stack, stack_entry,
|
||||
TRACE_STACK,
|
||||
|
||||
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"
|
||||
|
||||
@@ -227,7 +227,7 @@ int __trace_graph_entry(struct trace_array *tr,
|
||||
|
||||
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 in_irq();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1107,19 +1107,20 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
|
||||
{
|
||||
struct stack_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int i;
|
||||
unsigned long *p;
|
||||
unsigned long *end;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
end = (unsigned long *)((long)iter->ent + iter->ent_size);
|
||||
|
||||
if (!trace_seq_puts(s, "<stack trace>\n"))
|
||||
goto partial;
|
||||
for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
|
||||
if (!field->caller[i] || (field->caller[i] == ULONG_MAX))
|
||||
break;
|
||||
|
||||
for (p = field->caller; p && *p != ULONG_MAX && p < end; p++) {
|
||||
if (!trace_seq_puts(s, " => "))
|
||||
goto partial;
|
||||
|
||||
if (!seq_print_ip_sym(s, field->caller[i], flags))
|
||||
if (!seq_print_ip_sym(s, *p, flags))
|
||||
goto partial;
|
||||
if (!trace_seq_puts(s, "\n"))
|
||||
goto partial;
|
||||
|
||||
@@ -200,7 +200,6 @@ static int is_softlockup(unsigned long touch_ts)
|
||||
}
|
||||
|
||||
#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 = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
@@ -372,7 +371,6 @@ static int watchdog_nmi_enable(int cpu)
|
||||
|
||||
wd_attr = &wd_hw_attr;
|
||||
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 */
|
||||
event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
|
||||
|
||||
@@ -34,9 +34,11 @@ OPTIONS
|
||||
Specify vmlinux path which has debuginfo (Dwarf binary).
|
||||
|
||||
-m::
|
||||
--module=MODNAME::
|
||||
--module=MODNAME|PATH::
|
||||
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::
|
||||
--source=PATH::
|
||||
|
||||
@@ -279,6 +279,7 @@ LIB_H += util/thread.h
|
||||
LIB_H += util/thread_map.h
|
||||
LIB_H += util/trace-event.h
|
||||
LIB_H += util/probe-finder.h
|
||||
LIB_H += util/dwarf-aux.h
|
||||
LIB_H += util/probe-event.h
|
||||
LIB_H += util/pstack.h
|
||||
LIB_H += util/cpumap.h
|
||||
@@ -435,6 +436,7 @@ else
|
||||
BASIC_CFLAGS += -DDWARF_SUPPORT
|
||||
EXTLIBS += -lelf -ldw
|
||||
LIB_OBJS += $(OUTPUT)util/probe-finder.o
|
||||
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
|
||||
endif # PERF_HAVE_DWARF_REGS
|
||||
endif # NO_DWARF
|
||||
|
||||
|
||||
@@ -242,7 +242,8 @@ static const struct option options[] = {
|
||||
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
||||
"directory", "path to kernel source"),
|
||||
OPT_STRING('m', "module", ¶ms.target_module,
|
||||
"modname", "target module name"),
|
||||
"modname|path",
|
||||
"target module name (for online) or path (for offline)"),
|
||||
#endif
|
||||
OPT__DRY_RUN(&probe_event_dry_run),
|
||||
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
|
||||
|
||||
663
tools/perf/util/dwarf-aux.c
Normal file
663
tools/perf/util/dwarf-aux.c
Normal file
File diff suppressed because it is too large
Load Diff
100
tools/perf/util/dwarf-aux.h
Normal file
100
tools/perf/util/dwarf-aux.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#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)
|
||||
struct rb_node *nd;
|
||||
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)
|
||||
module = "kernel";
|
||||
|
||||
@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module)
|
||||
}
|
||||
|
||||
#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);
|
||||
if (!path) {
|
||||
pr_err("Failed to find path of %s module.\n",
|
||||
module ?: "kernel");
|
||||
return -ENOENT;
|
||||
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) {
|
||||
pr_err("Failed to find path of %s module.\n",
|
||||
module ?: "kernel");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
pr_debug("Try to open %s\n", path);
|
||||
return open(path, O_RDONLY);
|
||||
return debuginfo__new(path);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
||||
struct map *map;
|
||||
u64 addr;
|
||||
int ret = -ENOENT;
|
||||
struct debuginfo *dinfo;
|
||||
|
||||
sym = __find_kernel_function_by_name(tp->symbol, &map);
|
||||
if (sym) {
|
||||
addr = map->unmap_ip(map, sym->start + tp->offset);
|
||||
pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol,
|
||||
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) {
|
||||
pr_debug("Failed to find corresponding probes from "
|
||||
@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
||||
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 */
|
||||
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs,
|
||||
int max_tevs, const char *module)
|
||||
struct probe_trace_event **tevs,
|
||||
int max_tevs, const char *module)
|
||||
{
|
||||
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 (fd < 0) {
|
||||
if (!dinfo) {
|
||||
if (need_dwarf) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* Searching trace events corresponding to probe event */
|
||||
ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
|
||||
/* Searching trace events corresponding to a probe event */
|
||||
ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs);
|
||||
|
||||
debuginfo__delete(dinfo);
|
||||
|
||||
if (ntevs > 0) { /* Succeeded to find trace events */
|
||||
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. */
|
||||
@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module)
|
||||
{
|
||||
int l = 1;
|
||||
struct line_node *ln;
|
||||
struct debuginfo *dinfo;
|
||||
FILE *fp;
|
||||
int fd, ret;
|
||||
int ret;
|
||||
char *tmp;
|
||||
|
||||
/* Search a line range */
|
||||
@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fd = open_vmlinux(module);
|
||||
if (fd < 0) {
|
||||
dinfo = open_debuginfo(module);
|
||||
if (!dinfo) {
|
||||
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) {
|
||||
pr_warning("Specified source line is not found.\n");
|
||||
return -ENOENT;
|
||||
@@ -448,7 +513,8 @@ end:
|
||||
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,
|
||||
bool externs)
|
||||
{
|
||||
@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||
return -EINVAL;
|
||||
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) {
|
||||
pr_err("Failed to find variables at %s (%d)\n", buf, ret);
|
||||
goto end;
|
||||
@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
int max_vls, const char *module,
|
||||
struct strfilter *_filter, bool externs)
|
||||
{
|
||||
int i, fd, ret = 0;
|
||||
int i, ret = 0;
|
||||
struct debuginfo *dinfo;
|
||||
|
||||
ret = init_vmlinux();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dinfo = open_debuginfo(module);
|
||||
if (!dinfo) {
|
||||
pr_warning("Failed to open debuginfo file.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
setup_pager();
|
||||
|
||||
for (i = 0; i < npevs && ret >= 0; i++) {
|
||||
fd = open_vmlinux(module);
|
||||
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,
|
||||
for (i = 0; i < npevs && ret >= 0; i++)
|
||||
ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter,
|
||||
externs);
|
||||
}
|
||||
|
||||
debuginfo__delete(dinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -990,7 +1059,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
|
||||
|
||||
/* Parse probe_events event into struct probe_point */
|
||||
static int parse_probe_trace_command(const char *cmd,
|
||||
struct probe_trace_event *tev)
|
||||
struct probe_trace_event *tev)
|
||||
{
|
||||
struct probe_trace_point *tp = &tev->point;
|
||||
char pr;
|
||||
@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd,
|
||||
|
||||
tp->retprobe = (pr == 'r');
|
||||
|
||||
/* Scan function name and offset */
|
||||
ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol,
|
||||
/* Scan module name(if there), function name and offset */
|
||||
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);
|
||||
if (ret == 1)
|
||||
tp->offset = 0;
|
||||
@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
|
||||
if (buf == 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',
|
||||
tev->group, tev->event,
|
||||
tp->module ?: "", tp->module ? ":" : "",
|
||||
tp->symbol, tp->offset);
|
||||
if (len <= 0)
|
||||
goto error;
|
||||
@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
|
||||
free(tev->group);
|
||||
if (tev->point.symbol)
|
||||
free(tev->point.symbol);
|
||||
if (tev->point.module)
|
||||
free(tev->point.module);
|
||||
for (i = 0; i < tev->nargs; i++) {
|
||||
if (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,
|
||||
/* Convert perf_probe_event with debuginfo */
|
||||
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
return ret; /* Found in debuginfo or got an error */
|
||||
|
||||
/* Allocate trace event buffer */
|
||||
tev = *tevs = zalloc(sizeof(struct probe_trace_event));
|
||||
@@ -1742,6 +1820,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
tev->point.module = strdup(module);
|
||||
if (tev->point.module == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
tev->point.offset = pev->point.offset;
|
||||
tev->point.retprobe = pev->point.retprobe;
|
||||
tev->nargs = pev->nargs;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user