diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/Documentation/perf-probe.txt | 11 | ||||
-rw-r--r-- | tools/perf/builtin-probe.c | 78 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 28 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 898 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 33 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 753 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 1 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 42 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 12 |
9 files changed, 1300 insertions, 556 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index bb671b346774..63c25d304880 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -79,7 +79,16 @@ Probe points are defined by following syntax. 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. -'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). +'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT). + +PROBE ARGUMENT +-------------- +Each probe argument follows below syntax. + + [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] + +'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) +'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. LINE SYNTAX ----------- diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index bfc47fff9c59..c1e54035e8cf 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -40,7 +40,6 @@ #include "util/debug.h" #include "util/debugfs.h" #include "util/parse-options.h" -#include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" #include "util/probe-event.h" @@ -59,23 +58,25 @@ static struct { /* Parse an event definition. Note that any error must die. */ -static void parse_probe_event(const char *str) +static int parse_probe_event(const char *str) { struct perf_probe_event *pev = ¶ms.events[params.nevents]; + int ret; pr_debug("probe-definition(%d): %s\n", params.nevents, str); if (++params.nevents == MAX_PROBES) die("Too many probes (> %d) are specified.", MAX_PROBES); /* Parse a perf-probe command into event */ - parse_perf_probe_command(str, pev); - + ret = parse_perf_probe_command(str, pev); pr_debug("%d arguments\n", pev->nargs); + + return ret; } -static void parse_probe_event_argv(int argc, const char **argv) +static int parse_probe_event_argv(int argc, const char **argv) { - int i, len; + int i, len, ret; char *buf; /* Bind up rest arguments */ @@ -86,16 +87,18 @@ static void parse_probe_event_argv(int argc, const char **argv) len = 0; for (i = 0; i < argc; i++) len += sprintf(&buf[len], "%s ", argv[i]); - parse_probe_event(buf); + ret = parse_probe_event(buf); free(buf); + return ret; } static int opt_add_probe_event(const struct option *opt __used, const char *str, int unset __used) { if (str) - parse_probe_event(str); - return 0; + return parse_probe_event(str); + else + return 0; } static int opt_del_probe_event(const struct option *opt __used, @@ -113,11 +116,14 @@ static int opt_del_probe_event(const struct option *opt __used, static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) { + int ret = 0; + if (str) - parse_line_range_desc(str, ¶ms.line_range); + ret = parse_line_range_desc(str, ¶ms.line_range); INIT_LIST_HEAD(¶ms.line_range.line_list); params.show_lines = true; - return 0; + + return ret; } #endif @@ -142,9 +148,9 @@ static const struct option options[] = { OPT_CALLBACK('a', "add", NULL, #ifdef DWARF_SUPPORT "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" - " [ARG ...]", + " [[NAME=]ARG ...]", #else - "[EVENT=]FUNC[+OFF|%return] [ARG ...]", + "[EVENT=]FUNC[+OFF|%return] [[NAME=]ARG ...]", #endif "probe point definition, where\n" "\t\tGROUP:\tGroup name (optional)\n" @@ -178,6 +184,8 @@ static const struct option options[] = { int cmd_probe(int argc, const char **argv, const char *prefix __used) { + int ret; + argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (argc > 0) { @@ -185,28 +193,31 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) pr_warning(" Error: '-' is not supported.\n"); usage_with_options(probe_usage, options); } - parse_probe_event_argv(argc, argv); + ret = parse_probe_event_argv(argc, argv); + if (ret < 0) { + pr_err(" Error: Parse Error. (%d)\n", ret); + return ret; + } } if ((!params.nevents && !params.dellist && !params.list_events && !params.show_lines)) usage_with_options(probe_usage, options); - if (debugfs_valid_mountpoint(debugfs_path) < 0) - die("Failed to find debugfs path."); - if (params.list_events) { if (params.nevents != 0 || params.dellist) { - pr_warning(" Error: Don't use --list with" - " --add/--del.\n"); + pr_err(" Error: Don't use --list with --add/--del.\n"); usage_with_options(probe_usage, options); } if (params.show_lines) { - pr_warning(" Error: Don't use --list with --line.\n"); + pr_err(" Error: Don't use --list with --line.\n"); usage_with_options(probe_usage, options); } - show_perf_probe_events(); - return 0; + ret = show_perf_probe_events(); + if (ret < 0) + pr_err(" Error: Failed to show event list. (%d)\n", + ret); + return ret; } #ifdef DWARF_SUPPORT @@ -217,19 +228,30 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) usage_with_options(probe_usage, options); } - show_line_range(¶ms.line_range); - return 0; + ret = show_line_range(¶ms.line_range); + if (ret < 0) + pr_err(" Error: Failed to show lines. (%d)\n", ret); + return ret; } #endif if (params.dellist) { - del_perf_probe_events(params.dellist); + ret = del_perf_probe_events(params.dellist); strlist__delete(params.dellist); - if (params.nevents == 0) - return 0; + if (ret < 0) { + pr_err(" Error: Failed to delete events. (%d)\n", ret); + return ret; + } } - add_perf_probe_events(params.events, params.nevents, params.force_add); + if (params.nevents) { + ret = add_perf_probe_events(params.events, params.nevents, + params.force_add); + if (ret < 0) { + pr_err(" Error: Failed to add events. (%d)\n", ret); + return ret; + } + } return 0; } diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 18cf8b321608..9c2b8743cef6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -68,7 +68,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { - cmp = se->cmp(left, right); + cmp = se->se_cmp(left, right); if (cmp) break; } @@ -85,7 +85,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) list_for_each_entry(se, &hist_entry__sort_list, list) { int64_t (*f)(struct hist_entry *, struct hist_entry *); - f = se->collapse ?: se->cmp; + f = se->se_collapse ?: se->se_cmp; cmp = f(left, right); if (cmp) @@ -536,8 +536,8 @@ int hist_entry__snprintf(struct hist_entry *self, continue; ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->snprintf(self, s + ret, size - ret, - se->width ? *se->width : 0); + ret += se->se_snprintf(self, s + ret, size - ret, + se->se_width ? *se->se_width : 0); } return ret; @@ -564,7 +564,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, typeof(*se), list); - left_margin = se->width ? *se->width : 0; + left_margin = se->se_width ? *se->se_width : 0; left_margin -= thread__comm_len(self->thread); } @@ -615,22 +615,22 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, if (se->elide) continue; if (sep) { - fprintf(fp, "%c%s", *sep, se->header); + fprintf(fp, "%c%s", *sep, se->se_header); continue; } - width = strlen(se->header); - if (se->width) { + width = strlen(se->se_header); + if (se->se_width) { if (symbol_conf.col_width_list_str) { if (col_width) { - *se->width = atoi(col_width); + *se->se_width = atoi(col_width); col_width = strchr(col_width, ','); if (col_width) ++col_width; } } - width = *se->width = max(*se->width, width); + width = *se->se_width = max(*se->se_width, width); } - fprintf(fp, " %*s", width, se->header); + fprintf(fp, " %*s", width, se->se_header); } fprintf(fp, "\n"); @@ -652,10 +652,10 @@ size_t perf_session__fprintf_hists(struct rb_root *hists, continue; fprintf(fp, " "); - if (se->width) - width = *se->width; + if (se->se_width) + width = *se->se_width; else - width = strlen(se->header); + width = strlen(se->se_header); for (i = 0; i < width; i++) fprintf(fp, "."); } diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 3fc0be741b8e..5bf8ab034466 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" @@ -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,34 @@ 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) { - pp->function = xstrdup(tp->symbol); + pr_debug("Failed to find corresponding probes from " + "debuginfo. Use kprobe event information.\n"); + pp->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; pp->offset = tp->offset; } pp->retprobe = tp->retprobe; + + return 0; } /* Try to find perf_probe_event with debuginfo */ @@ -131,9 +152,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 +164,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 == -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."); + 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, int l, bool skip, bool show_num) { char buf[LINEBUF_SIZE]; const char *color = PERF_COLOR_BLUE; @@ -174,7 +198,7 @@ static void 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); } @@ -190,34 +214,48 @@ 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; + int l = 1; struct line_node *ln; FILE *fp; 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 +266,70 @@ 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->function = strdup(tp->symbol); + if (pp->function == NULL) + return -ENOMEM; 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; @@ -284,29 +340,45 @@ void 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); - if (*tmp == '+') - lr->end = lr->start + (unsigned int)strtoul(tmp + 1, - &tmp, 0); - else if (*tmp == '-') - lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); + lr->start = (int)strtoul(ptr + 1, &tmp, 0); + if (*tmp == '+') { + lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); + 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 = 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."); - 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); - tmp = xstrndup(arg, (ptr - arg)); - } else - tmp = xstrdup(arg); + return -EINVAL; + } + tmp = strndup(arg, (ptr - arg)); + } else { + tmp = strdup(arg); + lr->end = INT_MAX; + } + + if (tmp == NULL) + return -ENOMEM; if (strchr(tmp, '.')) lr->file = tmp; else lr->function = tmp; + + return 0; } /* Check the name is good for event/group */ @@ -322,7 +394,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,13 +411,18 @@ 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); - pev->event = xstrdup(arg); + "follow C symbol-naming rule.\n", arg); + return -EINVAL; + } + pev->event = strdup(arg); + if (pev->event == NULL) + return -ENOMEM; pev->group = NULL; arg = tmp; } @@ -356,18 +433,24 @@ static void 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, ";:+@%"); @@ -378,131 +461,211 @@ 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."); - pp->file = xstrdup(arg); + if (pp->file) { + semantic_error("SRC@SRC is not allowed.\n"); + return -EINVAL; + } + pp->file = strdup(arg); + if (pp->file == NULL) + return -ENOMEM; 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(const char *str, struct perf_probe_arg *arg) +static int parse_perf_probe_arg(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 = strndup(str, tmp - str); + if (arg->name == NULL) + return -ENOMEM; + pr_debug("name:%s ", arg->name); + str = tmp + 1; + } + + tmp = strchr(str, ':'); + if (tmp) { /* Type setting */ + *tmp = '\0'; + 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->name = xstrdup(str); - pr_debug("%s\n", arg->name); - return; + arg->var = strdup(str); + if (arg->var == NULL) + return -ENOMEM; + pr_debug("%s\n", arg->var); + return 0; } /* Structure fields */ - arg->name = xstrndup(str, tmp - str); - pr_debug("%s, ", arg->name); + arg->var = strndup(str, tmp - str); + if (arg->var == NULL) + return -ENOMEM; + pr_debug("%s, ", arg->var); 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; } 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) { - (*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 = strdup((*fieldp)->name); + if (arg->name == NULL) + return -ENOMEM; + } + 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].name) && pev->point.retprobe) + 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 && + 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 */ @@ -514,14 +677,14 @@ 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; } /* 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; @@ -531,17 +694,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'); @@ -553,19 +724,29 @@ void 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. */ *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: argv_free(argv); + return ret; } /* Compose only probe arg */ @@ -575,7 +756,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; @@ -590,9 +774,20 @@ 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)); + pr_debug("Failed to synthesize perf probe argument: %s", + strerror(-ret)); + return ret; } /* Compose only probe point (not argument) */ @@ -602,7 +797,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) @@ -614,12 +813,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; @@ -636,7 +835,11 @@ 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)); + if (buf) + free(buf); + return NULL; } #if 0 @@ -727,6 +930,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; } @@ -737,7 +947,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, @@ -759,29 +972,44 @@ 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; + 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 */ - 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++) + 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); + pev->args[i].name = strdup(tev->args[i].name); else { - synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); - pev->args[i].name = xstrdup(buf); + ret = synthesize_kprobe_trace_arg(&tev->args[i], + buf, 64); + pev->args[i].name = strdup(buf); } + if (pev->args[i].name == NULL && ret >= 0) + ret = -ENOMEM; + } + + if (ret < 0) + clear_perf_probe_event(pev); + + return ret; } void clear_perf_probe_event(struct perf_probe_event *pev) @@ -803,6 +1031,10 @@ 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); + if (pev->args[i].type) + free(pev->args[i].type); field = pev->args[i].field; while (field) { next = field->next; @@ -833,6 +1065,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; @@ -848,24 +1082,31 @@ 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); - if (ret < 0) - die("Failed to make kprobe_events path."); + __debugfs = debugfs_find_mountpoint(); + if (__debugfs == NULL) { + pr_warning("Debugfs is not mounted.\n"); + return -ENOENT; + } - if (readwrite && !probe_event_dry_run) - ret = open(buf, O_RDWR, O_APPEND); - else - ret = open(buf, O_RDONLY, 0); + 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 + 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; } @@ -891,8 +1132,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); @@ -900,7 +1144,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]; @@ -908,52 +1152,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 */ @@ -963,86 +1226,116 @@ 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++) { tev = &tevs[i]; @@ -1059,12 +1352,21 @@ 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); + 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; /* Add added event name to namelist */ strlist__add(namelist, event); @@ -1086,49 +1388,90 @@ 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, 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.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 = xzalloc(sizeof(struct kprobe_trace_arg) - * tev->nargs); - for (i = 0; i < tev->nargs; i++) - tev->args[i].value = xstrdup(pev->args[i].name); + tev->args = zalloc(sizeof(struct kprobe_trace_arg) + * tev->nargs); + if (tev->args == NULL) { + ret = -ENOMEM; + goto error; + } + for (i = 0; i < tev->nargs; i++) { + 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; + } + } + } } /* 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); + ret = -ENOENT; + goto error; + } - return ntevs; + return 1; +error: + clear_kprobe_trace_event(tev); + free(tev); + *tevs = NULL; + return ret; } struct __event_package { @@ -1137,96 +1480,137 @@ 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); + pkgs = zalloc(sizeof(struct __event_package) * npevs); + if (pkgs == NULL) + return -ENOMEM; /* 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); + str = strdup(ent->s); + if (str == NULL) { + ret = -ENOMEM; + break; + } pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); if (p) { @@ -1238,10 +1622,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; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 9d99fc24c4fc..e7ff0d02c0d4 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref { struct kprobe_trace_arg { char *name; /* Argument name */ char *value; /* Base value */ + char *type; /* Type name */ struct kprobe_trace_arg_ref *ref; /* Referencing offset */ }; @@ -55,6 +56,8 @@ struct perf_probe_arg_field { /* Perf probe probing argument */ struct perf_probe_arg { char *name; /* Argument name */ + char *var; /* Variable name */ + char *type; /* Type name */ struct perf_probe_arg_field *field; /* Structure fields */ }; @@ -71,25 +74,25 @@ struct perf_probe_event { /* Line number container */ struct line_node { struct list_head list; - unsigned int line; + int line; }; /* Line range */ struct line_range { char *file; /* File name */ char *function; /* Function name */ - unsigned int start; /* Start line number */ - unsigned int end; /* End line number */ + int start; /* Start line number */ + int end; /* End line number */ int offset; /* Start line offset */ char *path; /* Real path name */ struct list_head line_list; /* Visible lines */ }; /* Command string to events */ -extern void parse_perf_probe_command(const char *cmd, - struct perf_probe_event *pev); -extern void parse_kprobe_trace_command(const char *cmd, - struct kprobe_trace_event *tev); +extern int parse_perf_probe_command(const char *cmd, + struct perf_probe_event *pev); +extern int parse_kprobe_trace_command(const char *cmd, + struct kprobe_trace_event *tev); /* Events to command string */ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); @@ -101,22 +104,22 @@ extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); /* Convert from kprobe_trace_event to perf_probe_event */ -extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, - struct perf_probe_event *pev); +extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, + struct perf_probe_event *pev); /* Release event contents */ extern void clear_perf_probe_event(struct perf_probe_event *pev); extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); /* Command string to line-range */ -extern void parse_line_range_desc(const char *cmd, struct line_range *lr); +extern int parse_line_range_desc(const char *cmd, struct line_range *lr); -extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, - bool force_add); -extern void del_perf_probe_events(struct strlist *dellist); -extern void show_perf_probe_events(void); -extern void show_line_range(struct line_range *lr); +extern int add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, + bool force_add); +extern int del_perf_probe_events(struct strlist *dellist); +extern int show_perf_probe_events(void); +extern int show_line_range(struct line_range *lr); /* Maximum index number of event-name postfix */ diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index a8513772df08..3e7977560be5 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -84,6 +84,9 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = { #define arch_regs_table x86_32_regs_table #endif +/* Kprobe tracer basic type is up to u64 */ +#define MAX_BASIC_TYPE_BITS 64 + /* Return architecture dependent register string (for kprobe-tracer) */ static const char *get_arch_regstr(unsigned int n) { @@ -108,7 +111,7 @@ static int strtailcmp(const char *s1, const char *s2) /* Line number list operations */ /* Add a line to line number list */ -static void line_list__add_line(struct list_head *head, unsigned int line) +static int line_list__add_line(struct list_head *head, int line) { struct line_node *ln; struct list_head *p; @@ -119,20 +122,23 @@ static void line_list__add_line(struct list_head *head, unsigned int line) p = &ln->list; goto found; } else if (ln->line == line) /* Already exist */ - return ; + return 1; } /* List is empty, or the smallest entry */ p = head; found: pr_debug("line list: add a line %u\n", line); - ln = xzalloc(sizeof(struct line_node)); + ln = zalloc(sizeof(struct line_node)); + if (ln == NULL) + return -ENOMEM; ln->line = line; INIT_LIST_HEAD(&ln->list); list_add(&ln->list, p); + return 0; } /* Check if the line in line number list */ -static int line_list__has_line(struct list_head *head, unsigned int line) +static int line_list__has_line(struct list_head *head, int line) { struct line_node *ln; @@ -193,19 +199,7 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) { const char *name; name = dwarf_diename(dw_die); - DIE_IF(name == NULL); - return strcmp(tname, name); -} - -/* Get entry pc(or low pc, 1st entry of ranges) of the die */ -static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) -{ - Dwarf_Addr epc; - int ret; - - ret = dwarf_entrypc(dw_die, &epc); - DIE_IF(ret == -1); - return epc; + return name ? strcmp(tname, name) : -1; } /* Get type die, but skip qualifiers and typedef */ @@ -230,6 +224,58 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) return die_mem; } +static bool die_is_signed_type(Dwarf_Die *tp_die) +{ + Dwarf_Attribute attr; + Dwarf_Word ret; + + if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || + dwarf_formudata(&attr, &ret) != 0) + return false; + + return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || + ret == DW_ATE_signed_fixed); +} + +static int die_get_byte_size(Dwarf_Die *tp_die) +{ + Dwarf_Attribute attr; + Dwarf_Word ret; + + if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || + dwarf_formudata(&attr, &ret) != 0) + return 0; + + return (int)ret; +} + +/* Get data_member_location offset */ +static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) +{ + Dwarf_Attribute attr; + Dwarf_Op *expr; + size_t nexpr; + int ret; + + if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) + return -ENOENT; + + if (dwarf_formudata(&attr, offs) != 0) { + /* DW_AT_data_member_location should be DW_OP_plus_uconst */ + ret = dwarf_getlocation(&attr, &expr, &nexpr); + if (ret < 0 || nexpr == 0) + return -ENOENT; + + if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { + pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", + expr[0].atom, nexpr); + return -ENOTSUP; + } + *offs = (Dwarf_Word)expr[0].number; + } + return 0; +} + /* Return values for die_find callbacks */ enum { DIE_FIND_CB_FOUND = 0, /* End of Search */ @@ -362,7 +408,7 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, */ /* Show a location */ -static void convert_location(Dwarf_Op *op, struct probe_finder *pf) +static int convert_location(Dwarf_Op *op, struct probe_finder *pf) { unsigned int regn; Dwarf_Word offs = 0; @@ -370,11 +416,13 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) const char *regs; struct kprobe_trace_arg *tvar = pf->tvar; - /* TODO: support CFA */ /* If this is based on frame buffer, set the offset */ if (op->atom == DW_OP_fbreg) { - if (pf->fb_ops == NULL) - die("The attribute of frame base is not supported.\n"); + if (pf->fb_ops == NULL) { + pr_warning("The attribute of frame base is not " + "supported.\n"); + return -ENOTSUP; + } ref = true; offs = op->number; op = &pf->fb_ops[0]; @@ -392,84 +440,155 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) ref = true; } else if (op->atom == DW_OP_regx) { regn = op->number; - } else - die("DW_OP %d is not supported.", op->atom); + } else { + pr_warning("DW_OP %x is not supported.\n", op->atom); + return -ENOTSUP; + } regs = get_arch_regstr(regn); - if (!regs) - die("%u exceeds max register number.", regn); + if (!regs) { + pr_warning("%u exceeds max register number.\n", regn); + return -ERANGE; + } + + tvar->value = strdup(regs); + if (tvar->value == NULL) + return -ENOMEM; - tvar->value = xstrdup(regs); if (ref) { - tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (tvar->ref == NULL) + return -ENOMEM; tvar->ref->offset = (long)offs; } + return 0; +} + +static int convert_variable_type(Dwarf_Die *vr_die, + struct kprobe_trace_arg *targ) +{ + Dwarf_Die type; + char buf[16]; + int ret; + + if (die_get_real_type(vr_die, &type) == NULL) { + pr_warning("Failed to get a type information of %s.\n", + dwarf_diename(vr_die)); + return -ENOENT; + } + + ret = die_get_byte_size(&type) * 8; + if (ret) { + /* Check the bitwidth */ + if (ret > MAX_BASIC_TYPE_BITS) { + pr_info("%s exceeds max-bitwidth." + " Cut down to %d bits.\n", + dwarf_diename(&type), MAX_BASIC_TYPE_BITS); + ret = MAX_BASIC_TYPE_BITS; + } + + ret = snprintf(buf, 16, "%c%d", + die_is_signed_type(&type) ? 's' : 'u', ret); + if (ret < 0 || ret >= 16) { + if (ret >= 16) + ret = -E2BIG; + pr_warning("Failed to convert variable type: %s\n", + strerror(-ret)); + return ret; + } + targ->type = strdup(buf); + if (targ->type == NULL) + return -ENOMEM; + } + return 0; } -static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, +static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, struct perf_probe_arg_field *field, - struct kprobe_trace_arg_ref **ref_ptr) + struct kprobe_trace_arg_ref **ref_ptr, + Dwarf_Die *die_mem) { struct kprobe_trace_arg_ref *ref = *ref_ptr; - Dwarf_Attribute attr; - Dwarf_Die member; Dwarf_Die type; Dwarf_Word offs; + int ret; pr_debug("converting %s in %s\n", field->name, varname); - if (die_get_real_type(vr_die, &type) == NULL) - die("Failed to get a type information of %s.", varname); + if (die_get_real_type(vr_die, &type) == NULL) { + pr_warning("Failed to get the type of %s.\n", varname); + return -ENOENT; + } /* Check the pointer and dereference */ if (dwarf_tag(&type) == DW_TAG_pointer_type) { - if (!field->ref) - die("Semantic error: %s must be referred by '->'", - field->name); + if (!field->ref) { + pr_err("Semantic error: %s must be referred by '->'\n", + field->name); + return -EINVAL; + } /* Get the type pointed by this pointer */ - if (die_get_real_type(&type, &type) == NULL) - die("Failed to get a type information of %s.", varname); - + if (die_get_real_type(&type, &type) == NULL) { + pr_warning("Failed to get the type of %s.\n", varname); + return -ENOENT; + } /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) - die("%s is not a data structure.", varname); + if (dwarf_tag(&type) != DW_TAG_structure_type) { + pr_warning("%s is not a data structure.\n", varname); + return -EINVAL; + } - ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); + ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); + if (ref == NULL) + return -ENOMEM; if (*ref_ptr) (*ref_ptr)->next = ref; else *ref_ptr = ref; } else { /* Verify it is a data structure */ - if (dwarf_tag(&type) != DW_TAG_structure_type) - die("%s is not a data structure.", varname); - - if (field->ref) - die("Semantic error: %s must be referred by '.'", - field->name); - if (!ref) - die("Structure on a register is not supported yet."); + if (dwarf_tag(&type) != DW_TAG_structure_type) { + pr_warning("%s is not a data structure.\n", varname); + return -EINVAL; + } + if (field->ref) { + pr_err("Semantic error: %s must be referred by '.'\n", + field->name); + return -EINVAL; + } + if (!ref) { + pr_warning("Structure on a register is not " + "supported yet.\n"); + return -ENOTSUP; + } } - if (die_find_member(&type, field->name, &member) == NULL) - die("%s(tyep:%s) has no member %s.", varname, - dwarf_diename(&type), field->name); + if (die_find_member(&type, field->name, die_mem) == NULL) { + pr_warning("%s(tyep:%s) has no member %s.\n", varname, + dwarf_diename(&type), field->name); + return -EINVAL; + } /* Get the offset of the field */ - if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || - dwarf_formudata(&attr, &offs) != 0) - die("Failed to get the offset of %s.", field->name); + ret = die_get_data_member_location(die_mem, &offs); + if (ret < 0) { + pr_warning("Failed to get the offset of %s.\n", field->name); + return ret; + } ref->offset += (long)offs; /* Converting next field */ if (field->next) - convert_variable_fields(&member, field->name, field->next, - &ref); + return convert_variable_fields(die_mem, field->name, + field->next, &ref, die_mem); + else + return 0; } /* Show a variables in kprobe event format */ -static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) +static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) { Dwarf_Attribute attr; + Dwarf_Die die_mem; Dwarf_Op *expr; size_t nexpr; int ret; @@ -481,44 +600,74 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) if (ret <= 0 || nexpr == 0) goto error; - convert_location(expr, pf); - - if (pf->pvar->field) - convert_variable_fields(vr_die, pf->pvar->name, - pf->pvar->field, &pf->tvar->ref); + ret = convert_location(expr, pf); + if (ret == 0 && pf->pvar->field) { + ret = convert_variable_fields(vr_die, pf->pvar->var, + pf->pvar->field, &pf->tvar->ref, + &die_mem); + vr_die = &die_mem; + } + if (ret == 0) { + if (pf->pvar->type) { + pf->tvar->type = strdup(pf->pvar->type); + if (pf->tvar->type == NULL) + ret = -ENOMEM; + } else + ret = convert_variable_type(vr_die, pf->tvar); + } /* *expr will be cached in libdw. Don't free it. */ - return ; + return ret; error: /* TODO: Support const_value */ - die("Failed to find the location of %s at this address.\n" - " Perhaps, it has been optimized out.", pf->pvar->name); + pr_err("Failed to find the location of %s at this address.\n" + " Perhaps, it has been optimized out.\n", pf->pvar->var); + return -ENOENT; } /* Find a variable in a subprogram die */ -static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) +static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Die vr_die; - char buf[128]; + char buf[32], *ptr; + int ret; + + /* TODO: Support arrays */ + if (pf->pvar->name) + pf->tvar->name = strdup(pf->pvar->name); + else { + ret = synthesize_perf_probe_arg(pf->pvar, buf, 32); + if (ret < 0) + return ret; + ptr = strchr(buf, ':'); /* Change type separator to _ */ + if (ptr) + *ptr = '_'; + pf->tvar->name = strdup(buf); + } + if (pf->tvar->name == NULL) + return -ENOMEM; - /* TODO: Support struct members and arrays */ - if (!is_c_varname(pf->pvar->name)) { + if (!is_c_varname(pf->pvar->var)) { /* Copy raw parameters */ - pf->tvar->value = xstrdup(pf->pvar->name); - } else { - synthesize_perf_probe_arg(pf->pvar, buf, 128); - pf->tvar->name = xstrdup(buf); - pr_debug("Searching '%s' variable in context.\n", - pf->pvar->name); - /* Search child die for local variables and parameters. */ - if (!die_find_variable(sp_die, pf->pvar->name, &vr_die)) - die("Failed to find '%s' in this function.", - pf->pvar->name); - convert_variable(&vr_die, pf); + pf->tvar->value = strdup(pf->pvar->var); + if (pf->tvar->value == NULL) + return -ENOMEM; + else + return 0; + } + + pr_debug("Searching '%s' variable in context.\n", + pf->pvar->var); + /* Search child die for local variables and parameters. */ + if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { + pr_warning("Failed to find '%s' in this function.\n", + pf->pvar->var); + return -ENOENT; } + return convert_variable(&vr_die, pf); } /* Show a probe point to output buffer */ -static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) +static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) { struct kprobe_trace_event *tev; Dwarf_Addr eaddr; @@ -528,23 +677,34 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Attribute fb_attr; size_t nops; - if (pf->ntevs == MAX_PROBES) - die("Too many( > %d) probe point found.\n", MAX_PROBES); + if (pf->ntevs == MAX_PROBES) { + pr_warning("Too many( > %d) probe point found.\n", MAX_PROBES); + return -ERANGE; + } tev = &pf->tevs[pf->ntevs++]; /* If no real subprogram, find a real one */ if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { sp_die = die_find_real_subprogram(&pf->cu_die, pf->addr, &die_mem); - if (!sp_die) - die("Probe point is not found in subprograms."); + if (!sp_die) { + pr_warning("Failed to find probe point in any " + "functions.\n"); + return -ENOENT; + } } /* Copy the name of probe point */ name = dwarf_diename(sp_die); if (name) { - dwarf_entrypc(sp_die, &eaddr); - tev->point.symbol = xstrdup(name); + if (dwarf_entrypc(sp_die, &eaddr) != 0) { + pr_warning("Failed to get entry pc of %s\n", + dwarf_diename(sp_die)); + return -ENOENT; + } + tev->point.symbol = strdup(name); + if (tev->point.symbol == NULL) + return -ENOMEM; tev->point.offset = (unsigned long)(pf->addr - eaddr); } else /* This function has no name. */ @@ -556,55 +716,74 @@ static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) /* Get the frame base attribute/ops */ dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); - if (ret <= 0 || nops == 0) + if (ret <= 0 || nops == 0) { pf->fb_ops = NULL; + } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && + pf->cfi != NULL) { + Dwarf_Frame *frame; + if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || + dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { + pr_warning("Failed to get CFA on 0x%jx\n", + (uintmax_t)pf->addr); + return -ENOENT; + } + } /* Find each argument */ - /* TODO: use dwarf_cfi_addrframe */ tev->nargs = pf->pev->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) + return -ENOMEM; for (i = 0; i < pf->pev->nargs; i++) { pf->pvar = &pf->pev->args[i]; pf->tvar = &tev->args[i]; - find_variable(sp_die, pf); + ret = find_variable(sp_die, pf); + if (ret != 0) + return ret; } /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; + return 0; } /* Find probe point from its line number */ -static void find_probe_point_by_line(struct probe_finder *pf) +static int find_probe_point_by_line(struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; int lineno; - int ret; + int ret = 0; - ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); + if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } - for (i = 0; i < nlines; i++) { + for (i = 0; i < nlines && ret == 0; i++) { line = dwarf_onesrcline(lines, i); - dwarf_lineno(line, &lineno); - if (lineno != pf->lno) + if (dwarf_lineno(line, &lineno) != 0 || + lineno != pf->lno) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); + if (dwarf_lineaddr(line, &addr) != 0) { + pr_warning("Failed to get the address of the line.\n"); + return -ENOENT; + } pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", (int)i, lineno, (uintmax_t)addr); pf->addr = addr; - convert_probe_point(NULL, pf); + ret = convert_probe_point(NULL, pf); /* Continuing, because target line might be inlined. */ } + return ret; } /* Find lines which match lazy pattern */ @@ -612,15 +791,27 @@ static int find_lazy_match_lines(struct list_head *head, const char *fname, const char *pat) { char *fbuf, *p1, *p2; - int fd, line, nlines = 0; + int fd, ret, line, nlines = 0; struct stat st; fd = open(fname, O_RDONLY); - if (fd < 0) - die("failed to open %s", fname); - DIE_IF(fstat(fd, &st) < 0); + if (fd < 0) { + pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); + return fd; + } + + ret = fstat(fd, &st); + if (ret < 0) { + pr_warning("Failed to get the size of %s: %s\n", + fname, strerror(errno)); + return ret; + } fbuf = xmalloc(st.st_size + 2); - DIE_IF(read(fd, fbuf, st.st_size) < 0); + ret = read(fd, fbuf, st.st_size); + if (ret < 0) { + pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); + return ret; + } close(fd); fbuf[st.st_size] = '\n'; /* Dummy line */ fbuf[st.st_size + 1] = '\0'; @@ -640,7 +831,7 @@ static int find_lazy_match_lines(struct list_head *head, } /* Find probe points from lazy pattern */ -static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) +static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) { Dwarf_Lines *lines; Dwarf_Line *line; @@ -648,31 +839,40 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) Dwarf_Addr addr; Dwarf_Die die_mem; int lineno; - int ret; + int ret = 0; if (list_empty(&pf->lcache)) { /* Matching lazy line pattern */ ret = find_lazy_match_lines(&pf->lcache, pf->fname, pf->pev->point.lazy_line); - if (ret <= 0) - die("No matched lines found in %s.", pf->fname); + if (ret == 0) { + pr_debug("No matched lines found in %s.\n", pf->fname); + return 0; + } else if (ret < 0) + return ret; } - ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); - for (i = 0; i < nlines; i++) { + if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } + + for (i = 0; i < nlines && ret >= 0; i++) { line = dwarf_onesrcline(lines, i); - dwarf_lineno(line, &lineno); - if (!line_list__has_line(&pf->lcache, lineno)) + if (dwarf_lineno(line, &lineno) != 0 || + !line_list__has_line(&pf->lcache, lineno)) continue; /* TODO: Get fileno from line, but how? */ if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) continue; - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); + if (dwarf_lineaddr(line, &addr) != 0) { + pr_debug("Failed to get the address of line %d.\n", + lineno); + continue; + } if (sp_die) { /* Address filtering 1: does sp_die include addr? */ if (!dwarf_haspc(sp_die, addr)) @@ -686,27 +886,42 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) (int)i, lineno, (unsigned long long)addr); pf->addr = addr; - convert_probe_point(sp_die, pf); + ret = convert_probe_point(sp_die, pf); /* Continuing, because target line might be inlined. */ } /* TODO: deallocate lines, but how? */ + return ret; } +/* Callback parameter with return value */ +struct dwarf_callback_param { + void *data; + int retval; +}; + static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) { - struct probe_finder *pf = (struct probe_finder *)data; + struct dwarf_callback_param *param = data; + struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; + Dwarf_Addr addr; if (pp->lazy_line) - find_probe_point_lazy(in_die, pf); + param->retval = find_probe_point_lazy(in_die, pf); else { /* Get probe address */ - pf->addr = die_get_entrypc(in_die); + if (dwarf_entrypc(in_die, &addr) != 0) { + pr_warning("Failed to get entry pc of %s.\n", + dwarf_diename(in_die)); + param->retval = -ENOENT; + return DWARF_CB_ABORT; + } + pf->addr = addr; pf->addr += pp->offset; pr_debug("found inline addr: 0x%jx\n", (uintmax_t)pf->addr); - convert_probe_point(in_die, pf); + param->retval = convert_probe_point(in_die, pf); } return DWARF_CB_OK; @@ -715,39 +930,53 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) /* Search function from function name */ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) { - struct probe_finder *pf = (struct probe_finder *)data; + struct dwarf_callback_param *param = data; + struct probe_finder *pf = param->data; struct perf_probe_point *pp = &pf->pev->point; /* Check tag and diename */ if (dwarf_tag(sp_die) != DW_TAG_subprogram || die_compare_name(sp_die, pp->function) != 0) - return 0; + return DWARF_CB_OK; pf->fname = dwarf_decl_file(sp_die); if (pp->line) { /* Function relative line */ dwarf_decl_line(sp_die, &pf->lno); pf->lno += pp->line; - find_probe_point_by_line(pf); + param->retval = find_probe_point_by_line(pf); } else if (!dwarf_func_inline(sp_die)) { /* Real function */ if (pp->lazy_line) - find_probe_point_lazy(sp_die, pf); + param->retval = find_probe_point_lazy(sp_die, pf); else { - pf->addr = die_get_entrypc(sp_die); + if (dwarf_entrypc(sp_die, &pf->addr) != 0) { + pr_warning("Failed to get entry pc of %s.\n", + dwarf_diename(sp_die)); + param->retval = -ENOENT; + return DWARF_CB_ABORT; + } pf->addr += pp->offset; /* TODO: Check the address in this function */ - convert_probe_point(sp_die, pf); + param->retval = convert_probe_point(sp_die, pf); } - } else + } else { + struct dwarf_callback_param _param = {.data = (void *)pf, + .retval = 0}; /* Inlined function: search instances */ - dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); + dwarf_func_inline_instances(sp_die, probe_point_inline_cb, + &_param); + param->retval = _param.retval; + } - return 1; /* Exit; no same symbol in this CU. */ + return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ } -static void find_probe_point_by_func(struct probe_finder *pf) +static int find_probe_point_by_func(struct probe_finder *pf) { - dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); + struct dwarf_callback_param _param = {.data = (void *)pf, + .retval = 0}; + dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0); + return _param.retval; } /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ @@ -760,19 +989,29 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; + int ret = 0; - pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); + if (pf.tevs == NULL) + return -ENOMEM; *tevs = pf.tevs; pf.ntevs = 0; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -ENOENT; + if (!dbg) { + pr_warning("No dwarf info found in the vmlinux - " + "please rebuild with CONFIG_DEBUG_INFO=y.\n"); + return -EBADF; + } + + /* Get the call frame information from this dwarf */ + pf.cfi = dwarf_getcfi(dbg); off = 0; line_list__init(&pf.lcache); /* Loop on CUs (Compilation Unit) */ - while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { + while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && + ret >= 0) { /* Get the DIE(Debugging Information Entry) of this CU */ diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); if (!diep) @@ -786,12 +1025,12 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, if (!pp->file || pf.fname) { if (pp->function) - find_probe_point_by_func(&pf); + ret = find_probe_point_by_func(&pf); else if (pp->lazy_line) - find_probe_point_lazy(NULL, &pf); + ret = find_probe_point_lazy(NULL, &pf); else { pf.lno = pp->line; - find_probe_point_by_line(&pf); + ret = find_probe_point_by_line(&pf); } } off = noff; @@ -799,7 +1038,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, line_list__free(&pf.lcache); dwarf_end(dbg); - return pf.ntevs; + return (ret < 0) ? ret : pf.ntevs; } /* Reverse search */ @@ -812,10 +1051,11 @@ int find_perf_probe_point(int fd, unsigned long addr, Dwarf_Addr laddr, eaddr; const char *tmp; int lineno, ret = 0; + bool found = false; dbg = dwarf_begin(fd, DWARF_C_READ); if (!dbg) - return -ENOENT; + return -EBADF; /* Find cu die */ if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { @@ -826,82 +1066,135 @@ int find_perf_probe_point(int fd, unsigned long addr, /* Find a corresponding line */ line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); if (line) { - dwarf_lineaddr(line, &laddr); - if ((Dwarf_Addr)addr == laddr) { - dwarf_lineno(line, &lineno); - ppt->line = lineno; - + if (dwarf_lineaddr(line, &laddr) == 0 && + (Dwarf_Addr)addr == laddr && + dwarf_lineno(line, &lineno) == 0) { tmp = dwarf_linesrc(line, NULL, NULL); - DIE_IF(!tmp); - ppt->file = xstrdup(tmp); - ret = 1; + if (tmp) { + ppt->line = lineno; + ppt->file = strdup(tmp); + if (ppt->file == NULL) { + ret = -ENOMEM; + goto end; + } + found = true; + } } } /* Find a corresponding function */ if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { tmp = dwarf_diename(&spdie); - if (!tmp) + if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) goto end; - dwarf_entrypc(&spdie, &eaddr); - if (!lineno) { - /* We don't have a line number, let's use offset */ - ppt->function = xstrdup(tmp); - ppt->offset = addr - (unsigned long)eaddr; - ret = 1; - goto end; + if (ppt->line) { + if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, + &indie)) { + /* addr in an inline function */ + tmp = dwarf_diename(&indie); + if (!tmp) + goto end; + ret = dwarf_decl_line(&indie, &lineno); + } else { + if (eaddr == addr) { /* Function entry */ + lineno = ppt->line; + ret = 0; + } else + ret = dwarf_decl_line(&spdie, &lineno); + } + if (ret == 0) { + /* Make a relative line number */ + ppt->line -= lineno; + goto found; + } } - if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { - /* addr in an inline function */ - tmp = dwarf_diename(&indie); - if (!tmp) - goto end; - dwarf_decl_line(&indie, &lineno); - } else { - if (eaddr == addr) /* No offset: function entry */ - lineno = ppt->line; - else - dwarf_decl_line(&spdie, &lineno); + /* We don't have a line number, let's use offset */ + ppt->offset = addr - (unsigned long)eaddr; +found: + ppt->function = strdup(tmp); + if (ppt->function == NULL) { + ret = -ENOMEM; + goto end; } - ppt->function = xstrdup(tmp); - ppt->line -= lineno; /* Make a relative line number */ + found = true; } end: dwarf_end(dbg); + if (ret >= 0) + ret = found ? 1 : 0; return ret; } +/* Add a line and store the src path */ +static int line_range_add_line(const char *src, unsigned int lineno, + struct line_range *lr) +{ + /* Copy real path */ + if (!lr->path) { + lr->path = strdup(src); + if (lr->path == NULL) + return -ENOMEM; + } + return line_list__add_line(&lr->line_list, lineno); +} + +/* Search function declaration lines */ +static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) +{ + struct dwarf_callback_param *param = data; + struct line_finder *lf = param->data; + const char *src; + int lineno; + + src = dwarf_decl_file(sp_die); + if (src && strtailcmp(src, lf->fname) != 0) + return DWARF_CB_OK; + + if (dwarf_decl_line(sp_die, &lineno) != 0 || + (lf->lno_s > lineno || lf->lno_e < lineno)) + return DWARF_CB_OK; + + param->retval = line_range_add_line(src, lineno, lf->lr); + return DWARF_CB_OK; +} + +static int find_line_range_func_decl_lines(struct line_finder *lf) +{ + struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; + dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); + return param.retval; +} /* Find line range from its line number */ -static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) +static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) { Dwarf_Lines *lines; Dwarf_Line *line; size_t nlines, i; Dwarf_Addr addr; - int lineno; - int ret; + int lineno, ret = 0; const char *src; Dwarf_Die die_mem; line_list__init(&lf->lr->line_list); - ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); - DIE_IF(ret != 0); + if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { + pr_warning("No source lines found in this CU.\n"); + return -ENOENT; + } + /* Search probable lines on lines list */ for (i = 0; i < nlines; i++) { line = dwarf_onesrcline(lines, i); - ret = dwarf_lineno(line, &lineno); - DIE_IF(ret != 0); - if (lf->lno_s > lineno || lf->lno_e < lineno) + if (dwarf_lineno(line, &lineno) != 0 || + (lf->lno_s > lineno || lf->lno_e < lineno)) continue; if (sp_die) { /* Address filtering 1: does sp_die include addr? */ - ret = dwarf_lineaddr(line, &addr); - DIE_IF(ret != 0); - if (!dwarf_haspc(sp_die, addr)) + if (dwarf_lineaddr(line, &addr) != 0 || + !dwarf_haspc(sp_die, addr)) continue; /* Address filtering 2: No child include addr? */ @@ -914,30 +1207,49 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) if (strtailcmp(src, lf->fname) != 0) continue; - /* Copy real path */ - if (!lf->lr->path) - lf->lr->path = xstrdup(src); - line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); + ret = line_range_add_line(src, lineno, lf->lr); + if (ret < 0) + return ret; } + + /* + * Dwarf lines doesn't include function declarations. We have to + * check functions list or given function. + */ + if (sp_die) { + src = dwarf_decl_file(sp_die); + if (src && dwarf_decl_line(sp_die, &lineno) == 0 && + (lf->lno_s <= lineno && lf->lno_e >= lineno)) + ret = line_range_add_line(src, lineno, lf->lr); + } else + ret = find_line_range_func_decl_lines(lf); + /* Update status */ - if (!list_empty(&lf->lr->line_list)) - lf->found = 1; + if (ret >= 0) + if (!list_empty(&lf->lr->line_list)) + ret = lf->found = 1; + else + ret = 0; /* Lines are not found */ else { free(lf->lr->path); lf->lr->path = NULL; } + return ret; } static int line_range_inline_cb(Dwarf_Die *in_die, void *data) { - find_line_range_by_line(in_die, (struct line_finder *)data); + struct dwarf_callback_param *param = data; + + param->retval = find_line_range_by_line(in_die, param->data); return DWARF_CB_ABORT; /* No need to find other instances */ } /* Search function from function name */ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) { - struct line_finder *lf = (struct line_finder *)data; + struct dwarf_callback_param *param = data; + struct line_finder *lf = param->data; struct line_range *lr = lf->lr; if (dwarf_tag(sp_die) == DW_TAG_subprogram && @@ -946,44 +1258,55 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) dwarf_decl_line(sp_die, &lr->offset); pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); lf->lno_s = lr->offset + lr->start; - if (!lr->end) + if (lf->lno_s < 0) /* Overflow */ + lf->lno_s = INT_MAX; + lf->lno_e = lr->offset + lr->end; + if (lf->lno_e < 0) /* Overflow */ lf->lno_e = INT_MAX; - else - lf->lno_e = lr->offset + lr->end; + pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); lr->start = lf->lno_s; lr->end = lf->lno_e; - if (dwarf_func_inline(sp_die)) + if (dwarf_func_inline(sp_die)) { + struct dwarf_callback_param _param; + _param.data = (void *)lf; + _param.retval = 0; dwarf_func_inline_instances(sp_die, - line_range_inline_cb, lf); - else - find_line_range_by_line(sp_die, lf); - return 1; + line_range_inline_cb, + &_param); + param->retval = _param.retval; + } else + param->retval = find_line_range_by_line(sp_die, lf); + return DWARF_CB_ABORT; } - return 0; + return DWARF_CB_OK; } -static void find_line_range_by_func(struct line_finder *lf) +static int find_line_range_by_func(struct line_finder *lf) { - dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); + struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; + dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0); + return param.retval; } int find_line_range(int fd, struct line_range *lr) { struct line_finder lf = {.lr = lr, .found = 0}; - int ret; + int ret = 0; Dwarf_Off off = 0, noff; size_t cuhl; Dwarf_Die *diep; Dwarf *dbg; dbg = dwarf_begin(fd, DWARF_C_READ); - if (!dbg) - return -ENOENT; + if (!dbg) { + pr_warning("No dwarf info found in the vmlinux - " + "please rebuild with CONFIG_DEBUG_INFO=y.\n"); + return -EBADF; + } /* Loop on CUs (Compilation Unit) */ - while (!lf.found) { - ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); - if (ret != 0) + while (!lf.found && ret >= 0) { + if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) break; /* Get the DIE(Debugging Information Entry) of this CU */ @@ -999,20 +1322,18 @@ int find_line_range(int fd, struct line_range *lr) if (!lr->file || lf.fname) { if (lr->function) - find_line_range_by_func(&lf); + ret = find_line_range_by_func(&lf); else { lf.lno_s = lr->start; - if (!lr->end) - lf.lno_e = INT_MAX; - else - lf.lno_e = lr->end; - find_line_range_by_line(NULL, &lf); + lf.lno_e = lr->end; + ret = find_line_range_by_line(NULL, &lf); } } off = noff; } pr_debug("path: %lx\n", (unsigned long)lr->path); dwarf_end(dbg); - return lf.found; + + return (ret < 0) ? ret : lf.found; } diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 2a271321944f..310ce897229c 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -42,6 +42,7 @@ struct probe_finder { struct list_head lcache; /* Line cache for lazy match */ /* For variable searching */ + Dwarf_CFI *cfi; /* Call Frame Information */ Dwarf_Op *fb_ops; /* Frame base attribute */ struct perf_probe_arg *pvar; /* Current target variable */ struct kprobe_trace_arg *tvar; /* Current result variable */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9d24d4b2c8fb..da30b305fba0 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -30,38 +30,38 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, size_t size, unsigned int width); struct sort_entry sort_thread = { - .header = "Command: Pid", - .cmp = sort__thread_cmp, - .snprintf = hist_entry__thread_snprintf, - .width = &threads__col_width, + .se_header = "Command: Pid", + .se_cmp = sort__thread_cmp, + .se_snprintf = hist_entry__thread_snprintf, + .se_width = &threads__col_width, }; struct sort_entry sort_comm = { - .header = "Command", - .cmp = sort__comm_cmp, - .collapse = sort__comm_collapse, - .snprintf = hist_entry__comm_snprintf, - .width = &comms__col_width, + .se_header = "Command", + .se_cmp = sort__comm_cmp, + .se_collapse = sort__comm_collapse, + .se_snprintf = hist_entry__comm_snprintf, + .se_width = &comms__col_width, }; struct sort_entry sort_dso = { - .header = "Shared Object", - .cmp = sort__dso_cmp, - .snprintf = hist_entry__dso_snprintf, - .width = &dsos__col_width, + .se_header = "Shared Object", + .se_cmp = sort__dso_cmp, + .se_snprintf = hist_entry__dso_snprintf, + .se_width = &dsos__col_width, }; struct sort_entry sort_sym = { - .header = "Symbol", - .cmp = sort__sym_cmp, - .snprintf = hist_entry__sym_snprintf, + .se_header = "Symbol", + .se_cmp = sort__sym_cmp, + .se_snprintf = hist_entry__sym_snprintf, }; struct sort_entry sort_parent = { - .header = "Parent symbol", - .cmp = sort__parent_cmp, - .snprintf = hist_entry__parent_snprintf, - .width = &parent_symbol__col_width, + .se_header = "Parent symbol", + .se_cmp = sort__parent_cmp, + .se_snprintf = hist_entry__parent_snprintf, + .se_width = &parent_symbol__col_width, }; struct sort_dimension { @@ -255,7 +255,7 @@ int sort_dimension__add(const char *tok) if (strncasecmp(tok, sd->name, strlen(tok))) continue; - if (sd->entry->collapse) + if (sd->entry->se_collapse) sort__need_collapse = 1; if (sd->entry == &sort_parent) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6d7b4be70609..1d857aa2c01f 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -78,13 +78,13 @@ enum sort_type { struct sort_entry { struct list_head list; - const char *header; + const char *se_header; - int64_t (*cmp)(struct hist_entry *, struct hist_entry *); - int64_t (*collapse)(struct hist_entry *, struct hist_entry *); - int (*snprintf)(struct hist_entry *self, char *bf, size_t size, - unsigned int width); - unsigned int *width; + int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); + int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); + int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, + unsigned int width); + unsigned int *se_width; bool elide; }; |