Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/urgent
This commit is contained in:
commit
b8ecad8b2f
|
@ -410,7 +410,7 @@ extern void open_softirq(int nr, void (*action)(struct softirq_action *));
|
|||
extern void softirq_init(void);
|
||||
static inline void __raise_softirq_irqoff(unsigned int nr)
|
||||
{
|
||||
trace_softirq_raise((struct softirq_action *)(unsigned long)nr, NULL);
|
||||
trace_softirq_raise(nr);
|
||||
or_softirq_pending(1UL << nr);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,76 +86,62 @@ TRACE_EVENT(irq_handler_exit,
|
|||
|
||||
DECLARE_EVENT_CLASS(softirq,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec),
|
||||
TP_ARGS(vec_nr),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field( int, vec )
|
||||
__field( unsigned int, vec )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
if (vec)
|
||||
__entry->vec = (int)(h - vec);
|
||||
else
|
||||
__entry->vec = (int)(long)h;
|
||||
__entry->vec = vec_nr;
|
||||
),
|
||||
|
||||
TP_printk("vec=%d [action=%s]", __entry->vec,
|
||||
TP_printk("vec=%u [action=%s]", __entry->vec,
|
||||
show_softirq_name(__entry->vec))
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_entry - called immediately before the softirq handler
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter, contains a pointer to the struct softirq_action
|
||||
* which has a pointer to the action handler that is called. By subtracting
|
||||
* the @vec pointer from the @h pointer, we can determine the softirq
|
||||
* number. Also, when used in combination with the softirq_exit tracepoint
|
||||
* we can determine the softirq latency.
|
||||
* When used in combination with the softirq_exit tracepoint
|
||||
* we can determine the softirq handler runtine.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_entry,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_exit - called immediately after the softirq handler returns
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter contains a pointer to the struct softirq_action
|
||||
* that has handled the softirq. By subtracting the @vec pointer from
|
||||
* the @h pointer, we can determine the softirq number. Also, when used in
|
||||
* combination with the softirq_entry tracepoint we can determine the softirq
|
||||
* latency.
|
||||
* When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq handler runtine.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_exit,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
/**
|
||||
* softirq_raise - called immediately when a softirq is raised
|
||||
* @h: pointer to struct softirq_action
|
||||
* @vec: pointer to first struct softirq_action in softirq_vec array
|
||||
* @vec_nr: softirq vector number
|
||||
*
|
||||
* The @h parameter contains a pointer to the softirq vector number which is
|
||||
* raised. @vec is NULL and it means @h includes vector number not
|
||||
* softirq_action. When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq raise latency.
|
||||
* When used in combination with the softirq_entry tracepoint
|
||||
* we can determine the softirq raise to run latency.
|
||||
*/
|
||||
DEFINE_EVENT(softirq, softirq_raise,
|
||||
|
||||
TP_PROTO(struct softirq_action *h, struct softirq_action *vec),
|
||||
TP_PROTO(unsigned int vec_nr),
|
||||
|
||||
TP_ARGS(h, vec)
|
||||
TP_ARGS(vec_nr)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_IRQ_H */
|
||||
|
|
|
@ -229,18 +229,20 @@ restart:
|
|||
|
||||
do {
|
||||
if (pending & 1) {
|
||||
unsigned int vec_nr = h - softirq_vec;
|
||||
int prev_count = preempt_count();
|
||||
kstat_incr_softirqs_this_cpu(h - softirq_vec);
|
||||
|
||||
trace_softirq_entry(h, softirq_vec);
|
||||
kstat_incr_softirqs_this_cpu(vec_nr);
|
||||
|
||||
trace_softirq_entry(vec_nr);
|
||||
h->action(h);
|
||||
trace_softirq_exit(h, softirq_vec);
|
||||
trace_softirq_exit(vec_nr);
|
||||
if (unlikely(prev_count != preempt_count())) {
|
||||
printk(KERN_ERR "huh, entered softirq %td %s %p"
|
||||
printk(KERN_ERR "huh, entered softirq %u %s %p"
|
||||
"with preempt_count %08x,"
|
||||
" exited with %08x?\n", h - softirq_vec,
|
||||
softirq_to_name[h - softirq_vec],
|
||||
h->action, prev_count, preempt_count());
|
||||
" exited with %08x?\n", vec_nr,
|
||||
softirq_to_name[vec_nr], h->action,
|
||||
prev_count, preempt_count());
|
||||
preempt_count() = prev_count;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ or
|
|||
or
|
||||
'perf probe' --list
|
||||
or
|
||||
'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
||||
'perf probe' [options] --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
|
||||
or
|
||||
'perf probe' [options] --vars='PROBEPOINT'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -31,6 +33,11 @@ OPTIONS
|
|||
--vmlinux=PATH::
|
||||
Specify vmlinux path which has debuginfo (Dwarf binary).
|
||||
|
||||
-m::
|
||||
--module=MODNAME::
|
||||
Specify module name in which perf-probe searches probe points
|
||||
or lines.
|
||||
|
||||
-s::
|
||||
--source=PATH::
|
||||
Specify path to kernel source.
|
||||
|
@ -57,6 +64,15 @@ OPTIONS
|
|||
Show source code lines which can be probed. This needs an argument
|
||||
which specifies a range of the source code. (see LINE SYNTAX for detail)
|
||||
|
||||
-V::
|
||||
--vars=::
|
||||
Show available local variables at given probe point. The argument
|
||||
syntax is same as PROBE SYNTAX, but NO ARGs.
|
||||
|
||||
--externs::
|
||||
(Only for --vars) Show external defined variables in addition to local
|
||||
variables.
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Forcibly add events with existing name.
|
||||
|
|
|
@ -50,14 +50,17 @@ static struct {
|
|||
bool list_events;
|
||||
bool force_add;
|
||||
bool show_lines;
|
||||
bool show_vars;
|
||||
bool show_ext_vars;
|
||||
bool mod_events;
|
||||
int nevents;
|
||||
struct perf_probe_event events[MAX_PROBES];
|
||||
struct strlist *dellist;
|
||||
struct line_range line_range;
|
||||
const char *target_module;
|
||||
int max_probe_points;
|
||||
} params;
|
||||
|
||||
|
||||
/* Parse an event definition. Note that any error must die. */
|
||||
static int parse_probe_event(const char *str)
|
||||
{
|
||||
|
@ -92,6 +95,7 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
|||
len = 0;
|
||||
for (i = 0; i < argc; i++)
|
||||
len += sprintf(&buf[len], "%s ", argv[i]);
|
||||
params.mod_events = true;
|
||||
ret = parse_probe_event(buf);
|
||||
free(buf);
|
||||
return ret;
|
||||
|
@ -100,9 +104,10 @@ static int parse_probe_event_argv(int argc, const char **argv)
|
|||
static int opt_add_probe_event(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
if (str)
|
||||
if (str) {
|
||||
params.mod_events = true;
|
||||
return parse_probe_event(str);
|
||||
else
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -110,6 +115,7 @@ static int opt_del_probe_event(const struct option *opt __used,
|
|||
const char *str, int unset __used)
|
||||
{
|
||||
if (str) {
|
||||
params.mod_events = true;
|
||||
if (!params.dellist)
|
||||
params.dellist = strlist__new(true, NULL);
|
||||
strlist__add(params.dellist, str);
|
||||
|
@ -130,6 +136,25 @@ static int opt_show_lines(const struct option *opt __used,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int opt_show_vars(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
struct perf_probe_event *pev = ¶ms.events[params.nevents];
|
||||
int ret;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
ret = parse_probe_event(str);
|
||||
if (!ret && pev->nargs != 0) {
|
||||
pr_err(" Error: '--vars' doesn't accept arguments.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
params.show_vars = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char * const probe_usage[] = {
|
||||
|
@ -138,7 +163,8 @@ static const char * const probe_usage[] = {
|
|||
"perf probe [<options>] --del '[GROUP:]EVENT' ...",
|
||||
"perf probe --list",
|
||||
#ifdef DWARF_SUPPORT
|
||||
"perf probe --line 'LINEDESC'",
|
||||
"perf probe [<options>] --line 'LINEDESC'",
|
||||
"perf probe [<options>] --vars 'PROBEPOINT'",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
@ -180,10 +206,17 @@ static const struct option options[] = {
|
|||
OPT_CALLBACK('L', "line", NULL,
|
||||
"FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
|
||||
"Show source code lines.", opt_show_lines),
|
||||
OPT_CALLBACK('V', "vars", NULL,
|
||||
"FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
|
||||
"Show accessible variables on PROBEDEF", opt_show_vars),
|
||||
OPT_BOOLEAN('\0', "externs", ¶ms.show_ext_vars,
|
||||
"Show external variables too (with --vars only)"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_STRING('s', "source", &symbol_conf.source_prefix,
|
||||
"directory", "path to kernel source"),
|
||||
OPT_STRING('m', "module", ¶ms.target_module,
|
||||
"modname", "target module name"),
|
||||
#endif
|
||||
OPT__DRY_RUN(&probe_event_dry_run),
|
||||
OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points,
|
||||
|
@ -217,7 +250,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
|||
usage_with_options(probe_usage, options);
|
||||
|
||||
if (params.list_events) {
|
||||
if (params.nevents != 0 || params.dellist) {
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --list with --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
|
@ -225,6 +258,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
|||
pr_err(" Error: Don't use --list with --line.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
if (params.show_vars) {
|
||||
pr_err(" Error: Don't use --list with --vars.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
ret = show_perf_probe_events();
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show event list. (%d)\n",
|
||||
|
@ -234,17 +271,35 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
|||
|
||||
#ifdef DWARF_SUPPORT
|
||||
if (params.show_lines) {
|
||||
if (params.nevents != 0 || params.dellist) {
|
||||
pr_warning(" Error: Don't use --line with"
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --line with"
|
||||
" --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
if (params.show_vars) {
|
||||
pr_err(" Error: Don't use --line with --vars.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
|
||||
ret = show_line_range(¶ms.line_range);
|
||||
ret = show_line_range(¶ms.line_range, params.target_module);
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show lines. (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (params.show_vars) {
|
||||
if (params.mod_events) {
|
||||
pr_err(" Error: Don't use --vars with"
|
||||
" --add/--del.\n");
|
||||
usage_with_options(probe_usage, options);
|
||||
}
|
||||
ret = show_available_vars(params.events, params.nevents,
|
||||
params.max_probe_points,
|
||||
params.target_module,
|
||||
params.show_ext_vars);
|
||||
if (ret < 0)
|
||||
pr_err(" Error: Failed to show vars. (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (params.dellist) {
|
||||
|
@ -258,8 +313,9 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
|
|||
|
||||
if (params.nevents) {
|
||||
ret = add_perf_probe_events(params.events, params.nevents,
|
||||
params.force_add,
|
||||
params.max_probe_points);
|
||||
params.max_probe_points,
|
||||
params.target_module,
|
||||
params.force_add);
|
||||
if (ret < 0) {
|
||||
pr_err(" Error: Failed to add events. (%d)\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -215,6 +215,16 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *self,
|
|||
return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_function_by_name(struct machine *self,
|
||||
const char *name,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
return map_groups__find_function_by_name(&self->kmaps, name, mapp,
|
||||
filter);
|
||||
}
|
||||
|
||||
int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
|
||||
int verbose, FILE *fp);
|
||||
|
||||
|
|
|
@ -74,10 +74,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
|
|||
static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
|
||||
static struct machine machine;
|
||||
|
||||
/* Initialize symbol maps and path of vmlinux */
|
||||
/* Initialize symbol maps and path of vmlinux/modules */
|
||||
static int init_vmlinux(void)
|
||||
{
|
||||
struct dso *kernel;
|
||||
int ret;
|
||||
|
||||
symbol_conf.sort_by_name = true;
|
||||
|
@ -91,33 +90,61 @@ static int init_vmlinux(void)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = machine__init(&machine, "/", 0);
|
||||
ret = machine__init(&machine, "", HOST_KERNEL_ID);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
kernel = dso__new_kernel(symbol_conf.vmlinux_name);
|
||||
if (kernel == NULL)
|
||||
die("Failed to create kernel dso.");
|
||||
|
||||
ret = __machine__create_kernel_maps(&machine, kernel);
|
||||
if (ret < 0)
|
||||
pr_debug("Failed to create kernel maps.\n");
|
||||
|
||||
if (machine__create_kernel_maps(&machine) < 0) {
|
||||
pr_debug("machine__create_kernel_maps ");
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
if (ret < 0)
|
||||
pr_warning("Failed to init vmlinux path.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef DWARF_SUPPORT
|
||||
static int open_vmlinux(void)
|
||||
static struct symbol *__find_kernel_function_by_name(const char *name,
|
||||
struct map **mapp)
|
||||
{
|
||||
if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
|
||||
pr_debug("Failed to load kernel map.\n");
|
||||
return -EINVAL;
|
||||
return machine__find_kernel_function_by_name(&machine, name, mapp,
|
||||
NULL);
|
||||
}
|
||||
pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name);
|
||||
return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
|
||||
|
||||
const char *kernel_get_module_path(const char *module)
|
||||
{
|
||||
struct dso *dso;
|
||||
|
||||
if (module) {
|
||||
list_for_each_entry(dso, &machine.kernel_dsos, node) {
|
||||
if (strncmp(dso->short_name + 1, module,
|
||||
dso->short_name_len - 2) == 0)
|
||||
goto found;
|
||||
}
|
||||
pr_debug("Failed to find module %s.\n", module);
|
||||
return NULL;
|
||||
} else {
|
||||
dso = machine.vmlinux_maps[MAP__FUNCTION]->dso;
|
||||
if (dso__load_vmlinux_path(dso,
|
||||
machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) {
|
||||
pr_debug("Failed to load kernel map.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
found:
|
||||
return dso->long_name;
|
||||
}
|
||||
|
||||
#ifdef DWARF_SUPPORT
|
||||
static int open_vmlinux(const char *module)
|
||||
{
|
||||
const char *path = kernel_get_module_path(module);
|
||||
if (!path) {
|
||||
pr_err("Failed to find path of %s module", module ?: "kernel");
|
||||
return -ENOENT;
|
||||
}
|
||||
pr_debug("Try to open %s\n", path);
|
||||
return open(path, O_RDONLY);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -128,17 +155,16 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|||
struct perf_probe_point *pp)
|
||||
{
|
||||
struct symbol *sym;
|
||||
int fd, ret = -ENOENT;
|
||||
struct map *map;
|
||||
u64 addr;
|
||||
int ret = -ENOENT;
|
||||
|
||||
sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
|
||||
tp->symbol, NULL);
|
||||
sym = __find_kernel_function_by_name(tp->symbol, &map);
|
||||
if (sym) {
|
||||
fd = open_vmlinux();
|
||||
if (fd >= 0) {
|
||||
ret = find_perf_probe_point(fd,
|
||||
sym->start + tp->offset, pp);
|
||||
close(fd);
|
||||
}
|
||||
addr = map->unmap_ip(map, sym->start + tp->offset);
|
||||
pr_debug("try to find %s+%ld@%llx\n", tp->symbol,
|
||||
tp->offset, addr);
|
||||
ret = find_perf_probe_point((unsigned long)addr, pp);
|
||||
}
|
||||
if (ret <= 0) {
|
||||
pr_debug("Failed to find corresponding probes from "
|
||||
|
@ -156,12 +182,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|||
/* 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)
|
||||
int max_tevs, const char *module)
|
||||
{
|
||||
bool need_dwarf = perf_probe_event_need_dwarf(pev);
|
||||
int fd, ntevs;
|
||||
|
||||
fd = open_vmlinux();
|
||||
fd = open_vmlinux(module);
|
||||
if (fd < 0) {
|
||||
if (need_dwarf) {
|
||||
pr_warning("Failed to open debuginfo file.\n");
|
||||
|
@ -300,7 +326,7 @@ error:
|
|||
* Show line-range always requires debuginfo to find source file and
|
||||
* line number.
|
||||
*/
|
||||
int show_line_range(struct line_range *lr)
|
||||
int show_line_range(struct line_range *lr, const char *module)
|
||||
{
|
||||
int l = 1;
|
||||
struct line_node *ln;
|
||||
|
@ -313,7 +339,7 @@ int show_line_range(struct line_range *lr)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fd = open_vmlinux();
|
||||
fd = open_vmlinux(module);
|
||||
if (fd < 0) {
|
||||
pr_warning("Failed to open debuginfo file.\n");
|
||||
return fd;
|
||||
|
@ -378,11 +404,84 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int show_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||
int max_vls, bool externs)
|
||||
{
|
||||
char *buf;
|
||||
int ret, i;
|
||||
struct str_node *node;
|
||||
struct variable_list *vls = NULL, *vl;
|
||||
|
||||
buf = synthesize_perf_probe_point(&pev->point);
|
||||
if (!buf)
|
||||
return -EINVAL;
|
||||
pr_debug("Searching variables at %s\n", buf);
|
||||
|
||||
ret = find_available_vars_at(fd, pev, &vls, max_vls, externs);
|
||||
if (ret > 0) {
|
||||
/* Some variables were found */
|
||||
fprintf(stdout, "Available variables at %s\n", buf);
|
||||
for (i = 0; i < ret; i++) {
|
||||
vl = &vls[i];
|
||||
/*
|
||||
* A probe point might be converted to
|
||||
* several trace points.
|
||||
*/
|
||||
fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol,
|
||||
vl->point.offset);
|
||||
free(vl->point.symbol);
|
||||
if (vl->vars) {
|
||||
strlist__for_each(node, vl->vars)
|
||||
fprintf(stdout, "\t\t%s\n", node->s);
|
||||
strlist__delete(vl->vars);
|
||||
} else
|
||||
fprintf(stdout, "(No variables)\n");
|
||||
}
|
||||
free(vls);
|
||||
} else
|
||||
pr_err("Failed to find variables at %s (%d)\n", buf, ret);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Show available variables on given probe point */
|
||||
int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
int max_vls, const char *module, bool externs)
|
||||
{
|
||||
int i, fd, ret = 0;
|
||||
|
||||
ret = init_vmlinux();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
fd = open_vmlinux(module);
|
||||
if (fd < 0) {
|
||||
pr_warning("Failed to open debuginfo file.\n");
|
||||
return fd;
|
||||
}
|
||||
|
||||
setup_pager();
|
||||
|
||||
for (i = 0; i < npevs && ret >= 0; i++)
|
||||
ret = show_available_vars_at(fd, &pevs[i], max_vls, externs);
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* !DWARF_SUPPORT */
|
||||
|
||||
static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
||||
struct perf_probe_point *pp)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
sym = __find_kernel_function_by_name(tp->symbol, NULL);
|
||||
if (!sym) {
|
||||
pr_err("Failed to find symbol %s in kernel.\n", tp->symbol);
|
||||
return -ENOENT;
|
||||
}
|
||||
pp->function = strdup(tp->symbol);
|
||||
if (pp->function == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -394,7 +493,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
|
|||
|
||||
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs __unused,
|
||||
int max_tevs __unused)
|
||||
int max_tevs __unused, const char *mod __unused)
|
||||
{
|
||||
if (perf_probe_event_need_dwarf(pev)) {
|
||||
pr_warning("Debuginfo-analysis is not supported.\n");
|
||||
|
@ -403,12 +502,19 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int show_line_range(struct line_range *lr __unused)
|
||||
int show_line_range(struct line_range *lr __unused, const char *module __unused)
|
||||
{
|
||||
pr_warning("Debuginfo-analysis is not supported.\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int show_available_vars(struct perf_probe_event *pevs __unused,
|
||||
int npevs __unused, int max_vls __unused,
|
||||
const char *module __unused, bool externs __unused)
|
||||
{
|
||||
pr_warning("Debuginfo-analysis is not supported.\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
int parse_line_range_desc(const char *arg, struct line_range *lr)
|
||||
|
@ -1516,14 +1622,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
|
|||
|
||||
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs,
|
||||
int max_tevs)
|
||||
int max_tevs, const char *module)
|
||||
{
|
||||
struct symbol *sym;
|
||||
int ret = 0, i;
|
||||
struct probe_trace_event *tev;
|
||||
|
||||
/* Convert perf_probe_event with debuginfo */
|
||||
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs);
|
||||
ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1572,8 +1678,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
|
|||
}
|
||||
|
||||
/* Currently just checking function name from symbol map */
|
||||
sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
|
||||
tev->point.symbol, NULL);
|
||||
sym = __find_kernel_function_by_name(tev->point.symbol, NULL);
|
||||
if (!sym) {
|
||||
pr_warning("Kernel symbol \'%s\' not found.\n",
|
||||
tev->point.symbol);
|
||||
|
@ -1596,7 +1701,7 @@ struct __event_package {
|
|||
};
|
||||
|
||||
int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
||||
bool force_add, int max_tevs)
|
||||
int max_tevs, const char *module, bool force_add)
|
||||
{
|
||||
int i, j, ret;
|
||||
struct __event_package *pkgs;
|
||||
|
@ -1617,7 +1722,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
|||
pkgs[i].pev = &pevs[i];
|
||||
/* Convert with or without debuginfo */
|
||||
ret = convert_to_probe_trace_events(pkgs[i].pev,
|
||||
&pkgs[i].tevs, max_tevs);
|
||||
&pkgs[i].tevs,
|
||||
max_tevs,
|
||||
module);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
pkgs[i].ntevs = ret;
|
||||
|
|
|
@ -90,6 +90,12 @@ struct line_range {
|
|||
struct list_head line_list; /* Visible lines */
|
||||
};
|
||||
|
||||
/* List of variables */
|
||||
struct variable_list {
|
||||
struct probe_trace_point point; /* Actual probepoint */
|
||||
struct strlist *vars; /* Available variables */
|
||||
};
|
||||
|
||||
/* Command string to events */
|
||||
extern int parse_perf_probe_command(const char *cmd,
|
||||
struct perf_probe_event *pev);
|
||||
|
@ -109,12 +115,18 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
|
|||
/* Command string to line-range */
|
||||
extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
|
||||
|
||||
/* Internal use: Return kernel/module path */
|
||||
extern const char *kernel_get_module_path(const char *module);
|
||||
|
||||
extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
|
||||
bool force_add, int max_probe_points);
|
||||
int max_probe_points, const char *module,
|
||||
bool force_add);
|
||||
extern int del_perf_probe_events(struct strlist *dellist);
|
||||
extern int show_perf_probe_events(void);
|
||||
extern int show_line_range(struct line_range *lr);
|
||||
extern int show_line_range(struct line_range *lr, const char *module);
|
||||
extern int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
int max_probe_points, const char *module,
|
||||
bool externs);
|
||||
|
||||
|
||||
/* Maximum index number of event-name postfix */
|
||||
|
|
|
@ -116,6 +116,101 @@ static void line_list__free(struct list_head *head)
|
|||
}
|
||||
}
|
||||
|
||||
/* Dwarf FL wrappers */
|
||||
|
||||
static int __linux_kernel_find_elf(Dwfl_Module *mod,
|
||||
void **userdata,
|
||||
const char *module_name,
|
||||
Dwarf_Addr base,
|
||||
char **file_name, Elf **elfp)
|
||||
{
|
||||
int fd;
|
||||
const char *path = kernel_get_module_path(module_name);
|
||||
|
||||
if (path) {
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
*file_name = strdup(path);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
/* If failed, try to call standard method */
|
||||
return dwfl_linux_kernel_find_elf(mod, userdata, module_name, base,
|
||||
file_name, elfp);
|
||||
}
|
||||
|
||||
static char *debuginfo_path; /* Currently dummy */
|
||||
|
||||
static const Dwfl_Callbacks offline_callbacks = {
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
.debuginfo_path = &debuginfo_path,
|
||||
|
||||
.section_address = dwfl_offline_section_address,
|
||||
|
||||
/* We use this table for core files too. */
|
||||
.find_elf = dwfl_build_id_find_elf,
|
||||
};
|
||||
|
||||
static const Dwfl_Callbacks kernel_callbacks = {
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
.debuginfo_path = &debuginfo_path,
|
||||
|
||||
.find_elf = __linux_kernel_find_elf,
|
||||
.section_address = dwfl_linux_kernel_module_section_address,
|
||||
};
|
||||
|
||||
/* Get a Dwarf from offline image */
|
||||
static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias)
|
||||
{
|
||||
Dwfl_Module *mod;
|
||||
Dwarf *dbg = NULL;
|
||||
|
||||
if (!dwflp)
|
||||
return NULL;
|
||||
|
||||
*dwflp = dwfl_begin(&offline_callbacks);
|
||||
if (!*dwflp)
|
||||
return NULL;
|
||||
|
||||
mod = dwfl_report_offline(*dwflp, "", "", fd);
|
||||
if (!mod)
|
||||
goto error;
|
||||
|
||||
dbg = dwfl_module_getdwarf(mod, bias);
|
||||
if (!dbg) {
|
||||
error:
|
||||
dwfl_end(*dwflp);
|
||||
*dwflp = NULL;
|
||||
}
|
||||
return dbg;
|
||||
}
|
||||
|
||||
/* Get a Dwarf from live kernel image */
|
||||
static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp,
|
||||
Dwarf_Addr *bias)
|
||||
{
|
||||
Dwarf *dbg;
|
||||
|
||||
if (!dwflp)
|
||||
return NULL;
|
||||
|
||||
*dwflp = dwfl_begin(&kernel_callbacks);
|
||||
if (!*dwflp)
|
||||
return NULL;
|
||||
|
||||
/* Load the kernel dwarves: Don't care the result here */
|
||||
dwfl_linux_kernel_report_kernel(*dwflp);
|
||||
dwfl_linux_kernel_report_modules(*dwflp);
|
||||
|
||||
dbg = dwfl_addrdwarf(*dwflp, addr, bias);
|
||||
/* Here, check whether we could get a real dwarf */
|
||||
if (!dbg) {
|
||||
dwfl_end(*dwflp);
|
||||
*dwflp = NULL;
|
||||
}
|
||||
return dbg;
|
||||
}
|
||||
|
||||
/* Dwarf wrappers */
|
||||
|
||||
/* Find the realpath of the target file. */
|
||||
|
@ -160,26 +255,44 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
|
|||
return name ? (strcmp(tname, name) == 0) : false;
|
||||
}
|
||||
|
||||
/* Get type die, but skip qualifiers and typedef */
|
||||
static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL ||
|
||||
dwarf_formref_die(&attr, die_mem) == NULL)
|
||||
return NULL;
|
||||
|
||||
tag = dwarf_tag(die_mem);
|
||||
vr_die = die_mem;
|
||||
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 ||
|
||||
tag == DW_TAG_typedef);
|
||||
tag == DW_TAG_shared_type);
|
||||
|
||||
return die_mem;
|
||||
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 bool die_is_signed_type(Dwarf_Die *tp_die)
|
||||
|
@ -320,25 +433,35 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
|
|||
return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem);
|
||||
}
|
||||
|
||||
struct __find_variable_param {
|
||||
const char *name;
|
||||
Dwarf_Addr addr;
|
||||
};
|
||||
|
||||
static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
|
||||
{
|
||||
const char *name = 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, name))
|
||||
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' */
|
||||
static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
|
||||
Dwarf_Die *die_mem)
|
||||
/* 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)
|
||||
{
|
||||
return die_find_child(sp_die, __die_find_variable_cb, (void *)name,
|
||||
struct __find_variable_param fvp = { .name = name, .addr = addr};
|
||||
|
||||
return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp,
|
||||
die_mem);
|
||||
}
|
||||
|
||||
|
@ -361,6 +484,60 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe finder related functions
|
||||
*/
|
||||
|
@ -374,8 +551,13 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
|
|||
return ref;
|
||||
}
|
||||
|
||||
/* Show a location */
|
||||
static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
||||
/*
|
||||
* Convert a location into trace_arg.
|
||||
* If tvar == NULL, this just checks variable can be converted.
|
||||
*/
|
||||
static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
|
||||
Dwarf_Op *fb_ops,
|
||||
struct probe_trace_arg *tvar)
|
||||
{
|
||||
Dwarf_Attribute attr;
|
||||
Dwarf_Op *op;
|
||||
|
@ -384,20 +566,23 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|||
Dwarf_Word offs = 0;
|
||||
bool ref = false;
|
||||
const char *regs;
|
||||
struct probe_trace_arg *tvar = pf->tvar;
|
||||
int ret;
|
||||
|
||||
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
|
||||
goto static_var;
|
||||
|
||||
/* TODO: handle more than 1 exprs */
|
||||
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
|
||||
dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
|
||||
dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
|
||||
nops == 0) {
|
||||
/* TODO: Support const_value */
|
||||
pr_err("Failed to find the location of %s at this address.\n"
|
||||
" Perhaps, it has been optimized out.\n", pf->pvar->var);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (op->atom == DW_OP_addr) {
|
||||
static_var:
|
||||
if (!tvar)
|
||||
return 0;
|
||||
/* Static variables on memory (not stack), make @varname */
|
||||
ret = strlen(dwarf_diename(vr_die));
|
||||
tvar->value = zalloc(ret + 2);
|
||||
|
@ -412,14 +597,11 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|||
|
||||
/* If this is based on frame buffer, set the offset */
|
||||
if (op->atom == DW_OP_fbreg) {
|
||||
if (pf->fb_ops == NULL) {
|
||||
pr_warning("The attribute of frame base is not "
|
||||
"supported.\n");
|
||||
if (fb_ops == NULL)
|
||||
return -ENOTSUP;
|
||||
}
|
||||
ref = true;
|
||||
offs = op->number;
|
||||
op = &pf->fb_ops[0];
|
||||
op = &fb_ops[0];
|
||||
}
|
||||
|
||||
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
||||
|
@ -435,13 +617,18 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|||
} else if (op->atom == DW_OP_regx) {
|
||||
regn = op->number;
|
||||
} else {
|
||||
pr_warning("DW_OP %x is not supported.\n", op->atom);
|
||||
pr_debug("DW_OP %x is not supported.\n", op->atom);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (!tvar)
|
||||
return 0;
|
||||
|
||||
regs = get_arch_regstr(regn);
|
||||
if (!regs) {
|
||||
pr_warning("Mapping for DWARF register number %u missing on this architecture.", regn);
|
||||
/* This should be a bug in DWARF or this tool */
|
||||
pr_warning("Mapping for DWARF register number %u "
|
||||
"missing on this architecture.", regn);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
|
@ -666,8 +853,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
|
|||
pr_debug("Converting variable %s into trace event.\n",
|
||||
dwarf_diename(vr_die));
|
||||
|
||||
ret = convert_variable_location(vr_die, pf);
|
||||
if (ret == 0 && pf->pvar->field) {
|
||||
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
|
||||
pf->tvar);
|
||||
if (ret == -ENOENT)
|
||||
pr_err("Failed to find the location of %s at this address.\n"
|
||||
" Perhaps, it has been optimized out.\n", pf->pvar->var);
|
||||
else if (ret == -ENOTSUP)
|
||||
pr_err("Sorry, we don't support this variable location yet.\n");
|
||||
else if (pf->pvar->field) {
|
||||
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
||||
pf->pvar->field, &pf->tvar->ref,
|
||||
&die_mem);
|
||||
|
@ -722,45 +915,76 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|||
pr_debug("Searching '%s' variable in context.\n",
|
||||
pf->pvar->var);
|
||||
/* Search child die for local variables and parameters. */
|
||||
if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
|
||||
if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die))
|
||||
ret = convert_variable(&vr_die, pf);
|
||||
else {
|
||||
/* Search upper class */
|
||||
nscopes = dwarf_getscopes_die(sp_die, &scopes);
|
||||
if (nscopes > 0) {
|
||||
ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
|
||||
0, NULL, 0, 0, &vr_die);
|
||||
if (ret >= 0)
|
||||
while (nscopes-- > 1) {
|
||||
pr_debug("Searching variables in %s\n",
|
||||
dwarf_diename(&scopes[nscopes]));
|
||||
/* We should check this scope, so give dummy address */
|
||||
if (die_find_variable_at(&scopes[nscopes],
|
||||
pf->pvar->var, 0,
|
||||
&vr_die)) {
|
||||
ret = convert_variable(&vr_die, pf);
|
||||
else
|
||||
ret = -ENOENT;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
if (scopes)
|
||||
free(scopes);
|
||||
} else
|
||||
ret = -ENOENT;
|
||||
}
|
||||
found:
|
||||
if (ret < 0)
|
||||
pr_warning("Failed to find '%s' in this function.\n",
|
||||
pf->pvar->var);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Show a probe point to output buffer */
|
||||
static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
/* Convert subprogram DIE to trace point */
|
||||
static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr,
|
||||
bool retprobe, struct probe_trace_point *tp)
|
||||
{
|
||||
struct probe_trace_event *tev;
|
||||
Dwarf_Addr eaddr;
|
||||
Dwarf_Die die_mem;
|
||||
const char *name;
|
||||
int ret, i;
|
||||
|
||||
/* Copy the name of probe point */
|
||||
name = dwarf_diename(sp_die);
|
||||
if (name) {
|
||||
if (dwarf_entrypc(sp_die, &eaddr) != 0) {
|
||||
pr_warning("Failed to get entry pc of %s\n",
|
||||
dwarf_diename(sp_die));
|
||||
return -ENOENT;
|
||||
}
|
||||
tp->symbol = strdup(name);
|
||||
if (tp->symbol == NULL)
|
||||
return -ENOMEM;
|
||||
tp->offset = (unsigned long)(paddr - eaddr);
|
||||
} else
|
||||
/* This function has no name. */
|
||||
tp->offset = (unsigned long)paddr;
|
||||
|
||||
/* Return probe must be on the head of a subprogram */
|
||||
if (retprobe) {
|
||||
if (eaddr != paddr) {
|
||||
pr_warning("Return probe must be on the head of"
|
||||
" a real function\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
tp->retprobe = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call probe_finder callback with real subprogram DIE */
|
||||
static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
{
|
||||
Dwarf_Die die_mem;
|
||||
Dwarf_Attribute fb_attr;
|
||||
size_t nops;
|
||||
|
||||
if (pf->ntevs == pf->max_tevs) {
|
||||
pr_warning("Too many( > %d) probe point found.\n",
|
||||
pf->max_tevs);
|
||||
return -ERANGE;
|
||||
}
|
||||
tev = &pf->tevs[pf->ntevs++];
|
||||
int ret;
|
||||
|
||||
/* If no real subprogram, find a real one */
|
||||
if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) {
|
||||
|
@ -773,35 +997,6 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|||
}
|
||||
}
|
||||
|
||||
/* Copy the name of probe point */
|
||||
name = dwarf_diename(sp_die);
|
||||
if (name) {
|
||||
if (dwarf_entrypc(sp_die, &eaddr) != 0) {
|
||||
pr_warning("Failed to get entry pc of %s\n",
|
||||
dwarf_diename(sp_die));
|
||||
return -ENOENT;
|
||||
}
|
||||
tev->point.symbol = strdup(name);
|
||||
if (tev->point.symbol == NULL)
|
||||
return -ENOMEM;
|
||||
tev->point.offset = (unsigned long)(pf->addr - eaddr);
|
||||
} else
|
||||
/* This function has no name. */
|
||||
tev->point.offset = (unsigned long)pf->addr;
|
||||
|
||||
/* Return probe must be on the head of a subprogram */
|
||||
if (pf->pev->point.retprobe) {
|
||||
if (tev->point.offset != 0) {
|
||||
pr_warning("Return probe must be on the head of"
|
||||
" a real function\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
tev->point.retprobe = true;
|
||||
}
|
||||
|
||||
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
||||
tev->point.offset);
|
||||
|
||||
/* Get the frame base attribute/ops */
|
||||
dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr);
|
||||
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
|
||||
|
@ -820,22 +1015,13 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Find each argument */
|
||||
tev->nargs = pf->pev->nargs;
|
||||
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
||||
if (tev->args == NULL)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < pf->pev->nargs; i++) {
|
||||
pf->pvar = &pf->pev->args[i];
|
||||
pf->tvar = &tev->args[i];
|
||||
ret = find_variable(sp_die, pf);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
/* Call finder's callback handler */
|
||||
ret = pf->callback(sp_die, pf);
|
||||
|
||||
/* *pf->fb_ops will be cached in libdw. Don't free it. */
|
||||
pf->fb_ops = NULL;
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find probe point from its line number */
|
||||
|
@ -871,7 +1057,7 @@ static int find_probe_point_by_line(struct probe_finder *pf)
|
|||
(int)i, lineno, (uintmax_t)addr);
|
||||
pf->addr = addr;
|
||||
|
||||
ret = convert_probe_point(NULL, pf);
|
||||
ret = call_probe_finder(NULL, pf);
|
||||
/* Continuing, because target line might be inlined. */
|
||||
}
|
||||
return ret;
|
||||
|
@ -984,7 +1170,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|||
(int)i, lineno, (unsigned long long)addr);
|
||||
pf->addr = addr;
|
||||
|
||||
ret = convert_probe_point(sp_die, pf);
|
||||
ret = call_probe_finder(sp_die, pf);
|
||||
/* Continuing, because target line might be inlined. */
|
||||
}
|
||||
/* TODO: deallocate lines, but how? */
|
||||
|
@ -1019,7 +1205,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
|
|||
pr_debug("found inline addr: 0x%jx\n",
|
||||
(uintmax_t)pf->addr);
|
||||
|
||||
param->retval = convert_probe_point(in_die, pf);
|
||||
param->retval = call_probe_finder(in_die, pf);
|
||||
if (param->retval < 0)
|
||||
return DWARF_CB_ABORT;
|
||||
}
|
||||
|
@ -1057,7 +1243,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
|||
}
|
||||
pf->addr += pp->offset;
|
||||
/* TODO: Check the address in this function */
|
||||
param->retval = convert_probe_point(sp_die, pf);
|
||||
param->retval = call_probe_finder(sp_die, pf);
|
||||
}
|
||||
} else {
|
||||
struct dwarf_callback_param _param = {.data = (void *)pf,
|
||||
|
@ -1079,90 +1265,276 @@ static int find_probe_point_by_func(struct probe_finder *pf)
|
|||
return _param.retval;
|
||||
}
|
||||
|
||||
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
||||
int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs, int max_tevs)
|
||||
/* Find probe points from debuginfo */
|
||||
static int find_probes(int fd, struct probe_finder *pf)
|
||||
{
|
||||
struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
|
||||
struct perf_probe_point *pp = &pev->point;
|
||||
struct perf_probe_point *pp = &pf->pev->point;
|
||||
Dwarf_Off off, noff;
|
||||
size_t cuhl;
|
||||
Dwarf_Die *diep;
|
||||
Dwarf *dbg;
|
||||
Dwarf *dbg = NULL;
|
||||
Dwfl *dwfl;
|
||||
Dwarf_Addr bias; /* Currently ignored */
|
||||
int ret = 0;
|
||||
|
||||
pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
||||
if (pf.tevs == NULL)
|
||||
return -ENOMEM;
|
||||
*tevs = pf.tevs;
|
||||
pf.ntevs = 0;
|
||||
|
||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
||||
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
||||
if (!dbg) {
|
||||
pr_warning("No dwarf info found in the vmlinux - "
|
||||
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
||||
free(pf.tevs);
|
||||
*tevs = NULL;
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
#if _ELFUTILS_PREREQ(0, 142)
|
||||
/* Get the call frame information from this dwarf */
|
||||
pf.cfi = dwarf_getcfi(dbg);
|
||||
pf->cfi = dwarf_getcfi(dbg);
|
||||
#endif
|
||||
|
||||
off = 0;
|
||||
line_list__init(&pf.lcache);
|
||||
line_list__init(&pf->lcache);
|
||||
/* Loop on CUs (Compilation Unit) */
|
||||
while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) &&
|
||||
ret >= 0) {
|
||||
/* Get the DIE(Debugging Information Entry) of this CU */
|
||||
diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die);
|
||||
diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die);
|
||||
if (!diep)
|
||||
continue;
|
||||
|
||||
/* Check if target file is included. */
|
||||
if (pp->file)
|
||||
pf.fname = cu_find_realpath(&pf.cu_die, pp->file);
|
||||
pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
|
||||
else
|
||||
pf.fname = NULL;
|
||||
pf->fname = NULL;
|
||||
|
||||
if (!pp->file || pf.fname) {
|
||||
if (!pp->file || pf->fname) {
|
||||
if (pp->function)
|
||||
ret = find_probe_point_by_func(&pf);
|
||||
ret = find_probe_point_by_func(pf);
|
||||
else if (pp->lazy_line)
|
||||
ret = find_probe_point_lazy(NULL, &pf);
|
||||
ret = find_probe_point_lazy(NULL, pf);
|
||||
else {
|
||||
pf.lno = pp->line;
|
||||
ret = find_probe_point_by_line(&pf);
|
||||
pf->lno = pp->line;
|
||||
ret = find_probe_point_by_line(pf);
|
||||
}
|
||||
}
|
||||
off = noff;
|
||||
}
|
||||
line_list__free(&pf.lcache);
|
||||
dwarf_end(dbg);
|
||||
line_list__free(&pf->lcache);
|
||||
if (dwfl)
|
||||
dwfl_end(dwfl);
|
||||
|
||||
return (ret < 0) ? ret : pf.ntevs;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Add a found probe point into trace event list */
|
||||
static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
{
|
||||
struct trace_event_finder *tf =
|
||||
container_of(pf, struct trace_event_finder, pf);
|
||||
struct probe_trace_event *tev;
|
||||
int ret, i;
|
||||
|
||||
/* Check number of tevs */
|
||||
if (tf->ntevs == tf->max_tevs) {
|
||||
pr_warning("Too many( > %d) probe point found.\n",
|
||||
tf->max_tevs);
|
||||
return -ERANGE;
|
||||
}
|
||||
tev = &tf->tevs[tf->ntevs++];
|
||||
|
||||
ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
||||
&tev->point);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
||||
tev->point.offset);
|
||||
|
||||
/* Find each argument */
|
||||
tev->nargs = pf->pev->nargs;
|
||||
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
||||
if (tev->args == NULL)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < pf->pev->nargs; i++) {
|
||||
pf->pvar = &pf->pev->args[i];
|
||||
pf->tvar = &tev->args[i];
|
||||
ret = find_variable(sp_die, pf);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
||||
int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
||||
struct probe_trace_event **tevs, int max_tevs)
|
||||
{
|
||||
struct trace_event_finder tf = {
|
||||
.pf = {.pev = pev, .callback = add_probe_trace_event},
|
||||
.max_tevs = max_tevs};
|
||||
int ret;
|
||||
|
||||
/* Allocate result tevs array */
|
||||
*tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
|
||||
if (*tevs == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tf.tevs = *tevs;
|
||||
tf.ntevs = 0;
|
||||
|
||||
ret = find_probes(fd, &tf.pf);
|
||||
if (ret < 0) {
|
||||
free(*tevs);
|
||||
*tevs = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (ret < 0) ? ret : tf.ntevs;
|
||||
}
|
||||
|
||||
#define MAX_VAR_LEN 64
|
||||
|
||||
/* Collect available variables in this scope */
|
||||
static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
|
||||
{
|
||||
struct available_var_finder *af = data;
|
||||
struct variable_list *vl;
|
||||
char buf[MAX_VAR_LEN];
|
||||
int tag, ret;
|
||||
|
||||
vl = &af->vls[af->nvls - 1];
|
||||
|
||||
tag = dwarf_tag(die_mem);
|
||||
if (tag == DW_TAG_formal_parameter ||
|
||||
tag == DW_TAG_variable) {
|
||||
ret = convert_variable_location(die_mem, af->pf.addr,
|
||||
af->pf.fb_ops, NULL);
|
||||
if (ret == 0) {
|
||||
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
|
||||
pr_debug2("Add new var: %s\n", buf);
|
||||
if (ret > 0)
|
||||
strlist__add(vl->vars, buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (af->child && dwarf_haspc(die_mem, af->pf.addr))
|
||||
return DIE_FIND_CB_CONTINUE;
|
||||
else
|
||||
return DIE_FIND_CB_SIBLING;
|
||||
}
|
||||
|
||||
/* Add a found vars into available variables list */
|
||||
static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf)
|
||||
{
|
||||
struct available_var_finder *af =
|
||||
container_of(pf, struct available_var_finder, pf);
|
||||
struct variable_list *vl;
|
||||
Dwarf_Die die_mem, *scopes = NULL;
|
||||
int ret, nscopes;
|
||||
|
||||
/* Check number of tevs */
|
||||
if (af->nvls == af->max_vls) {
|
||||
pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
|
||||
return -ERANGE;
|
||||
}
|
||||
vl = &af->vls[af->nvls++];
|
||||
|
||||
ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe,
|
||||
&vl->point);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
|
||||
vl->point.offset);
|
||||
|
||||
/* Find local variables */
|
||||
vl->vars = strlist__new(true, NULL);
|
||||
if (vl->vars == NULL)
|
||||
return -ENOMEM;
|
||||
af->child = true;
|
||||
die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem);
|
||||
|
||||
/* Find external variables */
|
||||
if (!af->externs)
|
||||
goto out;
|
||||
/* Don't need to search child DIE for externs. */
|
||||
af->child = false;
|
||||
nscopes = dwarf_getscopes_die(sp_die, &scopes);
|
||||
while (nscopes-- > 1)
|
||||
die_find_child(&scopes[nscopes], collect_variables_cb,
|
||||
(void *)af, &die_mem);
|
||||
if (scopes)
|
||||
free(scopes);
|
||||
|
||||
out:
|
||||
if (strlist__empty(vl->vars)) {
|
||||
strlist__delete(vl->vars);
|
||||
vl->vars = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find available variables at given probe point */
|
||||
int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||
struct variable_list **vls, int max_vls,
|
||||
bool externs)
|
||||
{
|
||||
struct available_var_finder af = {
|
||||
.pf = {.pev = pev, .callback = add_available_vars},
|
||||
.max_vls = max_vls, .externs = externs};
|
||||
int ret;
|
||||
|
||||
/* Allocate result vls array */
|
||||
*vls = zalloc(sizeof(struct variable_list) * max_vls);
|
||||
if (*vls == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
af.vls = *vls;
|
||||
af.nvls = 0;
|
||||
|
||||
ret = find_probes(fd, &af.pf);
|
||||
if (ret < 0) {
|
||||
/* Free vlist for error */
|
||||
while (af.nvls--) {
|
||||
if (af.vls[af.nvls].point.symbol)
|
||||
free(af.vls[af.nvls].point.symbol);
|
||||
if (af.vls[af.nvls].vars)
|
||||
strlist__delete(af.vls[af.nvls].vars);
|
||||
}
|
||||
free(af.vls);
|
||||
*vls = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (ret < 0) ? ret : af.nvls;
|
||||
}
|
||||
|
||||
/* Reverse search */
|
||||
int find_perf_probe_point(int fd, unsigned long addr,
|
||||
struct perf_probe_point *ppt)
|
||||
int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt)
|
||||
{
|
||||
Dwarf_Die cudie, spdie, indie;
|
||||
Dwarf *dbg;
|
||||
Dwarf *dbg = NULL;
|
||||
Dwfl *dwfl = NULL;
|
||||
Dwarf_Line *line;
|
||||
Dwarf_Addr laddr, eaddr;
|
||||
Dwarf_Addr laddr, eaddr, bias = 0;
|
||||
const char *tmp;
|
||||
int lineno, ret = 0;
|
||||
bool found = false;
|
||||
|
||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
||||
if (!dbg)
|
||||
return -EBADF;
|
||||
/* Open the live linux kernel */
|
||||
dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias);
|
||||
if (!dbg) {
|
||||
pr_warning("No dwarf info found in the vmlinux - "
|
||||
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Adjust address with bias */
|
||||
addr += bias;
|
||||
/* Find cu die */
|
||||
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) {
|
||||
if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) {
|
||||
pr_warning("No CU DIE is found at %lx\n", addr);
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
@ -1225,7 +1597,8 @@ found:
|
|||
}
|
||||
|
||||
end:
|
||||
dwarf_end(dbg);
|
||||
if (dwfl)
|
||||
dwfl_end(dwfl);
|
||||
if (ret >= 0)
|
||||
ret = found ? 1 : 0;
|
||||
return ret;
|
||||
|
@ -1358,6 +1731,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
|||
struct line_finder *lf = param->data;
|
||||
struct line_range *lr = lf->lr;
|
||||
|
||||
pr_debug("find (%lx) %s\n", dwarf_dieoffset(sp_die),
|
||||
dwarf_diename(sp_die));
|
||||
if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
|
||||
die_compare_name(sp_die, lr->function)) {
|
||||
lf->fname = dwarf_decl_file(sp_die);
|
||||
|
@ -1401,10 +1776,12 @@ int find_line_range(int fd, struct line_range *lr)
|
|||
Dwarf_Off off = 0, noff;
|
||||
size_t cuhl;
|
||||
Dwarf_Die *diep;
|
||||
Dwarf *dbg;
|
||||
Dwarf *dbg = NULL;
|
||||
Dwfl *dwfl;
|
||||
Dwarf_Addr bias; /* Currently ignored */
|
||||
const char *comp_dir;
|
||||
|
||||
dbg = dwarf_begin(fd, DWARF_C_READ);
|
||||
dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias);
|
||||
if (!dbg) {
|
||||
pr_warning("No dwarf info found in the vmlinux - "
|
||||
"please rebuild with CONFIG_DEBUG_INFO=y.\n");
|
||||
|
@ -1450,8 +1827,7 @@ int find_line_range(int fd, struct line_range *lr)
|
|||
}
|
||||
|
||||
pr_debug("path: %s\n", lr->path);
|
||||
dwarf_end(dbg);
|
||||
|
||||
dwfl_end(dwfl);
|
||||
return (ret < 0) ? ret : lf.found;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,20 +22,27 @@ extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
|
|||
int max_tevs);
|
||||
|
||||
/* Find a perf_probe_point from debuginfo */
|
||||
extern int find_perf_probe_point(int fd, unsigned long addr,
|
||||
extern int find_perf_probe_point(unsigned long addr,
|
||||
struct perf_probe_point *ppt);
|
||||
|
||||
/* Find a line range */
|
||||
extern int find_line_range(int fd, struct line_range *lr);
|
||||
|
||||
/* Find available variables */
|
||||
extern int find_available_vars_at(int fd, struct perf_probe_event *pev,
|
||||
struct variable_list **vls, int max_points,
|
||||
bool externs);
|
||||
|
||||
#include <dwarf.h>
|
||||
#include <libdw.h>
|
||||
#include <libdwfl.h>
|
||||
#include <version.h>
|
||||
|
||||
struct probe_finder {
|
||||
struct perf_probe_event *pev; /* Target probe event */
|
||||
struct probe_trace_event *tevs; /* Result trace events */
|
||||
int ntevs; /* Number of trace events */
|
||||
int max_tevs; /* Max number of trace events */
|
||||
|
||||
/* Callback when a probe point is found */
|
||||
int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf);
|
||||
|
||||
/* For function searching */
|
||||
int lno; /* Line number */
|
||||
|
@ -53,6 +60,22 @@ struct probe_finder {
|
|||
struct probe_trace_arg *tvar; /* Current result variable */
|
||||
};
|
||||
|
||||
struct trace_event_finder {
|
||||
struct probe_finder pf;
|
||||
struct probe_trace_event *tevs; /* Found trace events */
|
||||
int ntevs; /* Number of trace events */
|
||||
int max_tevs; /* Max number of trace events */
|
||||
};
|
||||
|
||||
struct available_var_finder {
|
||||
struct probe_finder pf;
|
||||
struct variable_list *vls; /* Found variable lists */
|
||||
int nvls; /* Number of variable lists */
|
||||
int max_vls; /* Max no. of variable lists */
|
||||
bool externs; /* Find external vars too */
|
||||
bool child; /* Search child scopes */
|
||||
};
|
||||
|
||||
struct line_finder {
|
||||
struct line_range *lr; /* Target line range */
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include <slang.h>
|
||||
#include "libslang.h"
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
|
|
Loading…
Reference in New Issue