diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2010-03-16 18:06:26 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-17 07:11:15 -0400 |
commit | 7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 (patch) | |
tree | 1cccbf0d4239ebdd9c0f67762b5a2f0facbe53dc /tools | |
parent | fb1587d869a399554220e166d4b90b581a8ade01 (diff) |
perf probe: Add data structure member access support
Support accessing members in the data structures. With this,
perf-probe accepts data-structure members(IOW, it now accepts
dot '.' and arrow '->' operators) as probe arguemnts.
e.g.
./perf probe --add 'schedule:44 rq->curr'
./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry'
Note that '>' can be interpreted as redirection in command-line.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <20100316220626.32050.57552.stgit@localhost6.localdomain6>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/util/probe-event.c | 90 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 12 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 105 |
3 files changed, 201 insertions, 6 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 4e3c1aea7892..64dea6c3d58a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -262,6 +262,49 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
262 | pp->lazy_line); | 262 | pp->lazy_line); |
263 | } | 263 | } |
264 | 264 | ||
265 | /* Parse perf-probe event argument */ | ||
266 | static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) | ||
267 | { | ||
268 | const char *tmp; | ||
269 | struct perf_probe_arg_field **fieldp; | ||
270 | |||
271 | pr_debug("parsing arg: %s into ", str); | ||
272 | |||
273 | tmp = strpbrk(str, "-."); | ||
274 | if (!is_c_varname(str) || !tmp) { | ||
275 | /* A variable, register, symbol or special value */ | ||
276 | arg->name = xstrdup(str); | ||
277 | pr_debug("%s\n", arg->name); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | /* Structure fields */ | ||
282 | arg->name = xstrndup(str, tmp - str); | ||
283 | pr_debug("%s, ", arg->name); | ||
284 | fieldp = &arg->field; | ||
285 | |||
286 | do { | ||
287 | *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); | ||
288 | if (*tmp == '.') { | ||
289 | str = tmp + 1; | ||
290 | (*fieldp)->ref = false; | ||
291 | } else if (tmp[1] == '>') { | ||
292 | str = tmp + 2; | ||
293 | (*fieldp)->ref = true; | ||
294 | } else | ||
295 | semantic_error("Argument parse error: %s", str); | ||
296 | |||
297 | tmp = strpbrk(str, "-."); | ||
298 | if (tmp) { | ||
299 | (*fieldp)->name = xstrndup(str, tmp - str); | ||
300 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | ||
301 | fieldp = &(*fieldp)->next; | ||
302 | } | ||
303 | } while (tmp); | ||
304 | (*fieldp)->name = xstrdup(str); | ||
305 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | ||
306 | } | ||
307 | |||
265 | /* Parse perf-probe event command */ | 308 | /* Parse perf-probe event command */ |
266 | void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) | 309 | void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) |
267 | { | 310 | { |
@@ -281,7 +324,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) | |||
281 | pev->nargs = argc - 1; | 324 | pev->nargs = argc - 1; |
282 | pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); | 325 | pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
283 | for (i = 0; i < pev->nargs; i++) { | 326 | for (i = 0; i < pev->nargs; i++) { |
284 | pev->args[i].name = xstrdup(argv[i + 1]); | 327 | parse_perf_probe_arg(argv[i + 1], &pev->args[i]); |
285 | if (is_c_varname(pev->args[i].name) && pev->point.retprobe) | 328 | if (is_c_varname(pev->args[i].name) && pev->point.retprobe) |
286 | semantic_error("You can't specify local variable for" | 329 | semantic_error("You can't specify local variable for" |
287 | " kretprobe"); | 330 | " kretprobe"); |
@@ -353,6 +396,33 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) | |||
353 | argv_free(argv); | 396 | argv_free(argv); |
354 | } | 397 | } |
355 | 398 | ||
399 | /* Compose only probe arg */ | ||
400 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | ||
401 | { | ||
402 | struct perf_probe_arg_field *field = pa->field; | ||
403 | int ret; | ||
404 | char *tmp = buf; | ||
405 | |||
406 | ret = e_snprintf(tmp, len, "%s", pa->name); | ||
407 | if (ret <= 0) | ||
408 | goto error; | ||
409 | tmp += ret; | ||
410 | len -= ret; | ||
411 | |||
412 | while (field) { | ||
413 | ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", | ||
414 | field->name); | ||
415 | if (ret <= 0) | ||
416 | goto error; | ||
417 | tmp += ret; | ||
418 | len -= ret; | ||
419 | field = field->next; | ||
420 | } | ||
421 | return tmp - buf; | ||
422 | error: | ||
423 | die("Failed to synthesize perf probe argument: %s", strerror(-ret)); | ||
424 | } | ||
425 | |||
356 | /* Compose only probe point (not argument) */ | 426 | /* Compose only probe point (not argument) */ |
357 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | 427 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) |
358 | { | 428 | { |
@@ -563,6 +633,7 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev, | |||
563 | void clear_perf_probe_event(struct perf_probe_event *pev) | 633 | void clear_perf_probe_event(struct perf_probe_event *pev) |
564 | { | 634 | { |
565 | struct perf_probe_point *pp = &pev->point; | 635 | struct perf_probe_point *pp = &pev->point; |
636 | struct perf_probe_arg_field *field, *next; | ||
566 | int i; | 637 | int i; |
567 | 638 | ||
568 | if (pev->event) | 639 | if (pev->event) |
@@ -575,9 +646,18 @@ void clear_perf_probe_event(struct perf_probe_event *pev) | |||
575 | free(pp->function); | 646 | free(pp->function); |
576 | if (pp->lazy_line) | 647 | if (pp->lazy_line) |
577 | free(pp->lazy_line); | 648 | free(pp->lazy_line); |
578 | for (i = 0; i < pev->nargs; i++) | 649 | for (i = 0; i < pev->nargs; i++) { |
579 | if (pev->args[i].name) | 650 | if (pev->args[i].name) |
580 | free(pev->args[i].name); | 651 | free(pev->args[i].name); |
652 | field = pev->args[i].field; | ||
653 | while (field) { | ||
654 | next = field->next; | ||
655 | if (field->name) | ||
656 | free(field->name); | ||
657 | free(field); | ||
658 | field = next; | ||
659 | } | ||
660 | } | ||
581 | if (pev->args) | 661 | if (pev->args) |
582 | free(pev->args); | 662 | free(pev->args); |
583 | memset(pev, 0, sizeof(*pev)); | 663 | memset(pev, 0, sizeof(*pev)); |
@@ -682,8 +762,10 @@ static void show_perf_probe_event(struct perf_probe_event *pev) | |||
682 | 762 | ||
683 | if (pev->nargs > 0) { | 763 | if (pev->nargs > 0) { |
684 | printf(" with"); | 764 | printf(" with"); |
685 | for (i = 0; i < pev->nargs; i++) | 765 | for (i = 0; i < pev->nargs; i++) { |
686 | printf(" %s", pev->args[i].name); | 766 | synthesize_perf_probe_arg(&pev->args[i], buf, 128); |
767 | printf(" %s", buf); | ||
768 | } | ||
687 | } | 769 | } |
688 | printf(")\n"); | 770 | printf(")\n"); |
689 | free(place); | 771 | free(place); |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 2a2f0a26dc67..cd308b0a4d96 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -45,9 +45,17 @@ struct perf_probe_point { | |||
45 | bool retprobe; /* Return probe flag */ | 45 | bool retprobe; /* Return probe flag */ |
46 | }; | 46 | }; |
47 | 47 | ||
48 | /* Perf probe probing argument field chain */ | ||
49 | struct perf_probe_arg_field { | ||
50 | struct perf_probe_arg_field *next; /* Next field */ | ||
51 | char *name; /* Name of the field */ | ||
52 | bool ref; /* Referencing flag */ | ||
53 | }; | ||
54 | |||
48 | /* Perf probe probing argument */ | 55 | /* Perf probe probing argument */ |
49 | struct perf_probe_arg { | 56 | struct perf_probe_arg { |
50 | char *name; /* Argument name */ | 57 | char *name; /* Argument name */ |
58 | struct perf_probe_arg_field *field; /* Structure fields */ | ||
51 | }; | 59 | }; |
52 | 60 | ||
53 | /* Perf probe probing event (point + arg) */ | 61 | /* Perf probe probing event (point + arg) */ |
@@ -86,6 +94,8 @@ extern void parse_kprobe_trace_command(const char *cmd, | |||
86 | /* Events to command string */ | 94 | /* Events to command string */ |
87 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | 95 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); |
88 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); | 96 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); |
97 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, | ||
98 | size_t len); | ||
89 | 99 | ||
90 | /* Check the perf_probe_event needs debuginfo */ | 100 | /* Check the perf_probe_event needs debuginfo */ |
91 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); | 101 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index e02b60770485..db52ec2e84de 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -206,6 +206,28 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | |||
206 | return epc; | 206 | return epc; |
207 | } | 207 | } |
208 | 208 | ||
209 | /* Get type die, but skip qualifiers and typedef */ | ||
210 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
211 | { | ||
212 | Dwarf_Attribute attr; | ||
213 | int tag; | ||
214 | |||
215 | do { | ||
216 | if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || | ||
217 | dwarf_formref_die(&attr, die_mem) == NULL) | ||
218 | return NULL; | ||
219 | |||
220 | tag = dwarf_tag(die_mem); | ||
221 | vr_die = die_mem; | ||
222 | } while (tag == DW_TAG_const_type || | ||
223 | tag == DW_TAG_restrict_type || | ||
224 | tag == DW_TAG_volatile_type || | ||
225 | tag == DW_TAG_shared_type || | ||
226 | tag == DW_TAG_typedef); | ||
227 | |||
228 | return die_mem; | ||
229 | } | ||
230 | |||
209 | /* Return values for die_find callbacks */ | 231 | /* Return values for die_find callbacks */ |
210 | enum { | 232 | enum { |
211 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | 233 | DIE_FIND_CB_FOUND = 0, /* End of Search */ |
@@ -314,6 +336,25 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | |||
314 | die_mem); | 336 | die_mem); |
315 | } | 337 | } |
316 | 338 | ||
339 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
340 | { | ||
341 | const char *name = data; | ||
342 | |||
343 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
344 | (die_compare_name(die_mem, name) == 0)) | ||
345 | return DIE_FIND_CB_FOUND; | ||
346 | |||
347 | return DIE_FIND_CB_SIBLING; | ||
348 | } | ||
349 | |||
350 | /* Find a member called 'name' */ | ||
351 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
352 | Dwarf_Die *die_mem) | ||
353 | { | ||
354 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
355 | die_mem); | ||
356 | } | ||
357 | |||
317 | /* | 358 | /* |
318 | * Probe finder related functions | 359 | * Probe finder related functions |
319 | */ | 360 | */ |
@@ -363,6 +404,62 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) | |||
363 | } | 404 | } |
364 | } | 405 | } |
365 | 406 | ||
407 | static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | ||
408 | struct perf_probe_arg_field *field, | ||
409 | struct kprobe_trace_arg_ref **ref_ptr) | ||
410 | { | ||
411 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | ||
412 | Dwarf_Attribute attr; | ||
413 | Dwarf_Die member; | ||
414 | Dwarf_Die type; | ||
415 | Dwarf_Word offs; | ||
416 | |||
417 | pr_debug("converting %s in %s\n", field->name, varname); | ||
418 | if (die_get_real_type(vr_die, &type) == NULL) | ||
419 | die("Failed to get a type information of %s.", varname); | ||
420 | |||
421 | /* Check the pointer and dereference */ | ||
422 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | ||
423 | if (!field->ref) | ||
424 | die("Semantic error: %s must be referred by '->'", | ||
425 | field->name); | ||
426 | /* Get the type pointed by this pointer */ | ||
427 | if (die_get_real_type(&type, &type) == NULL) | ||
428 | die("Failed to get a type information of %s.", varname); | ||
429 | |||
430 | ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
431 | if (*ref_ptr) | ||
432 | (*ref_ptr)->next = ref; | ||
433 | else | ||
434 | *ref_ptr = ref; | ||
435 | } else { | ||
436 | if (field->ref) | ||
437 | die("Semantic error: %s must be referred by '.'", | ||
438 | field->name); | ||
439 | if (!ref) | ||
440 | die("Structure on a register is not supported yet."); | ||
441 | } | ||
442 | |||
443 | /* Verify it is a data structure */ | ||
444 | if (dwarf_tag(&type) != DW_TAG_structure_type) | ||
445 | die("%s is not a data structure.", varname); | ||
446 | |||
447 | if (die_find_member(&type, field->name, &member) == NULL) | ||
448 | die("%s(tyep:%s) has no member %s.", varname, | ||
449 | dwarf_diename(&type), field->name); | ||
450 | |||
451 | /* Get the offset of the field */ | ||
452 | if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || | ||
453 | dwarf_formudata(&attr, &offs) != 0) | ||
454 | die("Failed to get the offset of %s.", field->name); | ||
455 | ref->offset += (long)offs; | ||
456 | |||
457 | /* Converting next field */ | ||
458 | if (field->next) | ||
459 | convert_variable_fields(&member, field->name, field->next, | ||
460 | &ref); | ||
461 | } | ||
462 | |||
366 | /* Show a variables in kprobe event format */ | 463 | /* Show a variables in kprobe event format */ |
367 | static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 464 | static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
368 | { | 465 | { |
@@ -379,6 +476,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
379 | goto error; | 476 | goto error; |
380 | 477 | ||
381 | convert_location(expr, pf); | 478 | convert_location(expr, pf); |
479 | |||
480 | if (pf->pvar->field) | ||
481 | convert_variable_fields(vr_die, pf->pvar->name, | ||
482 | pf->pvar->field, &pf->tvar->ref); | ||
382 | /* *expr will be cached in libdw. Don't free it. */ | 483 | /* *expr will be cached in libdw. Don't free it. */ |
383 | return ; | 484 | return ; |
384 | error: | 485 | error: |
@@ -391,13 +492,15 @@ error: | |||
391 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 492 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
392 | { | 493 | { |
393 | Dwarf_Die vr_die; | 494 | Dwarf_Die vr_die; |
495 | char buf[128]; | ||
394 | 496 | ||
395 | /* TODO: Support struct members and arrays */ | 497 | /* TODO: Support struct members and arrays */ |
396 | if (!is_c_varname(pf->pvar->name)) { | 498 | if (!is_c_varname(pf->pvar->name)) { |
397 | /* Copy raw parameters */ | 499 | /* Copy raw parameters */ |
398 | pf->tvar->value = xstrdup(pf->pvar->name); | 500 | pf->tvar->value = xstrdup(pf->pvar->name); |
399 | } else { | 501 | } else { |
400 | pf->tvar->name = xstrdup(pf->pvar->name); | 502 | synthesize_perf_probe_arg(pf->pvar, buf, 128); |
503 | pf->tvar->name = xstrdup(buf); | ||
401 | pr_debug("Searching '%s' variable in context.\n", | 504 | pr_debug("Searching '%s' variable in context.\n", |
402 | pf->pvar->name); | 505 | pf->pvar->name); |
403 | /* Search child die for local variables and parameters. */ | 506 | /* Search child die for local variables and parameters. */ |