diff options
Diffstat (limited to 'tools/perf/builtin-probe.c')
-rw-r--r-- | tools/perf/builtin-probe.c | 145 |
1 files changed, 82 insertions, 63 deletions
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 5a47c1e11f7..c1e6774fd3e 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -38,34 +38,30 @@ #include "util/strlist.h" #include "util/event.h" #include "util/debug.h" +#include "util/debugfs.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/session.h" #include "util/parse-options.h" #include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" #include "util/probe-event.h" -/* Default vmlinux search paths */ -#define NR_SEARCH_PATH 4 -const char *default_search_path[NR_SEARCH_PATH] = { -"/lib/modules/%s/build/vmlinux", /* Custom build kernel */ -"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */ -"/boot/vmlinux-debug-%s", /* Ubuntu */ -"./vmlinux", /* CWD */ -}; - #define MAX_PATH_LEN 256 #define MAX_PROBES 128 /* Session management structure */ static struct { - char *vmlinux; - char *release; - int need_dwarf; + bool need_dwarf; + bool list_events; + bool force_add; int nr_probe; struct probe_point probes[MAX_PROBES]; struct strlist *dellist; + struct perf_session *psession; + struct map *kmap; } session; -static bool listing; /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) @@ -77,7 +73,7 @@ static void parse_probe_event(const char *str) die("Too many probes (> %d) are specified.", MAX_PROBES); /* Parse perf-probe event into probe_point */ - session.need_dwarf = parse_perf_probe_event(str, pp); + parse_perf_probe_event(str, pp, &session.need_dwarf); pr_debug("%d arguments\n", pp->nr_args); } @@ -120,34 +116,26 @@ static int opt_del_probe_event(const struct option *opt __used, return 0; } +/* Currently just checking function name from symbol map */ +static void evaluate_probe_point(struct probe_point *pp) +{ + struct symbol *sym; + sym = map__find_symbol_by_name(session.kmap, pp->function, + session.psession, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + pp->function); +} + #ifndef NO_LIBDWARF -static int open_default_vmlinux(void) +static int open_vmlinux(void) { - struct utsname uts; - char fname[MAX_PATH_LEN]; - int fd, ret, i; - - ret = uname(&uts); - if (ret) { - pr_debug("uname() failed.\n"); - return -errno; - } - session.release = uts.release; - for (i = 0; i < NR_SEARCH_PATH; i++) { - ret = snprintf(fname, MAX_PATH_LEN, - default_search_path[i], session.release); - if (ret >= MAX_PATH_LEN || ret < 0) { - pr_debug("Filename(%d,%s) is too long.\n", i, - uts.release); - errno = E2BIG; - return -E2BIG; - } - pr_debug("try to open %s\n", fname); - fd = open(fname, O_RDONLY); - if (fd >= 0) - break; + if (map__load(session.kmap, session.psession, NULL) < 0) { + pr_debug("Failed to load kernel map.\n"); + return -EINVAL; } - return fd; + pr_debug("Try to open %s\n", session.kmap->dso->long_name); + return open(session.kmap->dso->long_name, O_RDONLY); } #endif @@ -163,21 +151,22 @@ static const struct option options[] = { OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), #ifndef NO_LIBDWARF - OPT_STRING('k', "vmlinux", &session.vmlinux, "file", - "vmlinux/module pathname"), + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), #endif - OPT_BOOLEAN('l', "list", &listing, "list up current probe events"), + OPT_BOOLEAN('l', "list", &session.list_events, + "list up current probe events"), OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", opt_del_probe_event), OPT_CALLBACK('a', "add", NULL, #ifdef NO_LIBDWARF - "FUNC[+OFFS|%return] [ARG ...]", + "[EVENT=]FUNC[+OFFS|%return] [ARG ...]", #else - "FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", + "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", #endif "probe point definition, where\n" - "\t\tGRP:\tGroup name (optional)\n" - "\t\tNAME:\tEvent name\n" + "\t\tGROUP:\tGroup name (optional)\n" + "\t\tEVENT:\tEvent name\n" "\t\tFUNC:\tFunction name\n" "\t\tOFFS:\tOffset from function entry (in byte)\n" "\t\t%return:\tPut the probe at function return\n" @@ -191,6 +180,8 @@ static const struct option options[] = { #endif "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), + OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" + " with existing name"), OPT_END() }; @@ -204,13 +195,21 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (argc > 0) + if (argc > 0) { + if (strcmp(argv[0], "-") == 0) { + pr_warning(" Error: '-' is not supported.\n"); + usage_with_options(probe_usage, options); + } parse_probe_event_argv(argc, argv); + } - if ((session.nr_probe == 0 && !session.dellist && !listing)) + if ((!session.nr_probe && !session.dellist && !session.list_events)) usage_with_options(probe_usage, options); - if (listing) { + if (debugfs_valid_mountpoint(debugfs_path) < 0) + die("Failed to find debugfs path."); + + if (session.list_events) { if (session.nr_probe != 0 || session.dellist) { pr_warning(" Error: Don't use --list with" " --add/--del.\n"); @@ -227,17 +226,28 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) return 0; } + /* Initialize symbol maps for vmlinux */ + symbol_conf.sort_by_name = true; + if (symbol_conf.vmlinux_name == NULL) + symbol_conf.try_vmlinux_path = true; + if (symbol__init() < 0) + die("Failed to init symbol map."); + session.psession = perf_session__new(NULL, O_WRONLY, false); + if (session.psession == NULL) + die("Failed to init perf_session."); + session.kmap = map_groups__find_by_name(&session.psession->kmaps, + MAP__FUNCTION, + "[kernel.kallsyms]"); + if (!session.kmap) + die("Could not find kernel map.\n"); + if (session.need_dwarf) #ifdef NO_LIBDWARF die("Debuginfo-analysis is not supported"); #else /* !NO_LIBDWARF */ pr_debug("Some probes require debuginfo.\n"); - if (session.vmlinux) { - pr_debug("Try to open %s.", session.vmlinux); - fd = open(session.vmlinux, O_RDONLY); - } else - fd = open_default_vmlinux(); + fd = open_vmlinux(); if (fd < 0) { if (session.need_dwarf) die("Could not open debuginfo file."); @@ -255,15 +265,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) lseek(fd, SEEK_SET, 0); ret = find_probepoint(fd, pp); - if (ret < 0) { - if (session.need_dwarf) - die("Could not analyze debuginfo."); - - pr_warning("An error occurred in debuginfo analysis. Try to use symbols.\n"); - break; + if (ret > 0) + continue; + if (ret == 0) { /* No error but failed to find probe point. */ + synthesize_perf_probe_point(pp); + die("Probe point '%s' not found. - probe not added.", + pp->probes[0]); } - if (ret == 0) /* No error but failed to find probe point. */ - die("No probe point found."); + /* Error path */ + if (session.need_dwarf) { + if (ret == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); + } + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); + break; } close(fd); @@ -276,6 +293,7 @@ end_dwarf: if (pp->found) /* This probe is already found. */ continue; + evaluate_probe_point(pp); ret = synthesize_trace_kprobe_event(pp); if (ret == -E2BIG) die("probe point definition becomes too long."); @@ -284,7 +302,8 @@ end_dwarf: } /* Settng up probe points */ - add_trace_kprobe_events(session.probes, session.nr_probe); + add_trace_kprobe_events(session.probes, session.nr_probe, + session.force_add); return 0; } |