diff options
Diffstat (limited to 'tools/perf/util/probe-event.c')
-rw-r--r-- | tools/perf/util/probe-event.c | 524 |
1 files changed, 387 insertions, 137 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index fcc16e4349df..f0223166e761 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | ||
34 | 35 | ||
35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
36 | #include "util.h" | 37 | #include "util.h" |
@@ -74,10 +75,9 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
74 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); | 75 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); |
75 | static struct machine machine; | 76 | static struct machine machine; |
76 | 77 | ||
77 | /* Initialize symbol maps and path of vmlinux */ | 78 | /* Initialize symbol maps and path of vmlinux/modules */ |
78 | static int init_vmlinux(void) | 79 | static int init_vmlinux(void) |
79 | { | 80 | { |
80 | struct dso *kernel; | ||
81 | int ret; | 81 | int ret; |
82 | 82 | ||
83 | symbol_conf.sort_by_name = true; | 83 | symbol_conf.sort_by_name = true; |
@@ -91,33 +91,95 @@ static int init_vmlinux(void) | |||
91 | goto out; | 91 | goto out; |
92 | } | 92 | } |
93 | 93 | ||
94 | ret = machine__init(&machine, "/", 0); | 94 | ret = machine__init(&machine, "", HOST_KERNEL_ID); |
95 | if (ret < 0) | 95 | if (ret < 0) |
96 | goto out; | 96 | goto out; |
97 | 97 | ||
98 | kernel = dso__new_kernel(symbol_conf.vmlinux_name); | 98 | if (machine__create_kernel_maps(&machine) < 0) { |
99 | if (kernel == NULL) | 99 | pr_debug("machine__create_kernel_maps() failed.\n"); |
100 | die("Failed to create kernel dso."); | 100 | goto out; |
101 | 101 | } | |
102 | ret = __machine__create_kernel_maps(&machine, kernel); | ||
103 | if (ret < 0) | ||
104 | pr_debug("Failed to create kernel maps.\n"); | ||
105 | |||
106 | out: | 102 | out: |
107 | if (ret < 0) | 103 | if (ret < 0) |
108 | pr_warning("Failed to init vmlinux path.\n"); | 104 | pr_warning("Failed to init vmlinux path.\n"); |
109 | return ret; | 105 | return ret; |
110 | } | 106 | } |
111 | 107 | ||
108 | static struct symbol *__find_kernel_function_by_name(const char *name, | ||
109 | struct map **mapp) | ||
110 | { | ||
111 | return machine__find_kernel_function_by_name(&machine, name, mapp, | ||
112 | NULL); | ||
113 | } | ||
114 | |||
115 | static struct map *kernel_get_module_map(const char *module) | ||
116 | { | ||
117 | struct rb_node *nd; | ||
118 | struct map_groups *grp = &machine.kmaps; | ||
119 | |||
120 | if (!module) | ||
121 | module = "kernel"; | ||
122 | |||
123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
125 | if (strncmp(pos->dso->short_name + 1, module, | ||
126 | pos->dso->short_name_len - 2) == 0) { | ||
127 | return pos; | ||
128 | } | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dso *kernel_get_module_dso(const char *module) | ||
134 | { | ||
135 | struct dso *dso; | ||
136 | struct map *map; | ||
137 | const char *vmlinux_name; | ||
138 | |||
139 | if (module) { | ||
140 | list_for_each_entry(dso, &machine.kernel_dsos, node) { | ||
141 | if (strncmp(dso->short_name + 1, module, | ||
142 | dso->short_name_len - 2) == 0) | ||
143 | goto found; | ||
144 | } | ||
145 | pr_debug("Failed to find module %s.\n", module); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | map = machine.vmlinux_maps[MAP__FUNCTION]; | ||
150 | dso = map->dso; | ||
151 | |||
152 | vmlinux_name = symbol_conf.vmlinux_name; | ||
153 | if (vmlinux_name) { | ||
154 | if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) | ||
155 | return NULL; | ||
156 | } else { | ||
157 | if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { | ||
158 | pr_debug("Failed to load kernel map.\n"); | ||
159 | return NULL; | ||
160 | } | ||
161 | } | ||
162 | found: | ||
163 | return dso; | ||
164 | } | ||
165 | |||
166 | const char *kernel_get_module_path(const char *module) | ||
167 | { | ||
168 | struct dso *dso = kernel_get_module_dso(module); | ||
169 | return (dso) ? dso->long_name : NULL; | ||
170 | } | ||
171 | |||
112 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
113 | static int open_vmlinux(void) | 173 | static int open_vmlinux(const char *module) |
114 | { | 174 | { |
115 | if (map__load(machine.vmlinux_maps[MAP__FUNCTION], NULL) < 0) { | 175 | const char *path = kernel_get_module_path(module); |
116 | pr_debug("Failed to load kernel map.\n"); | 176 | if (!path) { |
117 | return -EINVAL; | 177 | pr_err("Failed to find path of %s module.\n", |
178 | module ?: "kernel"); | ||
179 | return -ENOENT; | ||
118 | } | 180 | } |
119 | pr_debug("Try to open %s\n", machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name); | 181 | pr_debug("Try to open %s\n", path); |
120 | return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | 182 | return open(path, O_RDONLY); |
121 | } | 183 | } |
122 | 184 | ||
123 | /* | 185 | /* |
@@ -125,20 +187,19 @@ static int open_vmlinux(void) | |||
125 | * Currently only handles kprobes. | 187 | * Currently only handles kprobes. |
126 | */ | 188 | */ |
127 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 189 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
128 | struct perf_probe_point *pp) | 190 | struct perf_probe_point *pp) |
129 | { | 191 | { |
130 | struct symbol *sym; | 192 | struct symbol *sym; |
131 | int fd, ret = -ENOENT; | 193 | struct map *map; |
194 | u64 addr; | ||
195 | int ret = -ENOENT; | ||
132 | 196 | ||
133 | sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], | 197 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
134 | tp->symbol, NULL); | ||
135 | if (sym) { | 198 | if (sym) { |
136 | fd = open_vmlinux(); | 199 | addr = map->unmap_ip(map, sym->start + tp->offset); |
137 | if (fd >= 0) { | 200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
138 | ret = find_perf_probe_point(fd, | 201 | tp->offset, addr); |
139 | sym->start + tp->offset, pp); | 202 | ret = find_perf_probe_point((unsigned long)addr, pp); |
140 | close(fd); | ||
141 | } | ||
142 | } | 203 | } |
143 | if (ret <= 0) { | 204 | if (ret <= 0) { |
144 | pr_debug("Failed to find corresponding probes from " | 205 | pr_debug("Failed to find corresponding probes from " |
@@ -156,12 +217,12 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
156 | /* Try to find perf_probe_event with debuginfo */ | 217 | /* Try to find perf_probe_event with debuginfo */ |
157 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 218 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
158 | struct probe_trace_event **tevs, | 219 | struct probe_trace_event **tevs, |
159 | int max_tevs) | 220 | int max_tevs, const char *module) |
160 | { | 221 | { |
161 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 222 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
162 | int fd, ntevs; | 223 | int fd, ntevs; |
163 | 224 | ||
164 | fd = open_vmlinux(); | 225 | fd = open_vmlinux(module); |
165 | if (fd < 0) { | 226 | if (fd < 0) { |
166 | if (need_dwarf) { | 227 | if (need_dwarf) { |
167 | pr_warning("Failed to open debuginfo file.\n"); | 228 | pr_warning("Failed to open debuginfo file.\n"); |
@@ -173,7 +234,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
173 | 234 | ||
174 | /* Searching trace events corresponding to probe event */ | 235 | /* Searching trace events corresponding to probe event */ |
175 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); |
176 | close(fd); | ||
177 | 237 | ||
178 | if (ntevs > 0) { /* Succeeded to find trace events */ | 238 | if (ntevs > 0) { /* Succeeded to find trace events */ |
179 | pr_debug("find %d probe_trace_events.\n", ntevs); | 239 | pr_debug("find %d probe_trace_events.\n", ntevs); |
@@ -191,7 +251,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
191 | pr_warning("Warning: No dwarf info found in the vmlinux - " | 251 | pr_warning("Warning: No dwarf info found in the vmlinux - " |
192 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); | 252 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); |
193 | if (!need_dwarf) { | 253 | if (!need_dwarf) { |
194 | pr_debug("Trying to use symbols.\nn"); | 254 | pr_debug("Trying to use symbols.\n"); |
195 | return 0; | 255 | return 0; |
196 | } | 256 | } |
197 | } | 257 | } |
@@ -260,47 +320,54 @@ static int get_real_path(const char *raw_path, const char *comp_dir, | |||
260 | #define LINEBUF_SIZE 256 | 320 | #define LINEBUF_SIZE 256 |
261 | #define NR_ADDITIONAL_LINES 2 | 321 | #define NR_ADDITIONAL_LINES 2 |
262 | 322 | ||
263 | static int show_one_line(FILE *fp, int l, bool skip, bool show_num) | 323 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
264 | { | 324 | { |
265 | char buf[LINEBUF_SIZE]; | 325 | char buf[LINEBUF_SIZE]; |
266 | const char *color = PERF_COLOR_BLUE; | 326 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
267 | 327 | const char *prefix = NULL; | |
268 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
269 | goto error; | ||
270 | if (!skip) { | ||
271 | if (show_num) | ||
272 | fprintf(stdout, "%7d %s", l, buf); | ||
273 | else | ||
274 | color_fprintf(stdout, color, " %s", buf); | ||
275 | } | ||
276 | 328 | ||
277 | while (strlen(buf) == LINEBUF_SIZE - 1 && | 329 | do { |
278 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
279 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | 330 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) |
280 | goto error; | 331 | goto error; |
281 | if (!skip) { | 332 | if (skip) |
282 | if (show_num) | 333 | continue; |
283 | fprintf(stdout, "%s", buf); | 334 | if (!prefix) { |
284 | else | 335 | prefix = show_num ? "%7d " : " "; |
285 | color_fprintf(stdout, color, "%s", buf); | 336 | color_fprintf(stdout, color, prefix, l); |
286 | } | 337 | } |
287 | } | 338 | color_fprintf(stdout, color, "%s", buf); |
288 | 339 | ||
289 | return 0; | 340 | } while (strchr(buf, '\n') == NULL); |
341 | |||
342 | return 1; | ||
290 | error: | 343 | error: |
291 | if (feof(fp)) | 344 | if (ferror(fp)) { |
292 | pr_warning("Source file is shorter than expected.\n"); | ||
293 | else | ||
294 | pr_warning("File read error: %s\n", strerror(errno)); | 345 | pr_warning("File read error: %s\n", strerror(errno)); |
346 | return -1; | ||
347 | } | ||
348 | return 0; | ||
349 | } | ||
295 | 350 | ||
296 | return -1; | 351 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) |
352 | { | ||
353 | int rv = __show_one_line(fp, l, skip, show_num); | ||
354 | if (rv == 0) { | ||
355 | pr_warning("Source file is shorter than expected.\n"); | ||
356 | rv = -1; | ||
357 | } | ||
358 | return rv; | ||
297 | } | 359 | } |
298 | 360 | ||
361 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) | ||
362 | #define show_one_line(f,l) _show_one_line(f,l,false,false) | ||
363 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) | ||
364 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) | ||
365 | |||
299 | /* | 366 | /* |
300 | * Show line-range always requires debuginfo to find source file and | 367 | * Show line-range always requires debuginfo to find source file and |
301 | * line number. | 368 | * line number. |
302 | */ | 369 | */ |
303 | int show_line_range(struct line_range *lr) | 370 | int show_line_range(struct line_range *lr, const char *module) |
304 | { | 371 | { |
305 | int l = 1; | 372 | int l = 1; |
306 | struct line_node *ln; | 373 | struct line_node *ln; |
@@ -313,14 +380,13 @@ int show_line_range(struct line_range *lr) | |||
313 | if (ret < 0) | 380 | if (ret < 0) |
314 | return ret; | 381 | return ret; |
315 | 382 | ||
316 | fd = open_vmlinux(); | 383 | fd = open_vmlinux(module); |
317 | if (fd < 0) { | 384 | if (fd < 0) { |
318 | pr_warning("Failed to open debuginfo file.\n"); | 385 | pr_warning("Failed to open debuginfo file.\n"); |
319 | return fd; | 386 | return fd; |
320 | } | 387 | } |
321 | 388 | ||
322 | ret = find_line_range(fd, lr); | 389 | ret = find_line_range(fd, lr); |
323 | close(fd); | ||
324 | if (ret == 0) { | 390 | if (ret == 0) { |
325 | pr_warning("Specified source line is not found.\n"); | 391 | pr_warning("Specified source line is not found.\n"); |
326 | return -ENOENT; | 392 | return -ENOENT; |
@@ -341,10 +407,10 @@ int show_line_range(struct line_range *lr) | |||
341 | setup_pager(); | 407 | setup_pager(); |
342 | 408 | ||
343 | if (lr->function) | 409 | if (lr->function) |
344 | fprintf(stdout, "<%s:%d>\n", lr->function, | 410 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
345 | lr->start - lr->offset); | 411 | lr->start - lr->offset); |
346 | else | 412 | else |
347 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | 413 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
348 | 414 | ||
349 | fp = fopen(lr->path, "r"); | 415 | fp = fopen(lr->path, "r"); |
350 | if (fp == NULL) { | 416 | if (fp == NULL) { |
@@ -353,36 +419,124 @@ int show_line_range(struct line_range *lr) | |||
353 | return -errno; | 419 | return -errno; |
354 | } | 420 | } |
355 | /* Skip to starting line number */ | 421 | /* Skip to starting line number */ |
356 | while (l < lr->start && ret >= 0) | 422 | while (l < lr->start) { |
357 | ret = show_one_line(fp, l++, true, false); | 423 | ret = skip_one_line(fp, l++); |
358 | if (ret < 0) | 424 | if (ret < 0) |
359 | goto end; | 425 | goto end; |
426 | } | ||
360 | 427 | ||
361 | list_for_each_entry(ln, &lr->line_list, list) { | 428 | list_for_each_entry(ln, &lr->line_list, list) { |
362 | while (ln->line > l && ret >= 0) | 429 | for (; ln->line > l; l++) { |
363 | ret = show_one_line(fp, (l++) - lr->offset, | 430 | ret = show_one_line(fp, l - lr->offset); |
364 | false, false); | 431 | if (ret < 0) |
365 | if (ret >= 0) | 432 | goto end; |
366 | ret = show_one_line(fp, (l++) - lr->offset, | 433 | } |
367 | false, true); | 434 | ret = show_one_line_with_num(fp, l++ - lr->offset); |
368 | if (ret < 0) | 435 | if (ret < 0) |
369 | goto end; | 436 | goto end; |
370 | } | 437 | } |
371 | 438 | ||
372 | if (lr->end == INT_MAX) | 439 | if (lr->end == INT_MAX) |
373 | lr->end = l + NR_ADDITIONAL_LINES; | 440 | lr->end = l + NR_ADDITIONAL_LINES; |
374 | while (l <= lr->end && !feof(fp) && ret >= 0) | 441 | while (l <= lr->end) { |
375 | ret = show_one_line(fp, (l++) - lr->offset, false, false); | 442 | ret = show_one_line_or_eof(fp, l++ - lr->offset); |
443 | if (ret <= 0) | ||
444 | break; | ||
445 | } | ||
376 | end: | 446 | end: |
377 | fclose(fp); | 447 | fclose(fp); |
378 | return ret; | 448 | return ret; |
379 | } | 449 | } |
380 | 450 | ||
451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | ||
452 | int max_vls, struct strfilter *_filter, | ||
453 | bool externs) | ||
454 | { | ||
455 | char *buf; | ||
456 | int ret, i, nvars; | ||
457 | struct str_node *node; | ||
458 | struct variable_list *vls = NULL, *vl; | ||
459 | const char *var; | ||
460 | |||
461 | buf = synthesize_perf_probe_point(&pev->point); | ||
462 | if (!buf) | ||
463 | return -EINVAL; | ||
464 | pr_debug("Searching variables at %s\n", buf); | ||
465 | |||
466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | ||
467 | if (ret <= 0) { | ||
468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | ||
469 | goto end; | ||
470 | } | ||
471 | /* Some variables are found */ | ||
472 | fprintf(stdout, "Available variables at %s\n", buf); | ||
473 | for (i = 0; i < ret; i++) { | ||
474 | vl = &vls[i]; | ||
475 | /* | ||
476 | * A probe point might be converted to | ||
477 | * several trace points. | ||
478 | */ | ||
479 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | ||
480 | vl->point.offset); | ||
481 | free(vl->point.symbol); | ||
482 | nvars = 0; | ||
483 | if (vl->vars) { | ||
484 | strlist__for_each(node, vl->vars) { | ||
485 | var = strchr(node->s, '\t') + 1; | ||
486 | if (strfilter__compare(_filter, var)) { | ||
487 | fprintf(stdout, "\t\t%s\n", node->s); | ||
488 | nvars++; | ||
489 | } | ||
490 | } | ||
491 | strlist__delete(vl->vars); | ||
492 | } | ||
493 | if (nvars == 0) | ||
494 | fprintf(stdout, "\t\t(No matched variables)\n"); | ||
495 | } | ||
496 | free(vls); | ||
497 | end: | ||
498 | free(buf); | ||
499 | return ret; | ||
500 | } | ||
501 | |||
502 | /* Show available variables on given probe point */ | ||
503 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | ||
504 | int max_vls, const char *module, | ||
505 | struct strfilter *_filter, bool externs) | ||
506 | { | ||
507 | int i, fd, ret = 0; | ||
508 | |||
509 | ret = init_vmlinux(); | ||
510 | if (ret < 0) | ||
511 | return ret; | ||
512 | |||
513 | setup_pager(); | ||
514 | |||
515 | for (i = 0; i < npevs && ret >= 0; i++) { | ||
516 | fd = open_vmlinux(module); | ||
517 | if (fd < 0) { | ||
518 | pr_warning("Failed to open debug information file.\n"); | ||
519 | ret = fd; | ||
520 | break; | ||
521 | } | ||
522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
523 | externs); | ||
524 | } | ||
525 | return ret; | ||
526 | } | ||
527 | |||
381 | #else /* !DWARF_SUPPORT */ | 528 | #else /* !DWARF_SUPPORT */ |
382 | 529 | ||
383 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 530 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
384 | struct perf_probe_point *pp) | 531 | struct perf_probe_point *pp) |
385 | { | 532 | { |
533 | struct symbol *sym; | ||
534 | |||
535 | sym = __find_kernel_function_by_name(tp->symbol, NULL); | ||
536 | if (!sym) { | ||
537 | pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); | ||
538 | return -ENOENT; | ||
539 | } | ||
386 | pp->function = strdup(tp->symbol); | 540 | pp->function = strdup(tp->symbol); |
387 | if (pp->function == NULL) | 541 | if (pp->function == NULL) |
388 | return -ENOMEM; | 542 | return -ENOMEM; |
@@ -394,7 +548,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
394 | 548 | ||
395 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 549 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
396 | struct probe_trace_event **tevs __unused, | 550 | struct probe_trace_event **tevs __unused, |
397 | int max_tevs __unused) | 551 | int max_tevs __unused, const char *mod __unused) |
398 | { | 552 | { |
399 | if (perf_probe_event_need_dwarf(pev)) { | 553 | if (perf_probe_event_need_dwarf(pev)) { |
400 | pr_warning("Debuginfo-analysis is not supported.\n"); | 554 | pr_warning("Debuginfo-analysis is not supported.\n"); |
@@ -403,64 +557,113 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
403 | return 0; | 557 | return 0; |
404 | } | 558 | } |
405 | 559 | ||
406 | int show_line_range(struct line_range *lr __unused) | 560 | int show_line_range(struct line_range *lr __unused, const char *module __unused) |
407 | { | 561 | { |
408 | pr_warning("Debuginfo-analysis is not supported.\n"); | 562 | pr_warning("Debuginfo-analysis is not supported.\n"); |
409 | return -ENOSYS; | 563 | return -ENOSYS; |
410 | } | 564 | } |
411 | 565 | ||
566 | int show_available_vars(struct perf_probe_event *pevs __unused, | ||
567 | int npevs __unused, int max_vls __unused, | ||
568 | const char *module __unused, | ||
569 | struct strfilter *filter __unused, | ||
570 | bool externs __unused) | ||
571 | { | ||
572 | pr_warning("Debuginfo-analysis is not supported.\n"); | ||
573 | return -ENOSYS; | ||
574 | } | ||
412 | #endif | 575 | #endif |
413 | 576 | ||
577 | static int parse_line_num(char **ptr, int *val, const char *what) | ||
578 | { | ||
579 | const char *start = *ptr; | ||
580 | |||
581 | errno = 0; | ||
582 | *val = strtol(*ptr, ptr, 0); | ||
583 | if (errno || *ptr == start) { | ||
584 | semantic_error("'%s' is not a valid number.\n", what); | ||
585 | return -EINVAL; | ||
586 | } | ||
587 | return 0; | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * Stuff 'lr' according to the line range described by 'arg'. | ||
592 | * The line range syntax is described by: | ||
593 | * | ||
594 | * SRC[:SLN[+NUM|-ELN]] | ||
595 | * FNC[@SRC][:SLN[+NUM|-ELN]] | ||
596 | */ | ||
414 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 597 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
415 | { | 598 | { |
416 | const char *ptr; | 599 | char *range, *file, *name = strdup(arg); |
417 | char *tmp; | 600 | int err; |
418 | /* | 601 | |
419 | * <Syntax> | 602 | if (!name) |
420 | * SRC:SLN[+NUM|-ELN] | 603 | return -ENOMEM; |
421 | * FUNC[:SLN[+NUM|-ELN]] | 604 | |
422 | */ | 605 | lr->start = 0; |
423 | ptr = strchr(arg, ':'); | 606 | lr->end = INT_MAX; |
424 | if (ptr) { | 607 | |
425 | lr->start = (int)strtoul(ptr + 1, &tmp, 0); | 608 | range = strchr(name, ':'); |
426 | if (*tmp == '+') { | 609 | if (range) { |
427 | lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); | 610 | *range++ = '\0'; |
428 | lr->end--; /* | 611 | |
429 | * Adjust the number of lines here. | 612 | err = parse_line_num(&range, &lr->start, "start line"); |
430 | * If the number of lines == 1, the | 613 | if (err) |
431 | * the end of line should be equal to | 614 | goto err; |
432 | * the start of line. | 615 | |
433 | */ | 616 | if (*range == '+' || *range == '-') { |
434 | } else if (*tmp == '-') | 617 | const char c = *range++; |
435 | lr->end = (int)strtoul(tmp + 1, &tmp, 0); | 618 | |
436 | else | 619 | err = parse_line_num(&range, &lr->end, "end line"); |
437 | lr->end = INT_MAX; | 620 | if (err) |
621 | goto err; | ||
622 | |||
623 | if (c == '+') { | ||
624 | lr->end += lr->start; | ||
625 | /* | ||
626 | * Adjust the number of lines here. | ||
627 | * If the number of lines == 1, the | ||
628 | * the end of line should be equal to | ||
629 | * the start of line. | ||
630 | */ | ||
631 | lr->end--; | ||
632 | } | ||
633 | } | ||
634 | |||
438 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); | 635 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); |
636 | |||
637 | err = -EINVAL; | ||
439 | if (lr->start > lr->end) { | 638 | if (lr->start > lr->end) { |
440 | semantic_error("Start line must be smaller" | 639 | semantic_error("Start line must be smaller" |
441 | " than end line.\n"); | 640 | " than end line.\n"); |
442 | return -EINVAL; | 641 | goto err; |
443 | } | 642 | } |
444 | if (*tmp != '\0') { | 643 | if (*range != '\0') { |
445 | semantic_error("Tailing with invalid character '%d'.\n", | 644 | semantic_error("Tailing with invalid str '%s'.\n", range); |
446 | *tmp); | 645 | goto err; |
447 | return -EINVAL; | ||
448 | } | 646 | } |
449 | tmp = strndup(arg, (ptr - arg)); | ||
450 | } else { | ||
451 | tmp = strdup(arg); | ||
452 | lr->end = INT_MAX; | ||
453 | } | 647 | } |
454 | 648 | ||
455 | if (tmp == NULL) | 649 | file = strchr(name, '@'); |
456 | return -ENOMEM; | 650 | if (file) { |
457 | 651 | *file = '\0'; | |
458 | if (strchr(tmp, '.')) | 652 | lr->file = strdup(++file); |
459 | lr->file = tmp; | 653 | if (lr->file == NULL) { |
654 | err = -ENOMEM; | ||
655 | goto err; | ||
656 | } | ||
657 | lr->function = name; | ||
658 | } else if (strchr(name, '.')) | ||
659 | lr->file = name; | ||
460 | else | 660 | else |
461 | lr->function = tmp; | 661 | lr->function = name; |
462 | 662 | ||
463 | return 0; | 663 | return 0; |
664 | err: | ||
665 | free(name); | ||
666 | return err; | ||
464 | } | 667 | } |
465 | 668 | ||
466 | /* Check the name is good for event/group */ | 669 | /* Check the name is good for event/group */ |
@@ -584,39 +787,40 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
584 | 787 | ||
585 | /* Exclusion check */ | 788 | /* Exclusion check */ |
586 | if (pp->lazy_line && pp->line) { | 789 | if (pp->lazy_line && pp->line) { |
587 | semantic_error("Lazy pattern can't be used with line number."); | 790 | semantic_error("Lazy pattern can't be used with" |
791 | " line number.\n"); | ||
588 | return -EINVAL; | 792 | return -EINVAL; |
589 | } | 793 | } |
590 | 794 | ||
591 | if (pp->lazy_line && pp->offset) { | 795 | if (pp->lazy_line && pp->offset) { |
592 | semantic_error("Lazy pattern can't be used with offset."); | 796 | semantic_error("Lazy pattern can't be used with offset.\n"); |
593 | return -EINVAL; | 797 | return -EINVAL; |
594 | } | 798 | } |
595 | 799 | ||
596 | if (pp->line && pp->offset) { | 800 | if (pp->line && pp->offset) { |
597 | semantic_error("Offset can't be used with line number."); | 801 | semantic_error("Offset can't be used with line number.\n"); |
598 | return -EINVAL; | 802 | return -EINVAL; |
599 | } | 803 | } |
600 | 804 | ||
601 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { | 805 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { |
602 | semantic_error("File always requires line number or " | 806 | semantic_error("File always requires line number or " |
603 | "lazy pattern."); | 807 | "lazy pattern.\n"); |
604 | return -EINVAL; | 808 | return -EINVAL; |
605 | } | 809 | } |
606 | 810 | ||
607 | if (pp->offset && !pp->function) { | 811 | if (pp->offset && !pp->function) { |
608 | semantic_error("Offset requires an entry function."); | 812 | semantic_error("Offset requires an entry function.\n"); |
609 | return -EINVAL; | 813 | return -EINVAL; |
610 | } | 814 | } |
611 | 815 | ||
612 | if (pp->retprobe && !pp->function) { | 816 | if (pp->retprobe && !pp->function) { |
613 | semantic_error("Return probe requires an entry function."); | 817 | semantic_error("Return probe requires an entry function.\n"); |
614 | return -EINVAL; | 818 | return -EINVAL; |
615 | } | 819 | } |
616 | 820 | ||
617 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { | 821 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { |
618 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 822 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
619 | "return probe."); | 823 | "return probe.\n"); |
620 | return -EINVAL; | 824 | return -EINVAL; |
621 | } | 825 | } |
622 | 826 | ||
@@ -890,7 +1094,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
890 | 1094 | ||
891 | return tmp - buf; | 1095 | return tmp - buf; |
892 | error: | 1096 | error: |
893 | pr_debug("Failed to synthesize perf probe argument: %s", | 1097 | pr_debug("Failed to synthesize perf probe argument: %s\n", |
894 | strerror(-ret)); | 1098 | strerror(-ret)); |
895 | return ret; | 1099 | return ret; |
896 | } | 1100 | } |
@@ -918,13 +1122,13 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
918 | goto error; | 1122 | goto error; |
919 | } | 1123 | } |
920 | if (pp->file) { | 1124 | if (pp->file) { |
921 | len = strlen(pp->file) - 31; | 1125 | tmp = pp->file; |
922 | if (len < 0) | 1126 | len = strlen(tmp); |
923 | len = 0; | 1127 | if (len > 30) { |
924 | tmp = strchr(pp->file + len, '/'); | 1128 | tmp = strchr(pp->file + len - 30, '/'); |
925 | if (!tmp) | 1129 | tmp = tmp ? tmp + 1 : pp->file + len - 30; |
926 | tmp = pp->file + len; | 1130 | } |
927 | ret = e_snprintf(file, 32, "@%s", tmp + 1); | 1131 | ret = e_snprintf(file, 32, "@%s", tmp); |
928 | if (ret <= 0) | 1132 | if (ret <= 0) |
929 | goto error; | 1133 | goto error; |
930 | } | 1134 | } |
@@ -940,7 +1144,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
940 | 1144 | ||
941 | return buf; | 1145 | return buf; |
942 | error: | 1146 | error: |
943 | pr_debug("Failed to synthesize perf probe point: %s", | 1147 | pr_debug("Failed to synthesize perf probe point: %s\n", |
944 | strerror(-ret)); | 1148 | strerror(-ret)); |
945 | if (buf) | 1149 | if (buf) |
946 | free(buf); | 1150 | free(buf); |
@@ -1087,7 +1291,7 @@ error: | |||
1087 | } | 1291 | } |
1088 | 1292 | ||
1089 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, | 1293 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
1090 | struct perf_probe_event *pev) | 1294 | struct perf_probe_event *pev) |
1091 | { | 1295 | { |
1092 | char buf[64] = ""; | 1296 | char buf[64] = ""; |
1093 | int i, ret; | 1297 | int i, ret; |
@@ -1516,14 +1720,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1516 | 1720 | ||
1517 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 1721 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1518 | struct probe_trace_event **tevs, | 1722 | struct probe_trace_event **tevs, |
1519 | int max_tevs) | 1723 | int max_tevs, const char *module) |
1520 | { | 1724 | { |
1521 | struct symbol *sym; | 1725 | struct symbol *sym; |
1522 | int ret = 0, i; | 1726 | int ret = 0, i; |
1523 | struct probe_trace_event *tev; | 1727 | struct probe_trace_event *tev; |
1524 | 1728 | ||
1525 | /* Convert perf_probe_event with debuginfo */ | 1729 | /* Convert perf_probe_event with debuginfo */ |
1526 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); | 1730 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
1527 | if (ret != 0) | 1731 | if (ret != 0) |
1528 | return ret; | 1732 | return ret; |
1529 | 1733 | ||
@@ -1572,8 +1776,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1572 | } | 1776 | } |
1573 | 1777 | ||
1574 | /* Currently just checking function name from symbol map */ | 1778 | /* Currently just checking function name from symbol map */ |
1575 | sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION], | 1779 | sym = __find_kernel_function_by_name(tev->point.symbol, NULL); |
1576 | tev->point.symbol, NULL); | ||
1577 | if (!sym) { | 1780 | if (!sym) { |
1578 | pr_warning("Kernel symbol \'%s\' not found.\n", | 1781 | pr_warning("Kernel symbol \'%s\' not found.\n", |
1579 | tev->point.symbol); | 1782 | tev->point.symbol); |
@@ -1596,7 +1799,7 @@ struct __event_package { | |||
1596 | }; | 1799 | }; |
1597 | 1800 | ||
1598 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 1801 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
1599 | bool force_add, int max_tevs) | 1802 | int max_tevs, const char *module, bool force_add) |
1600 | { | 1803 | { |
1601 | int i, j, ret; | 1804 | int i, j, ret; |
1602 | struct __event_package *pkgs; | 1805 | struct __event_package *pkgs; |
@@ -1617,16 +1820,21 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1617 | pkgs[i].pev = &pevs[i]; | 1820 | pkgs[i].pev = &pevs[i]; |
1618 | /* Convert with or without debuginfo */ | 1821 | /* Convert with or without debuginfo */ |
1619 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 1822 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1620 | &pkgs[i].tevs, max_tevs); | 1823 | &pkgs[i].tevs, |
1824 | max_tevs, | ||
1825 | module); | ||
1621 | if (ret < 0) | 1826 | if (ret < 0) |
1622 | goto end; | 1827 | goto end; |
1623 | pkgs[i].ntevs = ret; | 1828 | pkgs[i].ntevs = ret; |
1624 | } | 1829 | } |
1625 | 1830 | ||
1626 | /* Loop 2: add all events */ | 1831 | /* Loop 2: add all events */ |
1627 | for (i = 0; i < npevs && ret >= 0; i++) | 1832 | for (i = 0; i < npevs; i++) { |
1628 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1833 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1629 | pkgs[i].ntevs, force_add); | 1834 | pkgs[i].ntevs, force_add); |
1835 | if (ret < 0) | ||
1836 | break; | ||
1837 | } | ||
1630 | end: | 1838 | end: |
1631 | /* Loop 3: cleanup and free trace events */ | 1839 | /* Loop 3: cleanup and free trace events */ |
1632 | for (i = 0; i < npevs; i++) { | 1840 | for (i = 0; i < npevs; i++) { |
@@ -1680,7 +1888,7 @@ static int del_trace_probe_event(int fd, const char *group, | |||
1680 | 1888 | ||
1681 | ret = e_snprintf(buf, 128, "%s:%s", group, event); | 1889 | ret = e_snprintf(buf, 128, "%s:%s", group, event); |
1682 | if (ret < 0) { | 1890 | if (ret < 0) { |
1683 | pr_err("Failed to copy event."); | 1891 | pr_err("Failed to copy event.\n"); |
1684 | return ret; | 1892 | return ret; |
1685 | } | 1893 | } |
1686 | 1894 | ||
@@ -1752,4 +1960,46 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1752 | 1960 | ||
1753 | return ret; | 1961 | return ret; |
1754 | } | 1962 | } |
1963 | /* TODO: don't use a global variable for filter ... */ | ||
1964 | static struct strfilter *available_func_filter; | ||
1965 | |||
1966 | /* | ||
1967 | * If a symbol corresponds to a function with global binding and | ||
1968 | * matches filter return 0. For all others return 1. | ||
1969 | */ | ||
1970 | static int filter_available_functions(struct map *map __unused, | ||
1971 | struct symbol *sym) | ||
1972 | { | ||
1973 | if (sym->binding == STB_GLOBAL && | ||
1974 | strfilter__compare(available_func_filter, sym->name)) | ||
1975 | return 0; | ||
1976 | return 1; | ||
1977 | } | ||
1755 | 1978 | ||
1979 | int show_available_funcs(const char *module, struct strfilter *_filter) | ||
1980 | { | ||
1981 | struct map *map; | ||
1982 | int ret; | ||
1983 | |||
1984 | setup_pager(); | ||
1985 | |||
1986 | ret = init_vmlinux(); | ||
1987 | if (ret < 0) | ||
1988 | return ret; | ||
1989 | |||
1990 | map = kernel_get_module_map(module); | ||
1991 | if (!map) { | ||
1992 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1993 | return -EINVAL; | ||
1994 | } | ||
1995 | available_func_filter = _filter; | ||
1996 | if (map__load(map, filter_available_functions)) { | ||
1997 | pr_err("Failed to load map.\n"); | ||
1998 | return -EINVAL; | ||
1999 | } | ||
2000 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
2001 | dso__sort_by_name(map->dso, map->type); | ||
2002 | |||
2003 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
2004 | return 0; | ||
2005 | } | ||