Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 469b9b88 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Arnaldo Carvalho de Melo
Browse files

perf probe: Add basic module support



Add basic module probe support on perf probe. This introduces "--module
<MODNAME>" option to perf probe for putting probes and showing lines and
variables in the given module.

Currently, this supports only probing on running modules.  Supporting off-line
module probing is the next step.

e.g.)
[show lines]
 # ./perf probe --module drm -L drm_vblank_info
<drm_vblank_info:0>
      0  int drm_vblank_info(struct seq_file *m, void *data)
      1  {
                struct drm_info_node *node = (struct drm_info_node *) m->private
      3         struct drm_device *dev = node->minor->dev;
 ...
[show vars]
 # ./perf probe --module drm -V drm_vblank_info:3
Available variables at drm_vblank_info:3
        @<drm_vblank_info+20>
                (unknown_type)  data
                struct drm_info_node*   node
                struct seq_file*        m
[put a probe]
 # ./perf probe --module drm drm_vblank_info:3 node m
Add new event:
  probe:drm_vblank_info (on drm_vblank_info:3 with node m)

You can now use it on all perf tools, such as:

        perf record -e probe:drm_vblank_info -aR sleep 1
[list probes]
 # ./perf probe -l
probe:drm_vblank_info (on drm_vblank_info:3@drivers/gpu/drm/drm_info.c with ...

Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20101021101341.3542.71638.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: default avatarMasami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent fb8c5a56
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -16,9 +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' [--externs] --vars='PROBEPOINT'
'perf probe' [options] --vars='PROBEPOINT'

DESCRIPTION
-----------
@@ -33,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.
+8 −3
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ static struct {
	struct perf_probe_event events[MAX_PROBES];
	struct strlist *dellist;
	struct line_range line_range;
	const char *target_module;
	int max_probe_points;
} params;

@@ -162,8 +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 [--externs] --vars 'PROBEPOINT'",
	"perf probe [<options>] --line 'LINEDESC'",
	"perf probe [<options>] --vars 'PROBEPOINT'",
#endif
	NULL
};
@@ -214,6 +215,8 @@ static const struct option options[] = {
		   "file", "vmlinux pathname"),
	OPT_STRING('s', "source", &symbol_conf.source_prefix,
		   "directory", "path to kernel source"),
	OPT_STRING('m', "module", &params.target_module,
		   "modname", "target module name"),
#endif
	OPT__DRY_RUN(&probe_event_dry_run),
	OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
@@ -278,7 +281,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
			usage_with_options(probe_usage, options);
		}

		ret = show_line_range(&params.line_range);
		ret = show_line_range(&params.line_range, params.target_module);
		if (ret < 0)
			pr_err("  Error: Failed to show lines. (%d)\n", ret);
		return ret;
@@ -291,6 +294,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
		}
		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);
@@ -310,6 +314,7 @@ 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.max_probe_points,
					    params.target_module,
					    params.force_add);
		if (ret < 0) {
			pr_err("  Error: Failed to add events. (%d)\n", ret);
+10 −0
Original line number Diff line number Diff line
@@ -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);

+79 −44
Original line number Diff line number Diff line
@@ -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) {
	return machine__find_kernel_function_by_name(&machine, name, mapp,
						     NULL);
}

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 -EINVAL;
			return 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);
	}
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 @@ static int show_one_line(FILE *fp, int l, bool skip, bool show_num)
 * 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;
@@ -421,7 +447,7 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev,

/* Show available variables on given probe point */
int show_available_vars(struct perf_probe_event *pevs, int npevs,
			int max_vls, bool externs)
			int max_vls, const char *module, bool externs)
{
	int i, fd, ret = 0;

@@ -429,7 +455,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
	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;
@@ -449,6 +475,13 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
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;
@@ -460,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");
@@ -469,14 +502,15 @@ 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_probe_points __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;
@@ -1588,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;

@@ -1644,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);
@@ -1668,7 +1701,7 @@ struct __event_package {
};

int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
			  int max_tevs, bool force_add)
			  int max_tevs, const char *module, bool force_add)
{
	int i, j, ret;
	struct __event_package *pkgs;
@@ -1689,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;
+7 −3
Original line number Diff line number Diff line
@@ -115,14 +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,
				 int max_probe_points, bool force_add);
				 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, bool externs);
			       int max_probe_points, const char *module,
			       bool externs);


/* Maximum index number of event-name postfix */
Loading