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/perf/util/probe-finder.c | |
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/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 105 |
1 files changed, 104 insertions, 1 deletions
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. */ |