From 31facc5f1ac674fbcc29f212377e589396bb934c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:30 -0400 Subject: perf probe: Use wrapper functions Use wrapped functions as much as possible, to check out of memory conditions in perf probe. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220530.32050.53951.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7c004b6ef24f..88a3b6d75c7c 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -33,6 +33,7 @@ #include #undef _GNU_SOURCE +#include "util.h" #include "event.h" #include "string.h" #include "strlist.h" @@ -90,9 +91,9 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) if (*tmp != '\0') semantic_error("Tailing with invalid character '%d'.", *tmp); - tmp = strndup(arg, (ptr - arg)); + tmp = xstrndup(arg, (ptr - arg)); } else - tmp = strdup(arg); + tmp = xstrdup(arg); if (strchr(tmp, '.')) lr->file = tmp; @@ -135,7 +136,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.", arg); - pp->event = strdup(arg); + pp->event = xstrdup(arg); arg = tmp; } @@ -147,17 +148,16 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) /* Check arg is function or file and copy it */ if (strchr(arg, '.')) /* File */ - pp->file = strdup(arg); + pp->file = xstrdup(arg); else /* Function */ - pp->function = strdup(arg); - DIE_IF(pp->file == NULL && pp->function == NULL); + pp->function = xstrdup(arg); /* Parse other options */ while (ptr) { arg = ptr; c = nc; if (c == ';') { /* Lazy pattern must be the last part */ - pp->lazy_line = strdup(arg); + pp->lazy_line = xstrdup(arg); break; } ptr = strpbrk(arg, ";:+@%"); @@ -181,8 +181,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) case '@': /* File name */ if (pp->file) semantic_error("SRC@SRC is not allowed."); - pp->file = strdup(arg); - DIE_IF(pp->file == NULL); + pp->file = xstrdup(arg); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { @@ -247,11 +246,9 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp, /* Copy arguments and ensure return probe has no C argument */ pp->nr_args = argc - 1; - pp->args = zalloc(sizeof(char *) * pp->nr_args); + pp->args = xzalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = strdup(argv[i + 1]); - if (!pp->args[i]) - die("Failed to copy argument."); + pp->args[i] = xstrdup(argv[i + 1]); if (is_c_varname(pp->args[i])) { if (pp->retprobe) semantic_error("You can't specify local" @@ -299,14 +296,12 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) pp->file = NULL; pp->nr_args = argc - 2; - pp->args = zalloc(sizeof(char *) * pp->nr_args); + pp->args = xzalloc(sizeof(char *) * pp->nr_args); for (i = 0; i < pp->nr_args; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ *p = '\0'; - pp->args[i] = strdup(argv[i + 2]); - if (!pp->args[i]) - die("Failed to copy argument."); + pp->args[i] = xstrdup(argv[i + 2]); } argv_free(argv); @@ -319,10 +314,8 @@ int synthesize_perf_probe_point(struct probe_point *pp) char offs[64] = "", line[64] = ""; int ret; - pp->probes[0] = buf = zalloc(MAX_CMDLEN); + pp->probes[0] = buf = xzalloc(MAX_CMDLEN); pp->found = 1; - if (!buf) - die("Failed to allocate memory by zalloc."); if (pp->offset) { ret = e_snprintf(offs, 64, "+%d", pp->offset); if (ret <= 0) @@ -380,9 +373,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) char *buf; int i, len, ret; - pp->probes[0] = buf = zalloc(MAX_CMDLEN); - if (!buf) - die("Failed to allocate memory by zalloc."); + pp->probes[0] = buf = xzalloc(MAX_CMDLEN); ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); if (ret <= 0) goto error; @@ -612,10 +603,9 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, for (j = 0; j < nr_probes; j++) { pp = probes + j; if (!pp->event) - pp->event = strdup(pp->function); + pp->event = xstrdup(pp->function); if (!pp->group) - pp->group = strdup(PERFPROBE_GROUP); - DIE_IF(!pp->event || !pp->group); + pp->group = xstrdup(PERFPROBE_GROUP); /* If force_add is true, suffix search is allowed */ allow_suffix = force_add; for (i = 0; i < pp->found; i++) { @@ -709,9 +699,7 @@ void del_trace_kprobe_events(struct strlist *dellist) namelist = get_perf_event_names(fd, true); strlist__for_each(ent, dellist) { - str = strdup(ent->s); - if (!str) - die("Failed to copy event."); + str = xstrdup(ent->s); pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { -- cgit v1.2.2 From e0faa8d35845bb1893cf9e608a5a5d92e9390bf0 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:05:37 -0400 Subject: perf probe: Move add-probe routine to util/ Move add-probe routine to util/probe_event.c. This simplifies main routine for reducing maintenance cost. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220537.32050.72214.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 137 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 88a3b6d75c7c..1e60a659578b 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,6 +40,8 @@ #include "debug.h" #include "cache.h" #include "color.h" +#include "symbol.h" +#include "thread.h" #include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" @@ -65,6 +67,38 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } + +static struct map_groups kmap_groups; +static struct map *kmaps[MAP__NR_TYPES]; + +/* Initialize symbol maps for vmlinux */ +static void init_vmlinux(void) +{ + symbol_conf.sort_by_name = true; + if (symbol_conf.vmlinux_name == NULL) + symbol_conf.try_vmlinux_path = true; + else + pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); + if (symbol__init() < 0) + die("Failed to init symbol map."); + + map_groups__init(&kmap_groups); + if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0) + die("Failed to create kernel maps."); +} + +#ifndef NO_DWARF_SUPPORT +static int open_vmlinux(void) +{ + if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { + pr_debug("Failed to load kernel map.\n"); + return -EINVAL; + } + pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); + return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); +} +#endif + void parse_line_range_desc(const char *arg, struct line_range *lr) { const char *ptr; @@ -586,8 +620,8 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add) +static void __add_trace_kprobe_events(struct probe_point *probes, + int nr_probes, bool force_add) { int i, j, fd; struct probe_point *pp; @@ -640,6 +674,92 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, close(fd); } +/* 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(kmaps[MAP__FUNCTION], + pp->function, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + pp->function); +} + +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, + bool force_add, bool need_dwarf) +{ + int i, ret; + struct probe_point *pp; +#ifndef NO_DWARF_SUPPORT + int fd; +#endif + /* Add probes */ + init_vmlinux(); + + if (need_dwarf) +#ifdef NO_DWARF_SUPPORT + die("Debuginfo-analysis is not supported"); +#else /* !NO_DWARF_SUPPORT */ + pr_debug("Some probes require debuginfo.\n"); + + fd = open_vmlinux(); + if (fd < 0) { + if (need_dwarf) + die("Could not open debuginfo file."); + + pr_debug("Could not open vmlinux/module file." + " Try to use symbols.\n"); + goto end_dwarf; + } + + /* Searching probe points */ + for (i = 0; i < nr_probes; i++) { + pp = &probes[i]; + if (pp->found) + continue; + + lseek(fd, SEEK_SET, 0); + ret = find_probe_point(fd, pp); + 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]); + } + /* Error path */ + if (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); + +end_dwarf: +#endif /* !NO_DWARF_SUPPORT */ + + /* Synthesize probes without dwarf */ + for (i = 0; i < nr_probes; i++) { + pp = &probes[i]; + 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."); + else if (ret < 0) + die("Failed to synthesize a probe point."); + } + + /* Settng up probe points */ + __add_trace_kprobe_events(probes, nr_probes, force_add); +} + static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; @@ -759,6 +879,17 @@ void show_line_range(struct line_range *lr) unsigned int l = 1; struct line_node *ln; FILE *fp; + int fd, ret; + + /* Search a line range */ + init_vmlinux(); + fd = open_vmlinux(); + if (fd < 0) + die("Could not open debuginfo file."); + ret = find_line_range(fd, lr); + if (ret <= 0) + die("Source line is not found.\n"); + close(fd); setup_pager(); @@ -788,3 +919,5 @@ void show_line_range(struct line_range *lr) fclose(fp); } + + -- cgit v1.2.2 From f4d7da499e4fc1fdff8f26fdeb1a058d475a7a6c Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:05 -0400 Subject: perf probe: Add --dry-run option Add --dry-run option for debugging and testing. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220605.32050.6571.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1e60a659578b..ac41578a3552 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -49,6 +49,8 @@ #define MAX_PROBE_ARGS 128 #define PERFPROBE_GROUP "probe" +bool probe_event_dry_run; /* Dry run flag */ + #define semantic_error(msg ...) die("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ @@ -430,7 +432,7 @@ error: return ret; } -static int open_kprobe_events(int flags, int mode) +static int open_kprobe_events(bool readwrite) { char buf[PATH_MAX]; int ret; @@ -439,7 +441,11 @@ static int open_kprobe_events(int flags, int mode) if (ret < 0) die("Failed to make kprobe_events path."); - ret = open(buf, flags, mode); + if (readwrite && !probe_event_dry_run) + ret = open(buf, O_RDWR, O_APPEND); + else + ret = open(buf, O_RDONLY, 0); + if (ret < 0) { if (errno == ENOENT) die("kprobe_events file does not exist -" @@ -535,7 +541,7 @@ void show_perf_probe_events(void) setup_pager(); memset(&pp, 0, sizeof(pp)); - fd = open_kprobe_events(O_RDONLY, 0); + fd = open_kprobe_events(false); rawlist = get_trace_kprobe_event_rawlist(fd); close(fd); @@ -585,9 +591,11 @@ static void write_trace_kprobe_event(int fd, const char *buf) int ret; pr_debug("Writing event: %s\n", buf); - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + if (!probe_event_dry_run) { + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); + } } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -630,7 +638,7 @@ static void __add_trace_kprobe_events(struct probe_point *probes, struct strlist *namelist; bool allow_suffix; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, false); @@ -814,7 +822,7 @@ void del_trace_kprobe_events(struct strlist *dellist) struct str_node *ent; struct strlist *namelist; - fd = open_kprobe_events(O_RDWR, O_APPEND); + fd = open_kprobe_events(true); /* Get current event names */ namelist = get_perf_event_names(fd, true); -- cgit v1.2.2 From 4235b0454ebeefc2295ad8417e18a8761425b19e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:12 -0400 Subject: perf probe: Introduce kprobe_trace_event and perf_probe_event Introduce kprobe_trace_event and perf_probe_event and replace old probe_point structure with it. probe_point structure is not enough flexible nor extensible. New data structures will help implementing further features. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 626 +++++++++++++++++++++++++++--------------- 1 file changed, 400 insertions(+), 226 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ac41578a3552..b44ddfb030d7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -44,6 +44,7 @@ #include "thread.h" #include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" +#include "probe-finder.h" #define MAX_CMDLEN 256 #define MAX_PROBE_ARGS 128 @@ -150,8 +151,9 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) +static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { + struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; char c, nc = 0; /* @@ -172,7 +174,8 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) if (!check_event_name(arg)) semantic_error("%s is bad for event name -it must " "follow C symbol-naming rule.", arg); - pp->event = xstrdup(arg); + pev->event = xstrdup(arg); + pev->group = NULL; arg = tmp; } @@ -255,57 +258,65 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); - pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", + pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); } -/* Parse perf-probe event definition */ -void parse_perf_probe_event(const char *str, struct probe_point *pp, - bool *need_dwarf) +/* Parse perf-probe event command */ +void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { char **argv; int argc, i; - *need_dwarf = false; - - argv = argv_split(str, &argc); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc > MAX_PROBE_ARGS + 1) semantic_error("Too many arguments"); /* Parse probe point */ - parse_perf_probe_probepoint(argv[0], pp); - if (pp->file || pp->line || pp->lazy_line) - *need_dwarf = true; + parse_perf_probe_point(argv[0], pev); /* Copy arguments and ensure return probe has no C argument */ - pp->nr_args = argc - 1; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { - pp->args[i] = xstrdup(argv[i + 1]); - if (is_c_varname(pp->args[i])) { - if (pp->retprobe) - semantic_error("You can't specify local" - " variable for kretprobe"); - *need_dwarf = true; - } + pev->nargs = argc - 1; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < pev->nargs; i++) { + pev->args[i].name = xstrdup(argv[i + 1]); + if (is_c_varname(pev->args[i].name) && pev->point.retprobe) + semantic_error("You can't specify local variable for" + " kretprobe"); } argv_free(argv); } +/* Return true if this perf_probe_event requires debuginfo */ +bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) +{ + int i; + + if (pev->point.file || pev->point.line || pev->point.lazy_line) + return true; + + for (i = 0; i < pev->nargs; i++) + if (is_c_varname(pev->args[i].name)) + return true; + + return false; +} + /* Parse kprobe_events event into struct probe_point */ -void parse_trace_kprobe_event(const char *str, struct probe_point *pp) +void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) { + struct kprobe_trace_point *tp = &tev->point; char pr; char *p; int ret, i, argc; char **argv; - pr_debug("Parsing kprobe_events: %s\n", str); - argv = argv_split(str, &argc); + pr_debug("Parsing kprobe_events: %s\n", cmd); + argv = argv_split(cmd, &argc); if (!argv) die("argv_split failed."); if (argc < 2) @@ -313,47 +324,46 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", - &pr, (float *)(void *)&pp->group, - (float *)(void *)&pp->event); + &pr, (float *)(void *)&tev->group, + (float *)(void *)&tev->event); if (ret != 3) semantic_error("Failed to parse event name: %s", argv[0]); - pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); + pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); - pp->retprobe = (pr == 'r'); + tp->retprobe = (pr == 'r'); /* Scan function name and offset */ - ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, - &pp->offset); + ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, + &tp->offset); if (ret == 1) - pp->offset = 0; - - /* kprobe_events doesn't have this information */ - pp->line = 0; - pp->file = NULL; + tp->offset = 0; - pp->nr_args = argc - 2; - pp->args = xzalloc(sizeof(char *) * pp->nr_args); - for (i = 0; i < pp->nr_args; i++) { + tev->nargs = argc - 2; + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + for (i = 0; i < tev->nargs; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ - *p = '\0'; - pp->args[i] = xstrdup(argv[i + 2]); + *p++ = '\0'; + else + p = argv[i + 2]; + tev->args[i].name = xstrdup(argv[i + 2]); + /* TODO: parse regs and offset */ + tev->args[i].value = xstrdup(p); } argv_free(argv); } -/* Synthesize only probe point (not argument) */ -int synthesize_perf_probe_point(struct probe_point *pp) +/* Compose only probe point (not argument) */ +static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { char *buf; char offs[64] = "", line[64] = ""; int ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - pp->found = 1; + buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%d", pp->offset); + ret = e_snprintf(offs, 64, "+%lu", pp->offset); if (ret <= 0) goto error; } @@ -368,68 +378,209 @@ int synthesize_perf_probe_point(struct probe_point *pp) offs, pp->retprobe ? "%return" : "", line); else ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); - if (ret <= 0) { + if (ret <= 0) + goto error; + + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; - pp->found = 0; - } - return ret; + die("Failed to synthesize perf probe point: %s", strerror(-ret)); } -int synthesize_perf_probe_event(struct probe_point *pp) +#if 0 +char *synthesize_perf_probe_command(struct perf_probe_event *pev) { char *buf; int i, len, ret; - len = synthesize_perf_probe_point(pp); - if (len < 0) - return 0; + buf = synthesize_perf_probe_point(&pev->point); + if (!buf) + return NULL; - buf = pp->probes[0]; - for (i = 0; i < pp->nr_args; i++) { + len = strlen(buf); + for (i = 0; i < pev->nargs; i++) { ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); - if (ret <= 0) - goto error; + pev->args[i].name); + if (ret <= 0) { + free(buf); + return NULL; + } len += ret; } - pp->found = 1; - return pp->found; -error: - free(pp->probes[0]); - pp->probes[0] = NULL; + return buf; +} +#endif + +static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, + char **buf, size_t *buflen, + int depth) +{ + int ret; + if (ref->next) { + depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, + buflen, depth + 1); + if (depth < 0) + goto out; + } + + ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); + if (ret < 0) + depth = ret; + else { + *buf += ret; + *buflen -= ret; + } +out: + return depth; - return ret; } -int synthesize_trace_kprobe_event(struct probe_point *pp) +static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, + char *buf, size_t buflen) { + int ret, depth = 0; + char *tmp = buf; + + /* Argument name or separator */ + if (arg->name) + ret = e_snprintf(buf, buflen, " %s=", arg->name); + else + ret = e_snprintf(buf, buflen, " "); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Dereferencing arguments */ + if (arg->ref) { + depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, + &buflen, 1); + if (depth < 0) + return depth; + } + + /* Print argument value */ + ret = e_snprintf(buf, buflen, "%s", arg->value); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + + /* Closing */ + while (depth--) { + ret = e_snprintf(buf, buflen, ")"); + if (ret < 0) + return ret; + buf += ret; + buflen -= ret; + } + + return buf - tmp; +} + +char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_point *tp = &tev->point; char *buf; int i, len, ret; - pp->probes[0] = buf = xzalloc(MAX_CMDLEN); - ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); - if (ret <= 0) + buf = xzalloc(MAX_CMDLEN); + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", + tp->retprobe ? 'r' : 'p', + tev->group, tev->event, + tp->symbol, tp->offset); + if (len <= 0) goto error; - len = ret; - for (i = 0; i < pp->nr_args; i++) { - ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", - pp->args[i]); + for (i = 0; i < tev->nargs; i++) { + ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, + MAX_CMDLEN - len); if (ret <= 0) goto error; len += ret; } - pp->found = 1; - return pp->found; + return buf; error: - free(pp->probes[0]); - pp->probes[0] = NULL; + free(buf); + return NULL; +} - return ret; +void convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev) +{ + char buf[64]; + int i; + + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + + /* Convert trace_point to probe_point */ + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + pev->point.retprobe = tev->point.retprobe; + + /* Convert trace_arg to probe_arg */ + pev->nargs = tev->nargs; + pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + for (i = 0; i < tev->nargs; i++) + if (tev->args[i].name) + pev->args[i].name = xstrdup(tev->args[i].name); + else { + synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); + pev->args[i].name = xstrdup(buf); + } +} + +void clear_perf_probe_event(struct perf_probe_event *pev) +{ + struct perf_probe_point *pp = &pev->point; + int i; + + if (pev->event) + free(pev->event); + if (pev->group) + free(pev->group); + if (pp->file) + free(pp->file); + if (pp->function) + free(pp->function); + if (pp->lazy_line) + free(pp->lazy_line); + for (i = 0; i < pev->nargs; i++) + if (pev->args[i].name) + free(pev->args[i].name); + if (pev->args) + free(pev->args); + memset(pev, 0, sizeof(*pev)); +} + +void clear_kprobe_trace_event(struct kprobe_trace_event *tev) +{ + struct kprobe_trace_arg_ref *ref, *next; + int i; + + if (tev->event) + free(tev->event); + if (tev->group) + free(tev->group); + if (tev->point.symbol) + free(tev->point.symbol); + for (i = 0; i < tev->nargs; i++) { + if (tev->args[i].name) + free(tev->args[i].name); + if (tev->args[i].value) + free(tev->args[i].value); + ref = tev->args[i].ref; + while (ref) { + next = ref->next; + free(ref); + ref = next; + } + } + if (tev->args) + free(tev->args); + memset(tev, 0, sizeof(*tev)); } static int open_kprobe_events(bool readwrite) @@ -458,7 +609,7 @@ static int open_kprobe_events(bool readwrite) } /* Get raw string list of current kprobe_events */ -static struct strlist *get_trace_kprobe_event_rawlist(int fd) +static struct strlist *get_kprobe_trace_command_rawlist(int fd) { int ret, idx; FILE *fp; @@ -486,99 +637,82 @@ static struct strlist *get_trace_kprobe_event_rawlist(int fd) return sl; } -/* Free and zero clear probe_point */ -static void clear_probe_point(struct probe_point *pp) -{ - int i; - - if (pp->event) - free(pp->event); - if (pp->group) - free(pp->group); - if (pp->function) - free(pp->function); - if (pp->file) - free(pp->file); - if (pp->lazy_line) - free(pp->lazy_line); - for (i = 0; i < pp->nr_args; i++) - free(pp->args[i]); - if (pp->args) - free(pp->args); - for (i = 0; i < pp->found; i++) - free(pp->probes[i]); - memset(pp, 0, sizeof(*pp)); -} - /* Show an event */ -static void show_perf_probe_event(const char *event, const char *place, - struct probe_point *pp) +static void show_perf_probe_event(struct perf_probe_event *pev) { int i, ret; char buf[128]; + char *place; - ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); + /* Synthesize only event probe point */ + place = synthesize_perf_probe_point(&pev->point); + + ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); printf(" %-40s (on %s", buf, place); - if (pp->nr_args > 0) { + if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pp->nr_args; i++) - printf(" %s", pp->args[i]); + for (i = 0; i < pev->nargs; i++) + printf(" %s", pev->args[i].name); } printf(")\n"); + free(place); } /* List up current perf-probe events */ void show_perf_probe_events(void) { int fd; - struct probe_point pp; + struct kprobe_trace_event tev; + struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; setup_pager(); - memset(&pp, 0, sizeof(pp)); + + memset(&tev, 0, sizeof(tev)); + memset(&pev, 0, sizeof(pev)); fd = open_kprobe_events(false); - rawlist = get_trace_kprobe_event_rawlist(fd); + rawlist = get_kprobe_trace_command_rawlist(fd); close(fd); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); - /* Synthesize only event probe point */ - synthesize_perf_probe_point(&pp); + parse_kprobe_trace_command(ent->s, &tev); + convert_to_perf_probe_event(&tev, &pev); /* Show an event */ - show_perf_probe_event(pp.event, pp.probes[0], &pp); - clear_probe_point(&pp); + show_perf_probe_event(&pev); + clear_perf_probe_event(&pev); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); } /* Get current perf-probe event names */ -static struct strlist *get_perf_event_names(int fd, bool include_group) +static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) { char buf[128]; struct strlist *sl, *rawlist; struct str_node *ent; - struct probe_point pp; + struct kprobe_trace_event tev; - memset(&pp, 0, sizeof(pp)); - rawlist = get_trace_kprobe_event_rawlist(fd); + memset(&tev, 0, sizeof(tev)); + rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_trace_kprobe_event(ent->s, &pp); + parse_kprobe_trace_command(ent->s, &tev); if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", pp.group, - pp.event) < 0) + if (e_snprintf(buf, 128, "%s:%s", tev.group, + tev.event) < 0) die("Failed to copy group:event name."); strlist__add(sl, buf); } else - strlist__add(sl, pp.event); - clear_probe_point(&pp); + strlist__add(sl, tev.event); + clear_kprobe_trace_event(&tev); } strlist__delete(rawlist); @@ -586,9 +720,10 @@ static struct strlist *get_perf_event_names(int fd, bool include_group) return sl; } -static void write_trace_kprobe_event(int fd, const char *buf) +static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { int ret; + char *buf = synthesize_kprobe_trace_command(tev); pr_debug("Writing event: %s\n", buf); if (!probe_event_dry_run) { @@ -596,6 +731,7 @@ static void write_trace_kprobe_event(int fd, const char *buf) if (ret <= 0) die("Failed to write event: %s", strerror(errno)); } + free(buf); } static void get_new_event_name(char *buf, size_t len, const char *base, @@ -628,81 +764,83 @@ static void get_new_event_name(char *buf, size_t len, const char *base, die("Too many events are on the same function."); } -static void __add_trace_kprobe_events(struct probe_point *probes, - int nr_probes, bool force_add) +static void __add_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event *tevs, + int ntevs, bool allow_suffix) { - int i, j, fd; - struct probe_point *pp; - char buf[MAX_CMDLEN]; - char event[64]; + int i, fd; + struct kprobe_trace_event *tev; + char buf[64]; + const char *event, *group; struct strlist *namelist; - bool allow_suffix; fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, false); - - for (j = 0; j < nr_probes; j++) { - pp = probes + j; - if (!pp->event) - pp->event = xstrdup(pp->function); - if (!pp->group) - pp->group = xstrdup(PERFPROBE_GROUP); - /* If force_add is true, suffix search is allowed */ - allow_suffix = force_add; - for (i = 0; i < pp->found; i++) { - /* Get an unused new event name */ - get_new_event_name(event, 64, pp->event, namelist, - allow_suffix); - snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", - pp->retprobe ? 'r' : 'p', - pp->group, event, - pp->probes[i]); - write_trace_kprobe_event(fd, buf); - printf("Added new event:\n"); - /* Get the first parameter (probe-point) */ - sscanf(pp->probes[i], "%s", buf); - show_perf_probe_event(event, buf, pp); - /* Add added event name to namelist */ - strlist__add(namelist, event); - /* - * Probes after the first probe which comes from same - * user input are always allowed to add suffix, because - * there might be several addresses corresponding to - * one code line. - */ - allow_suffix = true; - } + namelist = get_kprobe_trace_event_names(fd, false); + + printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); + for (i = 0; i < ntevs; i++) { + tev = &tevs[i]; + if (pev->event) + event = pev->event; + else + if (pev->point.function) + event = pev->point.function; + else + event = tev->point.symbol; + if (pev->group) + group = pev->group; + else + group = PERFPROBE_GROUP; + + /* Get an unused new event name */ + get_new_event_name(buf, 64, event, namelist, allow_suffix); + event = buf; + + tev->event = xstrdup(event); + tev->group = xstrdup(group); + write_kprobe_trace_event(fd, tev); + /* Add added event name to namelist */ + strlist__add(namelist, event); + + /* Trick here - save current event/group */ + event = pev->event; + group = pev->group; + pev->event = tev->event; + pev->group = tev->group; + show_perf_probe_event(pev); + /* Trick here - restore current event/group */ + pev->event = (char *)event; + pev->group = (char *)group; + + /* + * Probes after the first probe which comes from same + * user input are always allowed to add suffix, because + * there might be several addresses corresponding to + * one code line. + */ + allow_suffix = true; } /* Show how to use the event. */ printf("\nYou can now use it on all perf tools, such as:\n\n"); - printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); + printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event); strlist__delete(namelist); close(fd); } -/* Currently just checking function name from symbol map */ -static void evaluate_probe_point(struct probe_point *pp) +static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) { struct symbol *sym; - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], - pp->function, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - pp->function); -} - -void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, - bool force_add, bool need_dwarf) -{ - int i, ret; - struct probe_point *pp; + bool need_dwarf; #ifndef NO_DWARF_SUPPORT int fd; #endif - /* Add probes */ - init_vmlinux(); + int ntevs = 0, i; + struct kprobe_trace_event *tev; + + need_dwarf = perf_probe_event_need_dwarf(pev); if (need_dwarf) #ifdef NO_DWARF_SUPPORT @@ -721,57 +859,90 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, } /* Searching probe points */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) - continue; - - lseek(fd, SEEK_SET, 0); - ret = find_probe_point(fd, pp); - 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]); - } - /* Error path */ - if (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; + ntevs = find_kprobe_trace_events(fd, pev, tevs); + + if (ntevs > 0) /* Found */ + goto found; + + if (ntevs == 0) /* No error but failed to find probe point. */ + die("Probe point '%s' not found. - probe not added.", + synthesize_perf_probe_point(&pev->point)); + + /* Error path */ + if (need_dwarf) { + if (ntevs == -ENOENT) + pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); + die("Could not analyze debuginfo."); } - close(fd); + pr_debug("An error occurred in debuginfo analysis." + " Try to use symbols.\n"); end_dwarf: #endif /* !NO_DWARF_SUPPORT */ - /* Synthesize probes without dwarf */ - for (i = 0; i < nr_probes; i++) { - pp = &probes[i]; - if (pp->found) /* This probe is already found. */ - continue; + /* Allocate trace event buffer */ + ntevs = 1; + tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); + + /* Copy parameters */ + tev->point.symbol = xstrdup(pev->point.function); + tev->point.offset = pev->point.offset; + tev->nargs = pev->nargs; + if (tev->nargs) { + tev->args = xzalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + for (i = 0; i < tev->nargs; i++) + tev->args[i].value = xstrdup(pev->args[i].name); + } + + /* Currently just checking function name from symbol map */ + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (!sym) + die("Kernel symbol \'%s\' not found - probe not added.", + tev->point.symbol); +found: + close(fd); + return ntevs; +} + +struct __event_package { + struct perf_probe_event *pev; + struct kprobe_trace_event *tevs; + int ntevs; +}; - evaluate_probe_point(pp); - ret = synthesize_trace_kprobe_event(pp); - if (ret == -E2BIG) - die("probe point definition becomes too long."); - else if (ret < 0) - die("Failed to synthesize a probe point."); +void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add) +{ + int i; + struct __event_package *pkgs; + + pkgs = xzalloc(sizeof(struct __event_package) * npevs); + + /* Init vmlinux path */ + init_vmlinux(); + + /* Loop 1: convert all events */ + for (i = 0; i < npevs; i++) { + pkgs[i].pev = &pevs[i]; + /* Convert with or without debuginfo */ + pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, + &pkgs[i].tevs); } - /* Settng up probe points */ - __add_trace_kprobe_events(probes, nr_probes, force_add); + /* Loop 2: add all events */ + for (i = 0; i < npevs; i++) + __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + pkgs[i].ntevs, force_add); + /* TODO: cleanup all trace events? */ } static void __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; + int ret; /* Convert from perf-probe event to trace-kprobe event */ if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) @@ -781,7 +952,10 @@ static void __del_trace_kprobe_event(int fd, struct str_node *ent) die("Internal error: %s should have ':' but not.", ent->s); *p = '/'; - write_trace_kprobe_event(fd, buf); + pr_debug("Writing event: %s\n", buf); + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to write event: %s", strerror(errno)); printf("Remove event: %s\n", ent->s); } @@ -814,7 +988,7 @@ static void del_trace_kprobe_event(int fd, const char *group, pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); } -void del_trace_kprobe_events(struct strlist *dellist) +void del_perf_probe_events(struct strlist *dellist) { int fd; const char *group, *event; @@ -824,7 +998,7 @@ void del_trace_kprobe_events(struct strlist *dellist) fd = open_kprobe_events(true); /* Get current event names */ - namelist = get_perf_event_names(fd, true); + namelist = get_kprobe_trace_event_names(fd, true); strlist__for_each(ent, dellist) { str = xstrdup(ent->s); -- cgit v1.2.2 From fb1587d869a399554220e166d4b90b581a8ade01 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:19 -0400 Subject: perf probe: List probes with line number and file name Improve --list to show current exist probes with line number and file name. This enables user easily to check which line is already probed. for example: ./perf probe --list probe:vfs_read (on vfs_read:8@linux-2.6-tip/fs/read_write.c) Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220619.32050.48702.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 55 +++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b44ddfb030d7..4e3c1aea7892 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -70,7 +70,6 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } - static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; @@ -357,27 +356,39 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { - char *buf; - char offs[64] = "", line[64] = ""; - int ret; + char *buf, *tmp; + char offs[32] = "", line[32] = "", file[32] = ""; + int ret, len; buf = xzalloc(MAX_CMDLEN); if (pp->offset) { - ret = e_snprintf(offs, 64, "+%lu", pp->offset); + ret = e_snprintf(offs, 32, "+%lu", pp->offset); if (ret <= 0) goto error; } if (pp->line) { - ret = e_snprintf(line, 64, ":%d", pp->line); + ret = e_snprintf(line, 32, ":%d", pp->line); + if (ret <= 0) + goto error; + } + if (pp->file) { + len = strlen(pp->file) - 32; + if (len < 0) + len = 0; + tmp = strchr(pp->file + len, '/'); + if (!tmp) + tmp = pp->file + len - 1; + ret = e_snprintf(file, 32, "@%s", tmp + 1); if (ret <= 0) goto error; } if (pp->function) - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, - offs, pp->retprobe ? "%return" : "", line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, + offs, pp->retprobe ? "%return" : "", line, + file); else - ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); + ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); if (ret <= 0) goto error; @@ -511,15 +522,32 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, { char buf[64]; int i; +#ifndef NO_DWARF_SUPPORT + struct symbol *sym; + int fd, ret = 0; - pev->event = xstrdup(tev->event); - pev->group = xstrdup(tev->group); - + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tev->point.symbol, NULL); + if (sym) { + fd = open_vmlinux(); + ret = find_perf_probe_point(fd, sym->start + tev->point.offset, + &pev->point); + close(fd); + } + if (ret <= 0) { + pev->point.function = xstrdup(tev->point.symbol); + pev->point.offset = tev->point.offset; + } +#else /* Convert trace_point to probe_point */ pev->point.function = xstrdup(tev->point.symbol); pev->point.offset = tev->point.offset; +#endif pev->point.retprobe = tev->point.retprobe; + pev->event = xstrdup(tev->event); + pev->group = xstrdup(tev->group); + /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -650,7 +678,7 @@ static void show_perf_probe_event(struct perf_probe_event *pev) ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) die("Failed to copy event: %s", strerror(-ret)); - printf(" %-40s (on %s", buf, place); + printf(" %-20s (on %s", buf, place); if (pev->nargs > 0) { printf(" with"); @@ -671,6 +699,7 @@ void show_perf_probe_events(void) struct str_node *ent; setup_pager(); + init_vmlinux(); memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); -- cgit v1.2.2 From 7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 16 Mar 2010 18:06:26 -0400 Subject: perf probe: Add data structure member access support Support accessing members in the data structures. With this, perf-probe accepts data-structure members(IOW, it now accepts dot '.' and arrow '->' operators) as probe arguemnts. e.g. ./perf probe --add 'schedule:44 rq->curr' ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' Note that '>' can be interpreted as redirection in command-line. Signed-off-by: Masami Hiramatsu Cc: systemtap Cc: DLE Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220626.32050.57552.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 90 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4e3c1aea7892..64dea6c3d58a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -262,6 +262,49 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) pp->lazy_line); } +/* Parse perf-probe event argument */ +static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) +{ + const char *tmp; + struct perf_probe_arg_field **fieldp; + + pr_debug("parsing arg: %s into ", str); + + tmp = strpbrk(str, "-."); + if (!is_c_varname(str) || !tmp) { + /* A variable, register, symbol or special value */ + arg->name = xstrdup(str); + pr_debug("%s\n", arg->name); + return; + } + + /* Structure fields */ + arg->name = xstrndup(str, tmp - str); + pr_debug("%s, ", arg->name); + fieldp = &arg->field; + + do { + *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); + if (*tmp == '.') { + str = tmp + 1; + (*fieldp)->ref = false; + } else if (tmp[1] == '>') { + str = tmp + 2; + (*fieldp)->ref = true; + } else + semantic_error("Argument parse error: %s", str); + + tmp = strpbrk(str, "-."); + if (tmp) { + (*fieldp)->name = xstrndup(str, tmp - str); + pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); + fieldp = &(*fieldp)->next; + } + } while (tmp); + (*fieldp)->name = xstrdup(str); + pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); +} + /* Parse perf-probe event command */ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { @@ -281,7 +324,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) pev->nargs = argc - 1; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); for (i = 0; i < pev->nargs; i++) { - pev->args[i].name = xstrdup(argv[i + 1]); + parse_perf_probe_arg(argv[i + 1], &pev->args[i]); if (is_c_varname(pev->args[i].name) && pev->point.retprobe) semantic_error("You can't specify local variable for" " kretprobe"); @@ -353,6 +396,33 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) argv_free(argv); } +/* Compose only probe arg */ +int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) +{ + struct perf_probe_arg_field *field = pa->field; + int ret; + char *tmp = buf; + + ret = e_snprintf(tmp, len, "%s", pa->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + + while (field) { + ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", + field->name); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + field = field->next; + } + return tmp - buf; +error: + die("Failed to synthesize perf probe argument: %s", strerror(-ret)); +} + /* Compose only probe point (not argument) */ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) { @@ -563,6 +633,7 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, void clear_perf_probe_event(struct perf_probe_event *pev) { struct perf_probe_point *pp = &pev->point; + struct perf_probe_arg_field *field, *next; int i; if (pev->event) @@ -575,9 +646,18 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pp->function); if (pp->lazy_line) free(pp->lazy_line); - for (i = 0; i < pev->nargs; i++) + for (i = 0; i < pev->nargs; i++) { if (pev->args[i].name) free(pev->args[i].name); + field = pev->args[i].field; + while (field) { + next = field->next; + if (field->name) + free(field->name); + free(field); + field = next; + } + } if (pev->args) free(pev->args); memset(pev, 0, sizeof(*pev)); @@ -682,8 +762,10 @@ static void show_perf_probe_event(struct perf_probe_event *pev) if (pev->nargs > 0) { printf(" with"); - for (i = 0; i < pev->nargs; i++) - printf(" %s", pev->args[i].name); + for (i = 0; i < pev->nargs; i++) { + synthesize_perf_probe_arg(&pev->args[i], buf, 128); + printf(" %s", buf); + } } printf(")\n"); free(place); -- cgit v1.2.2 From 3b0d516463f8deb897a55cb81e9dbbe58a2490ed Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 17 Mar 2010 12:13:28 +0100 Subject: perf probe: Fix !dwarf build Fix the !drawf build. This uses the existing NO_DWARF_SUPPORT mechanism we use for that, but it's really fragile and needs a cleanup. (in a separate patch) 1) Such uses: #ifndef NO_DWARF_SUPPORT are double inverted logic a'la 'not not'. Instead the flag should be called DWARF_SUPPORT. 2) Furthermore, assymetric #ifdef polluted code flow like: if (need_dwarf) #ifdef NO_DWARF_SUPPORT die("Debuginfo-analysis is not supported"); #else /* !NO_DWARF_SUPPORT */ pr_debug("Some probes require debuginfo.\n"); fd = open_vmlinux(); is very fragile and not acceptable. Instead of that helper functions should be created and the dwarf/no-dwarf logic should be separated more cleanly. 3) Local variable #ifdefs like this: #ifndef NO_DWARF_SUPPORT int fd; #endif Are fragile as well and should be eliminated. Helper functions achieve that too. Cc: Masami Hiramatsu Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 64dea6c3d58a..f33326980583 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1012,8 +1012,10 @@ end_dwarf: if (!sym) die("Kernel symbol \'%s\' not found - probe not added.", tev->point.symbol); +#ifndef NO_DWARF_SUPPORT found: close(fd); +#endif return ntevs; } @@ -1172,10 +1174,13 @@ void show_line_range(struct line_range *lr) unsigned int l = 1; struct line_node *ln; FILE *fp; +#ifndef NO_DWARF_SUPPORT int fd, ret; +#endif /* Search a line range */ init_vmlinux(); +#ifndef NO_DWARF_SUPPORT fd = open_vmlinux(); if (fd < 0) die("Could not open debuginfo file."); @@ -1183,6 +1188,7 @@ void show_line_range(struct line_range *lr) if (ret <= 0) die("Source line is not found.\n"); close(fd); +#endif setup_pager(); -- cgit v1.2.2 From 55632770d7298835645489828af87f854c47749c Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 18 Mar 2010 16:51:16 +0100 Subject: perf events: Fix false positive build warning with older GCC's gcc 4.2.1 produces: util/probe-event.c: In function 'add_perf_probe_events': util/probe-event.c:883: warning: 'tev' may be used uninitialized in this function make: *** [util/probe-event.o] Error 1 Newer GCCs get this right. To work it around, initialize the variable to NULL so that older GCCs see it as initialized too. Cc: Masami Hiramatsu Cc: Frederic Weisbecker Cc: Arnaldo Carvalho de Melo Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <20100316220612.32050.33806.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f33326980583..c6603f3bb430 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -880,7 +880,7 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, int ntevs, bool allow_suffix) { int i, fd; - struct kprobe_trace_event *tev; + struct kprobe_trace_event *tev = NULL; char buf[64]; const char *event, *group; struct strlist *namelist; -- cgit v1.2.2 From 4b4da7f76660ea8b5aa45615165c48f62167ffa8 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 22 Mar 2010 13:10:26 -0300 Subject: perf probe: Cleanup debuginfo related code Cleanup debuginfo related code to eliminate fragile code which pointed by Ingo (Thanks!). 1) Invert logic of NO_DWARF_SUPPORT to DWARF_SUPPORT. 2) For removing assymetric/local variable ifdefs, introduce more helper functions. 3) Change options order to reduce the number of ifdefs. Reported-by: Ingo Molnar Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Mike Galbraith Cc: Peter Zijlstra LKML-Reference: <1269274229-20442-2-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/util/probe-event.c | 343 ++++++++++++++++++++++-------------------- 1 file changed, 184 insertions(+), 159 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index c6603f3bb430..3fc0be741b8e 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -42,7 +42,8 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include "parse-events.h" /* For debugfs_path */ +#include "trace-event.h" /* For __unused */ +#include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" #include "probe-finder.h" @@ -70,10 +71,11 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) return ret; } +static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; -/* Initialize symbol maps for vmlinux */ +/* Initialize symbol maps and path of vmlinux */ static void init_vmlinux(void) { symbol_conf.sort_by_name = true; @@ -89,7 +91,7 @@ static void init_vmlinux(void) die("Failed to create kernel maps."); } -#ifndef NO_DWARF_SUPPORT +#ifdef DWARF_SUPPORT static int open_vmlinux(void) { if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { @@ -99,6 +101,176 @@ static int open_vmlinux(void) pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } + +static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) +{ + struct symbol *sym; + int fd, ret = 0; + + sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], + tp->symbol, NULL); + if (sym) { + fd = open_vmlinux(); + ret = find_perf_probe_point(fd, sym->start + tp->offset, pp); + close(fd); + } + if (ret <= 0) { + pp->function = xstrdup(tp->symbol); + pp->offset = tp->offset; + } + pp->retprobe = tp->retprobe; +} + +/* Try to find perf_probe_event with debuginfo */ +static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs) +{ + bool need_dwarf = perf_probe_event_need_dwarf(pev); + int fd, ntevs; + + fd = open_vmlinux(); + if (fd < 0) { + if (need_dwarf) + die("Could not open debuginfo file."); + + pr_debug("Could not open vmlinux. Try to use symbols.\n"); + return 0; + } + + /* Searching trace events corresponding to probe event */ + ntevs = find_kprobe_trace_events(fd, pev, tevs); + close(fd); + + if (ntevs > 0) /* Succeeded to find trace events */ + return ntevs; + + if (ntevs == 0) /* No error but failed to find probe point. */ + die("Probe point '%s' not found. - probe not added.", + synthesize_perf_probe_point(&pev->point)); + + /* Error path */ + if (need_dwarf) { + if (ntevs == -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"); + return 0; + +} + +#define LINEBUF_SIZE 256 +#define NR_ADDITIONAL_LINES 2 + +static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +{ + char buf[LINEBUF_SIZE]; + const char *color = PERF_COLOR_BLUE; + + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%7u %s", l, buf); + else + color_fprintf(stdout, color, " %s", buf); + } + + while (strlen(buf) == LINEBUF_SIZE - 1 && + buf[LINEBUF_SIZE - 2] != '\n') { + if (fgets(buf, LINEBUF_SIZE, fp) == NULL) + goto error; + if (!skip) { + if (show_num) + fprintf(stdout, "%s", buf); + else + color_fprintf(stdout, color, "%s", buf); + } + } + return; +error: + if (feof(fp)) + die("Source file is shorter than expected."); + else + die("File read error: %s", strerror(errno)); +} + +/* + * Show line-range always requires debuginfo to find source file and + * line number. + */ +void show_line_range(struct line_range *lr) +{ + unsigned int l = 1; + struct line_node *ln; + FILE *fp; + int fd, ret; + + /* Search a line range */ + init_vmlinux(); + fd = open_vmlinux(); + if (fd < 0) + die("Could not open debuginfo file."); + ret = find_line_range(fd, lr); + if (ret <= 0) + die("Source line is not found.\n"); + close(fd); + + setup_pager(); + + if (lr->function) + fprintf(stdout, "<%s:%d>\n", lr->function, + lr->start - lr->offset); + else + fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); + + fp = fopen(lr->path, "r"); + if (fp == NULL) + die("Failed to open %s: %s", lr->path, strerror(errno)); + /* Skip to starting line number */ + while (l < lr->start) + show_one_line(fp, l++, true, false); + + list_for_each_entry(ln, &lr->line_list, list) { + while (ln->line > l) + show_one_line(fp, (l++) - lr->offset, false, false); + show_one_line(fp, (l++) - lr->offset, false, true); + } + + if (lr->end == INT_MAX) + lr->end = l + NR_ADDITIONAL_LINES; + while (l < lr->end && !feof(fp)) + show_one_line(fp, (l++) - lr->offset, false, false); + + fclose(fp); +} + +#else /* !DWARF_SUPPORT */ + +static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) +{ + pp->function = xstrdup(tp->symbol); + pp->offset = tp->offset; + pp->retprobe = tp->retprobe; +} + +static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event **tevs __unused) +{ + if (perf_probe_event_need_dwarf(pev)) + die("Debuginfo-analysis is not supported"); + return 0; +} + +void show_line_range(struct line_range *lr __unused) +{ + die("Debuginfo-analysis is not supported"); +} + #endif void parse_line_range_desc(const char *arg, struct line_range *lr) @@ -592,32 +764,14 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, { char buf[64]; int i; -#ifndef NO_DWARF_SUPPORT - struct symbol *sym; - int fd, ret = 0; - - sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], - tev->point.symbol, NULL); - if (sym) { - fd = open_vmlinux(); - ret = find_perf_probe_point(fd, sym->start + tev->point.offset, - &pev->point); - close(fd); - } - if (ret <= 0) { - pev->point.function = xstrdup(tev->point.symbol); - pev->point.offset = tev->point.offset; - } -#else - /* Convert trace_point to probe_point */ - pev->point.function = xstrdup(tev->point.symbol); - pev->point.offset = tev->point.offset; -#endif - pev->point.retprobe = tev->point.retprobe; + /* Convert event/group name */ pev->event = xstrdup(tev->event); pev->group = xstrdup(tev->group); + /* Convert trace_point to probe_point */ + convert_to_perf_probe_point(&tev->point, &pev->point); + /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); @@ -944,52 +1098,13 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs) { struct symbol *sym; - bool need_dwarf; -#ifndef NO_DWARF_SUPPORT - int fd; -#endif int ntevs = 0, i; struct kprobe_trace_event *tev; - need_dwarf = perf_probe_event_need_dwarf(pev); - - if (need_dwarf) -#ifdef NO_DWARF_SUPPORT - die("Debuginfo-analysis is not supported"); -#else /* !NO_DWARF_SUPPORT */ - pr_debug("Some probes require debuginfo.\n"); - - fd = open_vmlinux(); - if (fd < 0) { - if (need_dwarf) - die("Could not open debuginfo file."); - - pr_debug("Could not open vmlinux/module file." - " Try to use symbols.\n"); - goto end_dwarf; - } - - /* Searching probe points */ - ntevs = find_kprobe_trace_events(fd, pev, tevs); - - if (ntevs > 0) /* Found */ - goto found; - - if (ntevs == 0) /* No error but failed to find probe point. */ - die("Probe point '%s' not found. - probe not added.", - synthesize_perf_probe_point(&pev->point)); - - /* Error path */ - if (need_dwarf) { - if (ntevs == -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"); - -end_dwarf: -#endif /* !NO_DWARF_SUPPORT */ + /* Convert perf_probe_event with debuginfo */ + ntevs = try_to_find_kprobe_trace_events(pev, tevs); + if (ntevs > 0) + return ntevs; /* Allocate trace event buffer */ ntevs = 1; @@ -1012,10 +1127,7 @@ end_dwarf: if (!sym) die("Kernel symbol \'%s\' not found - probe not added.", tev->point.symbol); -#ifndef NO_DWARF_SUPPORT -found: - close(fd); -#endif + return ntevs; } @@ -1133,90 +1245,3 @@ void del_perf_probe_events(struct strlist *dellist) close(fd); } -#define LINEBUF_SIZE 256 -#define NR_ADDITIONAL_LINES 2 - -static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) -{ - char buf[LINEBUF_SIZE]; - const char *color = PERF_COLOR_BLUE; - - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%7u %s", l, buf); - else - color_fprintf(stdout, color, " %s", buf); - } - - while (strlen(buf) == LINEBUF_SIZE - 1 && - buf[LINEBUF_SIZE - 2] != '\n') { - if (fgets(buf, LINEBUF_SIZE, fp) == NULL) - goto error; - if (!skip) { - if (show_num) - fprintf(stdout, "%s", buf); - else - color_fprintf(stdout, color, "%s", buf); - } - } - return; -error: - if (feof(fp)) - die("Source file is shorter than expected."); - else - die("File read error: %s", strerror(errno)); -} - -void show_line_range(struct line_range *lr) -{ - unsigned int l = 1; - struct line_node *ln; - FILE *fp; -#ifndef NO_DWARF_SUPPORT - int fd, ret; -#endif - - /* Search a line range */ - init_vmlinux(); -#ifndef NO_DWARF_SUPPORT - fd = open_vmlinux(); - if (fd < 0) - die("Could not open debuginfo file."); - ret = find_line_range(fd, lr); - if (ret <= 0) - die("Source line is not found.\n"); - close(fd); -#endif - - setup_pager(); - - if (lr->function) - fprintf(stdout, "<%s:%d>\n", lr->function, - lr->start - lr->offset); - else - fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); - - fp = fopen(lr->path, "r"); - if (fp == NULL) - die("Failed to open %s: %s", lr->path, strerror(errno)); - /* Skip to starting line number */ - while (l < lr->start) - show_one_line(fp, l++, true, false); - - list_for_each_entry(ln, &lr->line_list, list) { - while (ln->line > l) - show_one_line(fp, (l++) - lr->offset, false, false); - show_one_line(fp, (l++) - lr->offset, false, true); - } - - if (lr->end == INT_MAX) - lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp)) - show_one_line(fp, (l++) - lr->offset, false, false); - - fclose(fp); -} - - -- cgit v1.2.2 From 48481938b02471d505296d7557ed296eb093d496 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:16:53 -0400 Subject: perf probe: Support argument name Set given names to event arguments. The syntax is same as kprobe-tracer, you can add 'NAME=' right before each argument. e.g. ./perf probe vfs_read foo=file then, 'foo' is set to the argument name as below. ./perf probe -l probe:vfs_read (on vfs_read@linux-2.6-tip/fs/read_write.c with foo) Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171653.3790.74624.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3fc0be741b8e..ab6f53deabad 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -437,22 +437,28 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) /* Parse perf-probe event argument */ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) { - const char *tmp; + char *tmp; struct perf_probe_arg_field **fieldp; pr_debug("parsing arg: %s into ", str); + tmp = strchr(str, '='); + if (tmp) { + arg->name = xstrndup(str, tmp - str); + str = tmp + 1; + } + tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ - arg->name = xstrdup(str); - pr_debug("%s\n", arg->name); + arg->var = xstrdup(str); + pr_debug("%s\n", arg->var); return; } /* Structure fields */ - arg->name = xstrndup(str, tmp - str); - pr_debug("%s, ", arg->name); + arg->var = xstrndup(str, tmp - str); + pr_debug("%s, ", arg->var); fieldp = &arg->field; do { @@ -497,7 +503,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); for (i = 0; i < pev->nargs; i++) { parse_perf_probe_arg(argv[i + 1], &pev->args[i]); - if (is_c_varname(pev->args[i].name) && pev->point.retprobe) + if (is_c_varname(pev->args[i].var) && pev->point.retprobe) semantic_error("You can't specify local variable for" " kretprobe"); } @@ -514,7 +520,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) return true; for (i = 0; i < pev->nargs; i++) - if (is_c_varname(pev->args[i].name)) + if (is_c_varname(pev->args[i].var)) return true; return false; @@ -575,7 +581,10 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) int ret; char *tmp = buf; - ret = e_snprintf(tmp, len, "%s", pa->name); + if (pa->name && pa->var) + ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); + else + ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); if (ret <= 0) goto error; tmp += ret; @@ -803,6 +812,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev) for (i = 0; i < pev->nargs; i++) { if (pev->args[i].name) free(pev->args[i].name); + if (pev->args[i].var) + free(pev->args[i].var); field = pev->args[i].field; while (field) { next = field->next; @@ -1117,8 +1128,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (tev->nargs) { tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); - for (i = 0; i < tev->nargs; i++) - tev->args[i].value = xstrdup(pev->args[i].name); + for (i = 0; i < tev->nargs; i++) { + if (pev->args[i].name) + tev->args[i].name = xstrdup(pev->args[i].name); + tev->args[i].value = xstrdup(pev->args[i].var); + } } /* Currently just checking function name from symbol map */ -- cgit v1.2.2 From df0faf4be02996135bc3a06b4f34360449c78084 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:00 -0400 Subject: perf probe: Use the last field name as the argument name Set the last field name to the argument name when the argument is refering a data-structure member. e.g. ./perf probe --add 'vfs_read file->f_mode' Add new event: probe:vfs_read (on vfs_read with f_mode=file->f_mode) This probe records file->f_mode, but the argument name becomes "f_mode". This enables perf-trace command to parse trace event format correctly. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171700.3790.72961.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ab6f53deabad..19de8b77973d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -481,6 +481,10 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) } while (tmp); (*fieldp)->name = xstrdup(str); pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); + + /* If no name is specified, set the last field name */ + if (!arg->name) + arg->name = xstrdup((*fieldp)->name); } /* Parse perf-probe event command */ -- cgit v1.2.2 From 4984912eb23113a4007940cd09c8351c0623ea5f Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:15 -0400 Subject: perf probe: Query basic types from debuginfo Query the basic type information (byte-size and signed-flag) from debuginfo and pass that to kprobe-tracer. This is especially useful for tracing the members of data structure, because each member has different byte-size on the memory. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171715.3790.23730.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 19de8b77973d..05ca4a959e60 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, buf += ret; buflen -= ret; } + /* Print argument type */ + if (arg->type) { + ret = e_snprintf(buf, buflen, ":%s", arg->type); + if (ret <= 0) + return ret; + buf += ret; + } return buf - tmp; } @@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev) free(tev->args[i].name); if (tev->args[i].value) free(tev->args[i].value); + if (tev->args[i].type) + free(tev->args[i].type); ref = tev->args[i].ref; while (ref) { next = ref->next; -- cgit v1.2.2 From 11a1ca3554b377d2a8a318a3cbf8ce10a7a2a8e4 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:22 -0400 Subject: perf probe: Support basic type casting Add basic type casting for arguments to perf probe. This allows users to specify the actual type of arguments. Of course, if user sets invalid types, kprobe-tracer rejects that. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171722.3790.50372.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 05ca4a959e60..bef280527e60 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -435,7 +435,7 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) } /* Parse perf-probe event argument */ -static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) +static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) { char *tmp; struct perf_probe_arg_field **fieldp; @@ -445,9 +445,17 @@ static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) tmp = strchr(str, '='); if (tmp) { arg->name = xstrndup(str, tmp - str); + pr_debug("name:%s ", arg->name); str = tmp + 1; } + tmp = strchr(str, ':'); + if (tmp) { /* Type setting */ + *tmp = '\0'; + arg->type = xstrdup(tmp + 1); + pr_debug("type:%s ", arg->type); + } + tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ @@ -603,6 +611,15 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) len -= ret; field = field->next; } + + if (pa->type) { + ret = e_snprintf(tmp, len, ":%s", pa->type); + if (ret <= 0) + goto error; + tmp += ret; + len -= ret; + } + return tmp - buf; error: die("Failed to synthesize perf probe argument: %s", strerror(-ret)); @@ -825,6 +842,8 @@ void clear_perf_probe_event(struct perf_probe_event *pev) free(pev->args[i].name); if (pev->args[i].var) free(pev->args[i].var); + if (pev->args[i].type) + free(pev->args[i].type); field = pev->args[i].field; while (field) { next = field->next; @@ -1145,6 +1164,8 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (pev->args[i].name) tev->args[i].name = xstrdup(pev->args[i].name); tev->args[i].value = xstrdup(pev->args[i].var); + if (pev->args[i].type) + tev->args[i].type = xstrdup(pev->args[i].type); } } -- cgit v1.2.2 From b55a87ade3839c33ab94768a0b5955734073f987 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:35 -0400 Subject: perf probe: Remove die() from probe-finder code Remove die() and DIE_IF() code from util/probe-finder.c since these 'sudden death' in utility functions make reusing it from other code (especially tui/gui) difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171735.3790.88853.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bef280527e60..7893b3207dbe 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -151,10 +151,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, /* Error path */ if (need_dwarf) { - if (ntevs == -ENOENT) + if (ntevs == -EBADF) pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Could not analyze debuginfo."); + die("Failed to analyze debuginfo."); } pr_debug("An error occurred in debuginfo analysis." " Try to use symbols.\n"); -- cgit v1.2.2 From 146a143948ed9e8b248c0ec59937f3e9e1bbc7e5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:42 -0400 Subject: perf probe: Remove die() from probe-event code Remove die() and DIE_IF() code from util/probe-event.c since these 'sudden death' in utility functions make reusing it from other code (especially tui/gui) difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171742.3790.33650.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 615 ++++++++++++++++++++++++++++-------------- 1 file changed, 420 insertions(+), 195 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7893b3207dbe..bd68f7b33b21 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -53,7 +53,7 @@ bool probe_event_dry_run; /* Dry run flag */ -#define semantic_error(msg ...) die("Semantic error :" msg) +#define semantic_error(msg ...) pr_err("Semantic error :" msg) /* If there is no space to write, returns -E2BIG. */ static int e_snprintf(char *str, size_t size, const char *format, ...) @@ -76,19 +76,30 @@ static struct map_groups kmap_groups; static struct map *kmaps[MAP__NR_TYPES]; /* Initialize symbol maps and path of vmlinux */ -static void init_vmlinux(void) +static int init_vmlinux(void) { + int ret; + symbol_conf.sort_by_name = true; if (symbol_conf.vmlinux_name == NULL) symbol_conf.try_vmlinux_path = true; else pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); - if (symbol__init() < 0) - die("Failed to init symbol map."); + ret = symbol__init(); + if (ret < 0) { + pr_debug("Failed to init symbol map.\n"); + goto out; + } map_groups__init(&kmap_groups); - if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0) - die("Failed to create kernel maps."); + ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); + if (ret < 0) + pr_debug("Failed to create kernel maps.\n"); + +out: + if (ret < 0) + pr_warning("Failed to init vmlinux path.\n"); + return ret; } #ifdef DWARF_SUPPORT @@ -102,24 +113,32 @@ static int open_vmlinux(void) return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); } -static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, - struct perf_probe_point *pp) +/* Convert trace point to probe point with debuginfo */ +static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, + struct perf_probe_point *pp) { struct symbol *sym; - int fd, ret = 0; + int fd, ret = -ENOENT; sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], tp->symbol, NULL); if (sym) { fd = open_vmlinux(); - ret = find_perf_probe_point(fd, sym->start + tp->offset, pp); - close(fd); + if (fd >= 0) { + ret = find_perf_probe_point(fd, + sym->start + tp->offset, pp); + close(fd); + } } if (ret <= 0) { + pr_debug("Failed to find corresponding probes from " + "debuginfo. Use kprobe event information.\n"); pp->function = xstrdup(tp->symbol); pp->offset = tp->offset; } pp->retprobe = tp->retprobe; + + return 0; } /* Try to find perf_probe_event with debuginfo */ @@ -131,9 +150,10 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, fd = open_vmlinux(); if (fd < 0) { - if (need_dwarf) - die("Could not open debuginfo file."); - + if (need_dwarf) { + pr_warning("Failed to open debuginfo file.\n"); + return fd; + } pr_debug("Could not open vmlinux. Try to use symbols.\n"); return 0; } @@ -142,30 +162,32 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, ntevs = find_kprobe_trace_events(fd, pev, tevs); close(fd); - if (ntevs > 0) /* Succeeded to find trace events */ + if (ntevs > 0) { /* Succeeded to find trace events */ + pr_debug("find %d kprobe_trace_events.\n", ntevs); return ntevs; + } - if (ntevs == 0) /* No error but failed to find probe point. */ - die("Probe point '%s' not found. - probe not added.", - synthesize_perf_probe_point(&pev->point)); - - /* Error path */ + if (ntevs == 0) { /* No error but failed to find probe point. */ + pr_warning("Probe point '%s' not found.\n", + synthesize_perf_probe_point(&pev->point)); + return -ENOENT; + } + /* Error path : ntevs < 0 */ if (need_dwarf) { if (ntevs == -EBADF) pr_warning("No dwarf info found in the vmlinux - " "please rebuild with CONFIG_DEBUG_INFO=y.\n"); - die("Failed to analyze debuginfo."); + return ntevs; } pr_debug("An error occurred in debuginfo analysis." " Try to use symbols.\n"); return 0; - } #define LINEBUF_SIZE 256 #define NR_ADDITIONAL_LINES 2 -static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) { char buf[LINEBUF_SIZE]; const char *color = PERF_COLOR_BLUE; @@ -190,19 +212,22 @@ static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) color_fprintf(stdout, color, "%s", buf); } } - return; + + return 0; error: if (feof(fp)) - die("Source file is shorter than expected."); + pr_warning("Source file is shorter than expected.\n"); else - die("File read error: %s", strerror(errno)); + pr_warning("File read error: %s\n", strerror(errno)); + + return -1; } /* * Show line-range always requires debuginfo to find source file and * line number. */ -void show_line_range(struct line_range *lr) +int show_line_range(struct line_range *lr) { unsigned int l = 1; struct line_node *ln; @@ -210,14 +235,25 @@ void show_line_range(struct line_range *lr) int fd, ret; /* Search a line range */ - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; + fd = open_vmlinux(); - if (fd < 0) - die("Could not open debuginfo file."); + if (fd < 0) { + pr_warning("Failed to open debuginfo file.\n"); + return fd; + } + ret = find_line_range(fd, lr); - if (ret <= 0) - die("Source line is not found.\n"); close(fd); + if (ret == 0) { + pr_warning("Specified source line is not found.\n"); + return -ENOENT; + } else if (ret < 0) { + pr_warning("Debuginfo analysis failed. (%d)\n", ret); + return ret; + } setup_pager(); @@ -228,52 +264,68 @@ void show_line_range(struct line_range *lr) fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); fp = fopen(lr->path, "r"); - if (fp == NULL) - die("Failed to open %s: %s", lr->path, strerror(errno)); + if (fp == NULL) { + pr_warning("Failed to open %s: %s\n", lr->path, + strerror(errno)); + return -errno; + } /* Skip to starting line number */ - while (l < lr->start) - show_one_line(fp, l++, true, false); + while (l < lr->start && ret >= 0) + ret = show_one_line(fp, l++, true, false); + if (ret < 0) + goto end; list_for_each_entry(ln, &lr->line_list, list) { - while (ln->line > l) - show_one_line(fp, (l++) - lr->offset, false, false); - show_one_line(fp, (l++) - lr->offset, false, true); + while (ln->line > l && ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, + false, false); + if (ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, + false, true); + if (ret < 0) + goto end; } if (lr->end == INT_MAX) lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp)) - show_one_line(fp, (l++) - lr->offset, false, false); - + while (l < lr->end && !feof(fp) && ret >= 0) + ret = show_one_line(fp, (l++) - lr->offset, false, false); +end: fclose(fp); + return ret; } #else /* !DWARF_SUPPORT */ -static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, +static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct perf_probe_point *pp) { pp->function = xstrdup(tp->symbol); pp->offset = tp->offset; pp->retprobe = tp->retprobe; + + return 0; } static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs __unused) { - if (perf_probe_event_need_dwarf(pev)) - die("Debuginfo-analysis is not supported"); + if (perf_probe_event_need_dwarf(pev)) { + pr_warning("Debuginfo-analysis is not supported.\n"); + return -ENOSYS; + } return 0; } -void show_line_range(struct line_range *lr __unused) +int show_line_range(struct line_range *lr __unused) { - die("Debuginfo-analysis is not supported"); + pr_warning("Debuginfo-analysis is not supported.\n"); + return -ENOSYS; } #endif -void parse_line_range_desc(const char *arg, struct line_range *lr) +int parse_line_range_desc(const char *arg, struct line_range *lr) { const char *ptr; char *tmp; @@ -293,12 +345,16 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) else lr->end = 0; pr_debug("Line range is %u to %u\n", lr->start, lr->end); - if (lr->end && lr->start > lr->end) + if (lr->end && lr->start > lr->end) { semantic_error("Start line must be smaller" - " than end line."); - if (*tmp != '\0') - semantic_error("Tailing with invalid character '%d'.", + " than end line.\n"); + return -EINVAL; + } + if (*tmp != '\0') { + semantic_error("Tailing with invalid character '%d'.\n", *tmp); + return -EINVAL; + } tmp = xstrndup(arg, (ptr - arg)); } else tmp = xstrdup(arg); @@ -307,6 +363,8 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) lr->file = tmp; else lr->function = tmp; + + return 0; } /* Check the name is good for event/group */ @@ -322,7 +380,7 @@ static bool check_event_name(const char *name) } /* Parse probepoint definition. */ -static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) +static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) { struct perf_probe_point *pp = &pev->point; char *ptr, *tmp; @@ -339,12 +397,15 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) if (ptr && *ptr == '=') { /* Event name */ *ptr = '\0'; tmp = ptr + 1; - ptr = strchr(arg, ':'); - if (ptr) /* Group name is not supported yet. */ - semantic_error("Group name is not supported yet."); - if (!check_event_name(arg)) + if (strchr(arg, ':')) { + semantic_error("Group name is not supported yet.\n"); + return -ENOTSUP; + } + if (!check_event_name(arg)) { semantic_error("%s is bad for event name -it must " - "follow C symbol-naming rule.", arg); + "follow C symbol-naming rule.\n", arg); + return -EINVAL; + } pev->event = xstrdup(arg); pev->group = NULL; arg = tmp; @@ -378,64 +439,89 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) switch (c) { case ':': /* Line number */ pp->line = strtoul(arg, &tmp, 0); - if (*tmp != '\0') + if (*tmp != '\0') { semantic_error("There is non-digit char" - " in line number."); + " in line number.\n"); + return -EINVAL; + } break; case '+': /* Byte offset from a symbol */ pp->offset = strtoul(arg, &tmp, 0); - if (*tmp != '\0') + if (*tmp != '\0') { semantic_error("There is non-digit character" - " in offset."); + " in offset.\n"); + return -EINVAL; + } break; case '@': /* File name */ - if (pp->file) - semantic_error("SRC@SRC is not allowed."); + if (pp->file) { + semantic_error("SRC@SRC is not allowed.\n"); + return -EINVAL; + } pp->file = xstrdup(arg); break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { pp->retprobe = 1; - } else /* Others not supported yet */ - semantic_error("%%%s is not supported.", arg); + } else { /* Others not supported yet */ + semantic_error("%%%s is not supported.\n", arg); + return -ENOTSUP; + } break; - default: - DIE_IF("Program has a bug."); + default: /* Buggy case */ + pr_err("This program has a bug at %s:%d.\n", + __FILE__, __LINE__); + return -ENOTSUP; break; } } /* Exclusion check */ - if (pp->lazy_line && pp->line) + if (pp->lazy_line && pp->line) { semantic_error("Lazy pattern can't be used with line number."); + return -EINVAL; + } - if (pp->lazy_line && pp->offset) + if (pp->lazy_line && pp->offset) { semantic_error("Lazy pattern can't be used with offset."); + return -EINVAL; + } - if (pp->line && pp->offset) + if (pp->line && pp->offset) { semantic_error("Offset can't be used with line number."); + return -EINVAL; + } - if (!pp->line && !pp->lazy_line && pp->file && !pp->function) + if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { semantic_error("File always requires line number or " "lazy pattern."); + return -EINVAL; + } - if (pp->offset && !pp->function) + if (pp->offset && !pp->function) { semantic_error("Offset requires an entry function."); + return -EINVAL; + } - if (pp->retprobe && !pp->function) + if (pp->retprobe && !pp->function) { semantic_error("Return probe requires an entry function."); + return -EINVAL; + } - if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) + if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { semantic_error("Offset/Line/Lazy pattern can't be used with " "return probe."); + return -EINVAL; + } pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", pp->function, pp->file, pp->line, pp->offset, pp->retprobe, pp->lazy_line); + return 0; } /* Parse perf-probe event argument */ -static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) +static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) { char *tmp; struct perf_probe_arg_field **fieldp; @@ -461,7 +547,7 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) /* A variable, register, symbol or special value */ arg->var = xstrdup(str); pr_debug("%s\n", arg->var); - return; + return 0; } /* Structure fields */ @@ -477,8 +563,10 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) } else if (tmp[1] == '>') { str = tmp + 2; (*fieldp)->ref = true; - } else - semantic_error("Argument parse error: %s", str); + } else { + semantic_error("Argument parse error: %s\n", str); + return -EINVAL; + } tmp = strpbrk(str, "-."); if (tmp) { @@ -493,34 +581,47 @@ static void parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) /* If no name is specified, set the last field name */ if (!arg->name) arg->name = xstrdup((*fieldp)->name); + + return 0; } /* Parse perf-probe event command */ -void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) +int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) { char **argv; - int argc, i; + int argc, i, ret = 0; argv = argv_split(cmd, &argc); - if (!argv) - die("argv_split failed."); - if (argc > MAX_PROBE_ARGS + 1) - semantic_error("Too many arguments"); - + if (!argv) { + pr_debug("Failed to split arguments.\n"); + return -ENOMEM; + } + if (argc - 1 > MAX_PROBE_ARGS) { + semantic_error("Too many probe arguments (%d).\n", argc - 1); + ret = -ERANGE; + goto out; + } /* Parse probe point */ - parse_perf_probe_point(argv[0], pev); + ret = parse_perf_probe_point(argv[0], pev); + if (ret < 0) + goto out; /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); - for (i = 0; i < pev->nargs; i++) { - parse_perf_probe_arg(argv[i + 1], &pev->args[i]); - if (is_c_varname(pev->args[i].var) && pev->point.retprobe) + for (i = 0; i < pev->nargs && ret >= 0; i++) { + ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); + if (ret >= 0 && + is_c_varname(pev->args[i].var) && pev->point.retprobe) { semantic_error("You can't specify local variable for" - " kretprobe"); + " kretprobe.\n"); + ret = -EINVAL; + } } - +out: argv_free(argv); + + return ret; } /* Return true if this perf_probe_event requires debuginfo */ @@ -539,7 +640,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) } /* Parse kprobe_events event into struct probe_point */ -void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) +int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) { struct kprobe_trace_point *tp = &tev->point; char pr; @@ -549,17 +650,25 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) pr_debug("Parsing kprobe_events: %s\n", cmd); argv = argv_split(cmd, &argc); - if (!argv) - die("argv_split failed."); - if (argc < 2) - semantic_error("Too less arguments."); + if (!argv) { + pr_debug("Failed to split arguments.\n"); + return -ENOMEM; + } + if (argc < 2) { + semantic_error("Too few probe arguments.\n"); + ret = -ERANGE; + goto out; + } /* Scan event and group name. */ ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", &pr, (float *)(void *)&tev->group, (float *)(void *)&tev->event); - if (ret != 3) - semantic_error("Failed to parse event name: %s", argv[0]); + if (ret != 3) { + semantic_error("Failed to parse event name: %s\n", argv[0]); + ret = -EINVAL; + goto out; + } pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); tp->retprobe = (pr == 'r'); @@ -582,8 +691,10 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) /* TODO: parse regs and offset */ tev->args[i].value = xstrdup(p); } - + ret = 0; +out: argv_free(argv); + return ret; } /* Compose only probe arg */ @@ -622,7 +733,9 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) return tmp - buf; error: - die("Failed to synthesize perf probe argument: %s", strerror(-ret)); + pr_debug("Failed to synthesize perf probe argument: %s", + strerror(-ret)); + return ret; } /* Compose only probe point (not argument) */ @@ -666,7 +779,10 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) return buf; error: - die("Failed to synthesize perf probe point: %s", strerror(-ret)); + pr_debug("Failed to synthesize perf probe point: %s", + strerror(-ret)); + free(buf); + return NULL; } #if 0 @@ -796,29 +912,37 @@ error: return NULL; } -void convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev) +int convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev) { char buf[64]; - int i; + int i, ret; /* Convert event/group name */ pev->event = xstrdup(tev->event); pev->group = xstrdup(tev->group); /* Convert trace_point to probe_point */ - convert_to_perf_probe_point(&tev->point, &pev->point); + ret = convert_to_perf_probe_point(&tev->point, &pev->point); + if (ret < 0) + return ret; /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); - for (i = 0; i < tev->nargs; i++) + for (i = 0; i < tev->nargs && ret >= 0; i++) if (tev->args[i].name) pev->args[i].name = xstrdup(tev->args[i].name); else { - synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); + ret = synthesize_kprobe_trace_arg(&tev->args[i], + buf, 64); pev->args[i].name = xstrdup(buf); } + + if (ret < 0) + clear_perf_probe_event(pev); + + return ret; } void clear_perf_probe_event(struct perf_probe_event *pev) @@ -894,21 +1018,20 @@ static int open_kprobe_events(bool readwrite) int ret; ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); - if (ret < 0) - die("Failed to make kprobe_events path."); - - if (readwrite && !probe_event_dry_run) - ret = open(buf, O_RDWR, O_APPEND); - else - ret = open(buf, O_RDONLY, 0); + if (ret >= 0) { + if (readwrite && !probe_event_dry_run) + ret = open(buf, O_RDWR, O_APPEND); + else + ret = open(buf, O_RDONLY, 0); + } if (ret < 0) { if (errno == ENOENT) - die("kprobe_events file does not exist -" - " please rebuild with CONFIG_KPROBE_EVENT."); + pr_warning("kprobe_events file does not exist - please" + " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); else - die("Could not open kprobe_events file: %s", - strerror(errno)); + pr_warning("Failed to open kprobe_events file: %s\n", + strerror(errno)); } return ret; } @@ -934,8 +1057,11 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd) if (p[idx] == '\n') p[idx] = '\0'; ret = strlist__add(sl, buf); - if (ret < 0) - die("strlist__add failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("strlist__add failed: %s\n", strerror(-ret)); + strlist__delete(sl); + return NULL; + } } fclose(fp); @@ -943,7 +1069,7 @@ static struct strlist *get_kprobe_trace_command_rawlist(int fd) } /* Show an event */ -static void show_perf_probe_event(struct perf_probe_event *pev) +static int show_perf_probe_event(struct perf_probe_event *pev) { int i, ret; char buf[128]; @@ -951,52 +1077,71 @@ static void show_perf_probe_event(struct perf_probe_event *pev) /* Synthesize only event probe point */ place = synthesize_perf_probe_point(&pev->point); + if (!place) + return -EINVAL; ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); if (ret < 0) - die("Failed to copy event: %s", strerror(-ret)); + return ret; + printf(" %-20s (on %s", buf, place); if (pev->nargs > 0) { printf(" with"); for (i = 0; i < pev->nargs; i++) { - synthesize_perf_probe_arg(&pev->args[i], buf, 128); + ret = synthesize_perf_probe_arg(&pev->args[i], + buf, 128); + if (ret < 0) + break; printf(" %s", buf); } } printf(")\n"); free(place); + return ret; } /* List up current perf-probe events */ -void show_perf_probe_events(void) +int show_perf_probe_events(void) { - int fd; + int fd, ret; struct kprobe_trace_event tev; struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; setup_pager(); - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); fd = open_kprobe_events(false); + if (fd < 0) + return fd; + rawlist = get_kprobe_trace_command_rawlist(fd); close(fd); + if (!rawlist) + return -ENOENT; strlist__for_each(ent, rawlist) { - parse_kprobe_trace_command(ent->s, &tev); - convert_to_perf_probe_event(&tev, &pev); - /* Show an event */ - show_perf_probe_event(&pev); + ret = parse_kprobe_trace_command(ent->s, &tev); + if (ret >= 0) { + ret = convert_to_perf_probe_event(&tev, &pev); + if (ret >= 0) + ret = show_perf_probe_event(&pev); + } clear_perf_probe_event(&pev); clear_kprobe_trace_event(&tev); + if (ret < 0) + break; } - strlist__delete(rawlist); + + return ret; } /* Get current perf-probe event names */ @@ -1006,88 +1151,118 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) struct strlist *sl, *rawlist; struct str_node *ent; struct kprobe_trace_event tev; + int ret = 0; memset(&tev, 0, sizeof(tev)); rawlist = get_kprobe_trace_command_rawlist(fd); sl = strlist__new(true, NULL); strlist__for_each(ent, rawlist) { - parse_kprobe_trace_command(ent->s, &tev); + ret = parse_kprobe_trace_command(ent->s, &tev); + if (ret < 0) + break; if (include_group) { - if (e_snprintf(buf, 128, "%s:%s", tev.group, - tev.event) < 0) - die("Failed to copy group:event name."); - strlist__add(sl, buf); + ret = e_snprintf(buf, 128, "%s:%s", tev.group, + tev.event); + if (ret >= 0) + ret = strlist__add(sl, buf); } else - strlist__add(sl, tev.event); + ret = strlist__add(sl, tev.event); clear_kprobe_trace_event(&tev); + if (ret < 0) + break; } - strlist__delete(rawlist); + if (ret < 0) { + strlist__delete(sl); + return NULL; + } return sl; } -static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) +static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) { int ret; char *buf = synthesize_kprobe_trace_command(tev); + if (!buf) { + pr_debug("Failed to synthesize kprobe trace event.\n"); + return -EINVAL; + } + pr_debug("Writing event: %s\n", buf); if (!probe_event_dry_run) { ret = write(fd, buf, strlen(buf)); if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + pr_warning("Failed to write event: %s\n", + strerror(errno)); } free(buf); + return ret; } -static void get_new_event_name(char *buf, size_t len, const char *base, - struct strlist *namelist, bool allow_suffix) +static int get_new_event_name(char *buf, size_t len, const char *base, + struct strlist *namelist, bool allow_suffix) { int i, ret; /* Try no suffix */ ret = e_snprintf(buf, len, "%s", base); - if (ret < 0) - die("snprintf() failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("snprintf() failed: %s\n", strerror(-ret)); + return ret; + } if (!strlist__has_entry(namelist, buf)) - return; + return 0; if (!allow_suffix) { pr_warning("Error: event \"%s\" already exists. " "(Use -f to force duplicates.)\n", base); - die("Can't add new event."); + return -EEXIST; } /* Try to add suffix */ for (i = 1; i < MAX_EVENT_INDEX; i++) { ret = e_snprintf(buf, len, "%s_%d", base, i); - if (ret < 0) - die("snprintf() failed: %s", strerror(-ret)); + if (ret < 0) { + pr_debug("snprintf() failed: %s\n", strerror(-ret)); + return ret; + } if (!strlist__has_entry(namelist, buf)) break; } - if (i == MAX_EVENT_INDEX) - die("Too many events are on the same function."); + if (i == MAX_EVENT_INDEX) { + pr_warning("Too many events are on the same function.\n"); + ret = -ERANGE; + } + + return ret; } -static void __add_kprobe_trace_events(struct perf_probe_event *pev, - struct kprobe_trace_event *tevs, - int ntevs, bool allow_suffix) +static int __add_kprobe_trace_events(struct perf_probe_event *pev, + struct kprobe_trace_event *tevs, + int ntevs, bool allow_suffix) { - int i, fd; + int i, fd, ret; struct kprobe_trace_event *tev = NULL; char buf[64]; const char *event, *group; struct strlist *namelist; fd = open_kprobe_events(true); + if (fd < 0) + return fd; /* Get current event names */ namelist = get_kprobe_trace_event_names(fd, false); + if (!namelist) { + pr_debug("Failed to get current event list.\n"); + return -EIO; + } + ret = 0; printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); - for (i = 0; i < ntevs; i++) { + for (i = 0; i < ntevs && ret >= 0; i++) { tev = &tevs[i]; if (pev->event) event = pev->event; @@ -1102,12 +1277,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, group = PERFPROBE_GROUP; /* Get an unused new event name */ - get_new_event_name(buf, 64, event, namelist, allow_suffix); + ret = get_new_event_name(buf, 64, event, + namelist, allow_suffix); + if (ret < 0) + break; event = buf; tev->event = xstrdup(event); tev->group = xstrdup(group); - write_kprobe_trace_event(fd, tev); + ret = write_kprobe_trace_event(fd, tev); + if (ret < 0) + break; /* Add added event name to namelist */ strlist__add(namelist, event); @@ -1129,12 +1309,17 @@ static void __add_kprobe_trace_events(struct perf_probe_event *pev, */ allow_suffix = true; } - /* Show how to use the event. */ - printf("\nYou can now use it on all perf tools, such as:\n\n"); - printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event); + + if (ret >= 0) { + /* Show how to use the event. */ + printf("\nYou can now use it on all perf tools, such as:\n\n"); + printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, + tev->event); + } strlist__delete(namelist); close(fd); + return ret; } static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, @@ -1146,7 +1331,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, /* Convert perf_probe_event with debuginfo */ ntevs = try_to_find_kprobe_trace_events(pev, tevs); - if (ntevs > 0) + if (ntevs != 0) return ntevs; /* Allocate trace event buffer */ @@ -1172,10 +1357,11 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, /* Currently just checking function name from symbol map */ sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], tev->point.symbol, NULL); - if (!sym) - die("Kernel symbol \'%s\' not found - probe not added.", - tev->point.symbol); - + if (!sym) { + pr_warning("Kernel symbol \'%s\' not found.\n", + tev->point.symbol); + return -ENOENT; + } return ntevs; } @@ -1185,93 +1371,128 @@ struct __event_package { int ntevs; }; -void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - bool force_add) +int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, + bool force_add) { - int i; + int i, j, ret; struct __event_package *pkgs; pkgs = xzalloc(sizeof(struct __event_package) * npevs); /* Init vmlinux path */ - init_vmlinux(); + ret = init_vmlinux(); + if (ret < 0) + return ret; /* Loop 1: convert all events */ for (i = 0; i < npevs; i++) { pkgs[i].pev = &pevs[i]; /* Convert with or without debuginfo */ - pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, - &pkgs[i].tevs); + ret = convert_to_kprobe_trace_events(pkgs[i].pev, + &pkgs[i].tevs); + if (ret < 0) + goto end; + pkgs[i].ntevs = ret; } /* Loop 2: add all events */ + for (i = 0; i < npevs && ret >= 0; i++) + ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, + pkgs[i].ntevs, force_add); +end: + /* Loop 3: cleanup trace events */ for (i = 0; i < npevs; i++) - __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, - pkgs[i].ntevs, force_add); - /* TODO: cleanup all trace events? */ + for (j = 0; j < pkgs[i].ntevs; j++) + clear_kprobe_trace_event(&pkgs[i].tevs[j]); + + return ret; } -static void __del_trace_kprobe_event(int fd, struct str_node *ent) +static int __del_trace_kprobe_event(int fd, struct str_node *ent) { char *p; char buf[128]; int ret; /* Convert from perf-probe event to trace-kprobe event */ - if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) - die("Failed to copy event."); + ret = e_snprintf(buf, 128, "-:%s", ent->s); + if (ret < 0) + goto error; + p = strchr(buf + 2, ':'); - if (!p) - die("Internal error: %s should have ':' but not.", ent->s); + if (!p) { + pr_debug("Internal error: %s should have ':' but not.\n", + ent->s); + ret = -ENOTSUP; + goto error; + } *p = '/'; pr_debug("Writing event: %s\n", buf); ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to write event: %s", strerror(errno)); + if (ret < 0) + goto error; + printf("Remove event: %s\n", ent->s); + return 0; +error: + pr_warning("Failed to delete event: %s\n", strerror(-ret)); + return ret; } -static void del_trace_kprobe_event(int fd, const char *group, - const char *event, struct strlist *namelist) +static int del_trace_kprobe_event(int fd, const char *group, + const char *event, struct strlist *namelist) { char buf[128]; struct str_node *ent, *n; - int found = 0; + int found = 0, ret = 0; - if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) - die("Failed to copy event."); + ret = e_snprintf(buf, 128, "%s:%s", group, event); + if (ret < 0) { + pr_err("Failed to copy event."); + return ret; + } if (strpbrk(buf, "*?")) { /* Glob-exp */ strlist__for_each_safe(ent, n, namelist) if (strglobmatch(ent->s, buf)) { found++; - __del_trace_kprobe_event(fd, ent); + ret = __del_trace_kprobe_event(fd, ent); + if (ret < 0) + break; strlist__remove(namelist, ent); } } else { ent = strlist__find(namelist, buf); if (ent) { found++; - __del_trace_kprobe_event(fd, ent); - strlist__remove(namelist, ent); + ret = __del_trace_kprobe_event(fd, ent); + if (ret >= 0) + strlist__remove(namelist, ent); } } - if (found == 0) - pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); + if (found == 0 && ret >= 0) + pr_info("Info: Event \"%s\" does not exist.\n", buf); + + return ret; } -void del_perf_probe_events(struct strlist *dellist) +int del_perf_probe_events(struct strlist *dellist) { - int fd; + int fd, ret = 0; const char *group, *event; char *p, *str; struct str_node *ent; struct strlist *namelist; fd = open_kprobe_events(true); + if (fd < 0) + return fd; + /* Get current event names */ namelist = get_kprobe_trace_event_names(fd, true); + if (namelist == NULL) + return -EINVAL; strlist__for_each(ent, dellist) { str = xstrdup(ent->s); @@ -1286,10 +1507,14 @@ void del_perf_probe_events(struct strlist *dellist) event = str; } pr_debug("Group: %s, Event: %s\n", group, event); - del_trace_kprobe_event(fd, group, event, namelist); + ret = del_trace_kprobe_event(fd, group, event, namelist); free(str); + if (ret < 0) + break; } strlist__delete(namelist); close(fd); + + return ret; } -- cgit v1.2.2 From e334016f1d7250a6523b3a44ccecfe23af6e2f57 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:49 -0400 Subject: perf probe: Remove xzalloc() from util/probe-{event, finder}.c Remove all xzalloc() calls from util/probe-{event,finder}.c since it may cause 'sudden death' in utility functions and it makes reusing it from other code difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171749.3790.33303.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 69 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 18 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index bd68f7b33b21..aacbf730b475 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -556,7 +556,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) fieldp = &arg->field; do { - *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); + *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); + if (*fieldp == NULL) + return -ENOMEM; if (*tmp == '.') { str = tmp + 1; (*fieldp)->ref = false; @@ -608,7 +610,11 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) /* Copy arguments and ensure return probe has no C argument */ pev->nargs = argc - 1; - pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + if (pev->args == NULL) { + ret = -ENOMEM; + goto out; + } for (i = 0; i < pev->nargs && ret >= 0; i++) { ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); if (ret >= 0 && @@ -680,7 +686,11 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) tp->offset = 0; tev->nargs = argc - 2; - tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); + if (tev->args == NULL) { + ret = -ENOMEM; + goto out; + } for (i = 0; i < tev->nargs; i++) { p = strchr(argv[i + 2], '='); if (p) /* We don't need which register is assigned. */ @@ -745,7 +755,11 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) char offs[32] = "", line[32] = "", file[32] = ""; int ret, len; - buf = xzalloc(MAX_CMDLEN); + buf = zalloc(MAX_CMDLEN); + if (buf == NULL) { + ret = -ENOMEM; + goto error; + } if (pp->offset) { ret = e_snprintf(offs, 32, "+%lu", pp->offset); if (ret <= 0) @@ -781,7 +795,8 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) error: pr_debug("Failed to synthesize perf probe point: %s", strerror(-ret)); - free(buf); + if (buf) + free(buf); return NULL; } @@ -890,7 +905,10 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) char *buf; int i, len, ret; - buf = xzalloc(MAX_CMDLEN); + buf = zalloc(MAX_CMDLEN); + if (buf == NULL) + return NULL; + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", tp->retprobe ? 'r' : 'p', tev->group, tev->event, @@ -929,7 +947,9 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, /* Convert trace_arg to probe_arg */ pev->nargs = tev->nargs; - pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); + pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); + if (pev->args == NULL) + return -ENOMEM; for (i = 0; i < tev->nargs && ret >= 0; i++) if (tev->args[i].name) pev->args[i].name = xstrdup(tev->args[i].name); @@ -1326,25 +1346,31 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, struct kprobe_trace_event **tevs) { struct symbol *sym; - int ntevs = 0, i; + int ret = 0, i; struct kprobe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ntevs = try_to_find_kprobe_trace_events(pev, tevs); - if (ntevs != 0) - return ntevs; + ret = try_to_find_kprobe_trace_events(pev, tevs); + if (ret != 0) + return ret; /* Allocate trace event buffer */ - ntevs = 1; - tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); + tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); + if (tev == NULL) + return -ENOMEM; /* Copy parameters */ tev->point.symbol = xstrdup(pev->point.function); tev->point.offset = pev->point.offset; tev->nargs = pev->nargs; if (tev->nargs) { - tev->args = xzalloc(sizeof(struct kprobe_trace_arg) - * tev->nargs); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + if (tev->args == NULL) { + free(tev); + *tevs = NULL; + return -ENOMEM; + } for (i = 0; i < tev->nargs; i++) { if (pev->args[i].name) tev->args[i].name = xstrdup(pev->args[i].name); @@ -1360,9 +1386,14 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", tev->point.symbol); + clear_kprobe_trace_event(tev); + free(tev); + *tevs = NULL; return -ENOENT; - } - return ntevs; + } else + ret = 1; + + return ret; } struct __event_package { @@ -1377,7 +1408,9 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int i, j, ret; struct __event_package *pkgs; - pkgs = xzalloc(sizeof(struct __event_package) * npevs); + pkgs = zalloc(sizeof(struct __event_package) * npevs); + if (pkgs == NULL) + return -ENOMEM; /* Init vmlinux path */ ret = init_vmlinux(); -- cgit v1.2.2 From 02b95dadc8a1d2c302513e5fa24c492380d26e93 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Mon, 12 Apr 2010 13:17:56 -0400 Subject: perf probe: Remove xstrdup()/xstrndup() from util/probe-{event, finder}.c Remove all xstr*dup() calls from util/probe-{event,finder}.c since it may cause 'sudden death' in utility functions and it makes reusing it from other code difficult. Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker LKML-Reference: <20100412171756.3790.89607.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 159 ++++++++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 46 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index aacbf730b475..ca108b2cd15f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -133,7 +133,9 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, if (ret <= 0) { pr_debug("Failed to find corresponding probes from " "debuginfo. Use kprobe event information.\n"); - pp->function = xstrdup(tp->symbol); + pp->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; pp->offset = tp->offset; } pp->retprobe = tp->retprobe; @@ -300,7 +302,9 @@ end: static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, struct perf_probe_point *pp) { - pp->function = xstrdup(tp->symbol); + pp->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; pp->offset = tp->offset; pp->retprobe = tp->retprobe; @@ -355,9 +359,12 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) *tmp); return -EINVAL; } - tmp = xstrndup(arg, (ptr - arg)); + tmp = strndup(arg, (ptr - arg)); } else - tmp = xstrdup(arg); + tmp = strdup(arg); + + if (tmp == NULL) + return -ENOMEM; if (strchr(tmp, '.')) lr->file = tmp; @@ -406,7 +413,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) "follow C symbol-naming rule.\n", arg); return -EINVAL; } - pev->event = xstrdup(arg); + pev->event = strdup(arg); + if (pev->event == NULL) + return -ENOMEM; pev->group = NULL; arg = tmp; } @@ -417,18 +426,24 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) *ptr++ = '\0'; } + tmp = strdup(arg); + if (tmp == NULL) + return -ENOMEM; + /* Check arg is function or file and copy it */ - if (strchr(arg, '.')) /* File */ - pp->file = xstrdup(arg); + if (strchr(tmp, '.')) /* File */ + pp->file = tmp; else /* Function */ - pp->function = xstrdup(arg); + pp->function = tmp; /* Parse other options */ while (ptr) { arg = ptr; c = nc; if (c == ';') { /* Lazy pattern must be the last part */ - pp->lazy_line = xstrdup(arg); + pp->lazy_line = strdup(arg); + if (pp->lazy_line == NULL) + return -ENOMEM; break; } ptr = strpbrk(arg, ";:+@%"); @@ -458,7 +473,9 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) semantic_error("SRC@SRC is not allowed.\n"); return -EINVAL; } - pp->file = xstrdup(arg); + pp->file = strdup(arg); + if (pp->file == NULL) + return -ENOMEM; break; case '%': /* Probe places */ if (strcmp(arg, "return") == 0) { @@ -530,7 +547,9 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strchr(str, '='); if (tmp) { - arg->name = xstrndup(str, tmp - str); + arg->name = strndup(str, tmp - str); + if (arg->name == NULL) + return -ENOMEM; pr_debug("name:%s ", arg->name); str = tmp + 1; } @@ -538,20 +557,26 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strchr(str, ':'); if (tmp) { /* Type setting */ *tmp = '\0'; - arg->type = xstrdup(tmp + 1); + arg->type = strdup(tmp + 1); + if (arg->type == NULL) + return -ENOMEM; pr_debug("type:%s ", arg->type); } tmp = strpbrk(str, "-."); if (!is_c_varname(str) || !tmp) { /* A variable, register, symbol or special value */ - arg->var = xstrdup(str); + arg->var = strdup(str); + if (arg->var == NULL) + return -ENOMEM; pr_debug("%s\n", arg->var); return 0; } /* Structure fields */ - arg->var = xstrndup(str, tmp - str); + arg->var = strndup(str, tmp - str); + if (arg->var == NULL) + return -ENOMEM; pr_debug("%s, ", arg->var); fieldp = &arg->field; @@ -572,18 +597,24 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) tmp = strpbrk(str, "-."); if (tmp) { - (*fieldp)->name = xstrndup(str, tmp - str); + (*fieldp)->name = strndup(str, tmp - str); + if ((*fieldp)->name == NULL) + return -ENOMEM; pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); fieldp = &(*fieldp)->next; } } while (tmp); - (*fieldp)->name = xstrdup(str); + (*fieldp)->name = strdup(str); + if ((*fieldp)->name == NULL) + return -ENOMEM; pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); /* If no name is specified, set the last field name */ - if (!arg->name) - arg->name = xstrdup((*fieldp)->name); - + if (!arg->name) { + arg->name = strdup((*fieldp)->name); + if (arg->name == NULL) + return -ENOMEM; + } return 0; } @@ -697,9 +728,13 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) *p++ = '\0'; else p = argv[i + 2]; - tev->args[i].name = xstrdup(argv[i + 2]); + tev->args[i].name = strdup(argv[i + 2]); /* TODO: parse regs and offset */ - tev->args[i].value = xstrdup(p); + tev->args[i].value = strdup(p); + if (tev->args[i].name == NULL || tev->args[i].value == NULL) { + ret = -ENOMEM; + goto out; + } } ret = 0; out: @@ -933,12 +968,14 @@ error: int convert_to_perf_probe_event(struct kprobe_trace_event *tev, struct perf_probe_event *pev) { - char buf[64]; + char buf[64] = ""; int i, ret; /* Convert event/group name */ - pev->event = xstrdup(tev->event); - pev->group = xstrdup(tev->group); + pev->event = strdup(tev->event); + pev->group = strdup(tev->group); + if (pev->event == NULL || pev->group == NULL) + return -ENOMEM; /* Convert trace_point to probe_point */ ret = convert_to_perf_probe_point(&tev->point, &pev->point); @@ -950,14 +987,17 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); if (pev->args == NULL) return -ENOMEM; - for (i = 0; i < tev->nargs && ret >= 0; i++) + for (i = 0; i < tev->nargs && ret >= 0; i++) { if (tev->args[i].name) - pev->args[i].name = xstrdup(tev->args[i].name); + pev->args[i].name = strdup(tev->args[i].name); else { ret = synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); - pev->args[i].name = xstrdup(buf); + pev->args[i].name = strdup(buf); } + if (pev->args[i].name == NULL && ret >= 0) + ret = -ENOMEM; + } if (ret < 0) clear_perf_probe_event(pev); @@ -1282,7 +1322,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, ret = 0; printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); - for (i = 0; i < ntevs && ret >= 0; i++) { + for (i = 0; i < ntevs; i++) { tev = &tevs[i]; if (pev->event) event = pev->event; @@ -1303,8 +1343,12 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, break; event = buf; - tev->event = xstrdup(event); - tev->group = xstrdup(group); + tev->event = strdup(event); + tev->group = strdup(group); + if (tev->event == NULL || tev->group == NULL) { + ret = -ENOMEM; + break; + } ret = write_kprobe_trace_event(fd, tev); if (ret < 0) break; @@ -1360,23 +1404,40 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, return -ENOMEM; /* Copy parameters */ - tev->point.symbol = xstrdup(pev->point.function); + tev->point.symbol = strdup(pev->point.function); + if (tev->point.symbol == NULL) { + ret = -ENOMEM; + goto error; + } tev->point.offset = pev->point.offset; tev->nargs = pev->nargs; if (tev->nargs) { tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); if (tev->args == NULL) { - free(tev); - *tevs = NULL; - return -ENOMEM; + ret = -ENOMEM; + goto error; } for (i = 0; i < tev->nargs; i++) { - if (pev->args[i].name) - tev->args[i].name = xstrdup(pev->args[i].name); - tev->args[i].value = xstrdup(pev->args[i].var); - if (pev->args[i].type) - tev->args[i].type = xstrdup(pev->args[i].type); + if (pev->args[i].name) { + tev->args[i].name = strdup(pev->args[i].name); + if (tev->args[i].name == NULL) { + ret = -ENOMEM; + goto error; + } + } + tev->args[i].value = strdup(pev->args[i].var); + if (tev->args[i].value == NULL) { + ret = -ENOMEM; + goto error; + } + if (pev->args[i].type) { + tev->args[i].type = strdup(pev->args[i].type); + if (tev->args[i].type == NULL) { + ret = -ENOMEM; + goto error; + } + } } } @@ -1386,13 +1447,15 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, if (!sym) { pr_warning("Kernel symbol \'%s\' not found.\n", tev->point.symbol); - clear_kprobe_trace_event(tev); - free(tev); - *tevs = NULL; - return -ENOENT; - } else - ret = 1; + ret = -ENOENT; + goto error; + } + return 1; +error: + clear_kprobe_trace_event(tev); + free(tev); + *tevs = NULL; return ret; } @@ -1528,7 +1591,11 @@ int del_perf_probe_events(struct strlist *dellist) return -EINVAL; strlist__for_each(ent, dellist) { - str = xstrdup(ent->s); + str = strdup(ent->s); + if (str == NULL) { + ret = -ENOMEM; + break; + } pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { -- cgit v1.2.2 From 7ca5989dd065cbc48a958666c273794686ea7525 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:28 -0400 Subject: perf probe: Fix to use correct debugfs path finder Instead of using debugfs_path, use debugfs_find_mountpoint() to find actual debugfs path. LKML-Reference: <20100414223928.14630.38326.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Reported-by: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index ca108b2cd15f..1c4a20a284c0 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -42,8 +42,8 @@ #include "color.h" #include "symbol.h" #include "thread.h" +#include "debugfs.h" #include "trace-event.h" /* For __unused */ -#include "parse-events.h" /* For debugfs_path */ #include "probe-event.h" #include "probe-finder.h" @@ -1075,10 +1075,18 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev) static int open_kprobe_events(bool readwrite) { char buf[PATH_MAX]; + const char *__debugfs; int ret; - ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); + __debugfs = debugfs_find_mountpoint(); + if (__debugfs == NULL) { + pr_warning("Debugfs is not mounted.\n"); + return -ENOENT; + } + + ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); if (ret >= 0) { + pr_debug("Opening %s write=%d\n", buf, readwrite); if (readwrite && !probe_event_dry_run) ret = open(buf, O_RDWR, O_APPEND); else -- cgit v1.2.2 From dd259c5db26ccda46409dbf6efc79d5a2b259e38 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:35 -0400 Subject: perf probe: Fix mis-estimation for shortening filename Fix mis-estimation size for making a short filename. Since the buffer size is 32 bytes and there are '@' prefix and '\0' termination, maximum shorten filename length should be 30. This means, before searching '/', it should be 31 bytes. LKML-Reference: <20100414223935.14630.11954.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 1c4a20a284c0..6d438391bae5 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -806,12 +806,12 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) goto error; } if (pp->file) { - len = strlen(pp->file) - 32; + len = strlen(pp->file) - 31; if (len < 0) len = 0; tmp = strchr(pp->file + len, '/'); if (!tmp) - tmp = pp->file + len - 1; + tmp = pp->file + len; ret = e_snprintf(file, 32, "@%s", tmp + 1); if (ret <= 0) goto error; -- cgit v1.2.2 From d3b63d7ae04879a817bac5c0bf09749f73629d32 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:42 -0400 Subject: perf probe: Fix a bug that --line range can be overflow Since line_finder.lno_s/e are signed int but line_range.start/end are unsigned int, it is possible to be overflow when converting line_range->start/end to line_finder->lno_s/e. This changes line_range.start/end and line_list.line to signed int and adds overflow checks when setting line_finder.lno_s/e. LKML-Reference: <20100414223942.14630.72730.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6d438391bae5..954ca210e4b7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -189,7 +189,7 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, #define LINEBUF_SIZE 256 #define NR_ADDITIONAL_LINES 2 -static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) +static int show_one_line(FILE *fp, int l, bool skip, bool show_num) { char buf[LINEBUF_SIZE]; const char *color = PERF_COLOR_BLUE; @@ -198,7 +198,7 @@ static int show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) goto error; if (!skip) { if (show_num) - fprintf(stdout, "%7u %s", l, buf); + fprintf(stdout, "%7d %s", l, buf); else color_fprintf(stdout, color, " %s", buf); } @@ -231,7 +231,7 @@ error: */ int show_line_range(struct line_range *lr) { - unsigned int l = 1; + int l = 1; struct line_node *ln; FILE *fp; int fd, ret; @@ -340,16 +340,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) */ ptr = strchr(arg, ':'); if (ptr) { - lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); + lr->start = (int)strtoul(ptr + 1, &tmp, 0); if (*tmp == '+') - lr->end = lr->start + (unsigned int)strtoul(tmp + 1, - &tmp, 0); + lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); else if (*tmp == '-') - lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); + lr->end = (int)strtoul(tmp + 1, &tmp, 0); else - lr->end = 0; - pr_debug("Line range is %u to %u\n", lr->start, lr->end); - if (lr->end && lr->start > lr->end) { + lr->end = INT_MAX; + pr_debug("Line range is %d to %d\n", lr->start, lr->end); + if (lr->start > lr->end) { semantic_error("Start line must be smaller" " than end line.\n"); return -EINVAL; @@ -360,8 +359,10 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) return -EINVAL; } tmp = strndup(arg, (ptr - arg)); - } else + } else { tmp = strdup(arg); + lr->end = INT_MAX; + } if (tmp == NULL) return -ENOMEM; -- cgit v1.2.2 From dda4ab34fe1905d3d590572b776dd92aa0866558 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 14 Apr 2010 18:39:50 -0400 Subject: perf probe: Fix line range to show end line Line range should reject the range if the number of lines is 0 (e.g. "sched.c:1024+0"), and it should show the lines include the end of line number (e.g. "sched.c:1024-2048" should show 2048th line). LKML-Reference: <20100414223950.14630.42263.stgit@localhost6.localdomain6> Signed-off-by: Masami Hiramatsu Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Frederic Weisbecker Cc: Ingo Molnar Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-event.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 954ca210e4b7..5bf8ab034466 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -290,7 +290,7 @@ int show_line_range(struct line_range *lr) if (lr->end == INT_MAX) lr->end = l + NR_ADDITIONAL_LINES; - while (l < lr->end && !feof(fp) && ret >= 0) + while (l <= lr->end && !feof(fp) && ret >= 0) ret = show_one_line(fp, (l++) - lr->offset, false, false); end: fclose(fp); @@ -341,9 +341,15 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) ptr = strchr(arg, ':'); if (ptr) { lr->start = (int)strtoul(ptr + 1, &tmp, 0); - if (*tmp == '+') + if (*tmp == '+') { lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); - else if (*tmp == '-') + lr->end--; /* + * Adjust the number of lines here. + * If the number of lines == 1, the + * the end of line should be equal to + * the start of line. + */ + } else if (*tmp == '-') lr->end = (int)strtoul(tmp + 1, &tmp, 0); else lr->end = INT_MAX; -- cgit v1.2.2 From a1645ce12adb6c9cc9e19d7695466204e3f017fe Mon Sep 17 00:00:00 2001 From: "Zhang, Yanmin" Date: Mon, 19 Apr 2010 13:32:50 +0800 Subject: perf: 'perf kvm' tool for monitoring guest performance from host Here is the patch of userspace perf tool. Signed-off-by: Zhang Yanmin Signed-off-by: Avi Kivity --- tools/perf/util/probe-event.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/probe-event.c') diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 5bf8ab034466..3967f8f63d0d 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -78,6 +78,7 @@ static struct map *kmaps[MAP__NR_TYPES]; /* Initialize symbol maps and path of vmlinux */ static int init_vmlinux(void) { + struct dso *kernel; int ret; symbol_conf.sort_by_name = true; @@ -91,8 +92,12 @@ static int init_vmlinux(void) goto out; } + kernel = dso__new_kernel(symbol_conf.vmlinux_name); + if (kernel == NULL) + die("Failed to create kernel dso."); + map_groups__init(&kmap_groups); - ret = map_groups__create_kernel_maps(&kmap_groups, kmaps); + ret = __map_groups__create_kernel_maps(&kmap_groups, kmaps, kernel); if (ret < 0) pr_debug("Failed to create kernel maps.\n"); -- cgit v1.2.2