diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2010-04-12 13:17:15 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-04-14 16:27:56 -0400 |
commit | 4984912eb23113a4007940cd09c8351c0623ea5f (patch) | |
tree | 81e725def8479b4f4c7d7114f30df690784864c1 | |
parent | 93ccae7a2227466a0d071fe52c51319f2f34c365 (diff) |
perf probe: Query basic types from debuginfo
Query the basic type information (byte-size and signed-flag) from
debuginfo and pass that to kprobe-tracer. This is especially useful
for tracing the members of data structure, because each member has
different byte-size on the memory.
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20100412171715.3790.23730.stgit@localhost6.localdomain6>
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/probe-event.c | 9 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 1 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 78 |
3 files changed, 80 insertions, 8 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 19de8b77973d..05ca4a959e60 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -740,6 +740,13 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
740 | buf += ret; | 740 | buf += ret; |
741 | buflen -= ret; | 741 | buflen -= ret; |
742 | } | 742 | } |
743 | /* Print argument type */ | ||
744 | if (arg->type) { | ||
745 | ret = e_snprintf(buf, buflen, ":%s", arg->type); | ||
746 | if (ret <= 0) | ||
747 | return ret; | ||
748 | buf += ret; | ||
749 | } | ||
743 | 750 | ||
744 | return buf - tmp; | 751 | return buf - tmp; |
745 | } | 752 | } |
@@ -848,6 +855,8 @@ void clear_kprobe_trace_event(struct kprobe_trace_event *tev) | |||
848 | free(tev->args[i].name); | 855 | free(tev->args[i].name); |
849 | if (tev->args[i].value) | 856 | if (tev->args[i].value) |
850 | free(tev->args[i].value); | 857 | free(tev->args[i].value); |
858 | if (tev->args[i].type) | ||
859 | free(tev->args[i].type); | ||
851 | ref = tev->args[i].ref; | 860 | ref = tev->args[i].ref; |
852 | while (ref) { | 861 | while (ref) { |
853 | next = ref->next; | 862 | next = ref->next; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 10411f596321..a393a3f87cd0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -23,6 +23,7 @@ struct kprobe_trace_arg_ref { | |||
23 | struct kprobe_trace_arg { | 23 | struct kprobe_trace_arg { |
24 | char *name; /* Argument name */ | 24 | char *name; /* Argument name */ |
25 | char *value; /* Base value */ | 25 | char *value; /* Base value */ |
26 | char *type; /* Type name */ | ||
26 | struct kprobe_trace_arg_ref *ref; /* Referencing offset */ | 27 | struct kprobe_trace_arg_ref *ref; /* Referencing offset */ |
27 | }; | 28 | }; |
28 | 29 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 105e95c95eeb..ebeb413ac473 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] = { | |||
84 | #define arch_regs_table x86_32_regs_table | 84 | #define arch_regs_table x86_32_regs_table |
85 | #endif | 85 | #endif |
86 | 86 | ||
87 | /* Kprobe tracer basic type is up to u64 */ | ||
88 | #define MAX_BASIC_TYPE_BITS 64 | ||
89 | |||
87 | /* Return architecture dependent register string (for kprobe-tracer) */ | 90 | /* Return architecture dependent register string (for kprobe-tracer) */ |
88 | static const char *get_arch_regstr(unsigned int n) | 91 | static const char *get_arch_regstr(unsigned int n) |
89 | { | 92 | { |
@@ -230,6 +233,31 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | |||
230 | return die_mem; | 233 | return die_mem; |
231 | } | 234 | } |
232 | 235 | ||
236 | static bool die_is_signed_type(Dwarf_Die *tp_die) | ||
237 | { | ||
238 | Dwarf_Attribute attr; | ||
239 | Dwarf_Word ret; | ||
240 | |||
241 | if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || | ||
242 | dwarf_formudata(&attr, &ret) != 0) | ||
243 | return false; | ||
244 | |||
245 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
246 | ret == DW_ATE_signed_fixed); | ||
247 | } | ||
248 | |||
249 | static int die_get_byte_size(Dwarf_Die *tp_die) | ||
250 | { | ||
251 | Dwarf_Attribute attr; | ||
252 | Dwarf_Word ret; | ||
253 | |||
254 | if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || | ||
255 | dwarf_formudata(&attr, &ret) != 0) | ||
256 | return 0; | ||
257 | |||
258 | return (int)ret; | ||
259 | } | ||
260 | |||
233 | /* Return values for die_find callbacks */ | 261 | /* Return values for die_find callbacks */ |
234 | enum { | 262 | enum { |
235 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | 263 | DIE_FIND_CB_FOUND = 0, /* End of Search */ |
@@ -406,13 +434,42 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf) | |||
406 | } | 434 | } |
407 | } | 435 | } |
408 | 436 | ||
437 | static void convert_variable_type(Dwarf_Die *vr_die, | ||
438 | struct kprobe_trace_arg *targ) | ||
439 | { | ||
440 | Dwarf_Die type; | ||
441 | char buf[16]; | ||
442 | int ret; | ||
443 | |||
444 | if (die_get_real_type(vr_die, &type) == NULL) | ||
445 | die("Failed to get a type information of %s.", | ||
446 | dwarf_diename(vr_die)); | ||
447 | |||
448 | ret = die_get_byte_size(&type) * 8; | ||
449 | if (ret) { | ||
450 | /* Check the bitwidth */ | ||
451 | if (ret > MAX_BASIC_TYPE_BITS) { | ||
452 | pr_warning(" Warning: %s exceeds max-bitwidth." | ||
453 | " Cut down to %d bits.\n", | ||
454 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); | ||
455 | ret = MAX_BASIC_TYPE_BITS; | ||
456 | } | ||
457 | |||
458 | ret = snprintf(buf, 16, "%c%d", | ||
459 | die_is_signed_type(&type) ? 's' : 'u', ret); | ||
460 | if (ret < 0 || ret >= 16) | ||
461 | die("Failed to convert variable type."); | ||
462 | targ->type = xstrdup(buf); | ||
463 | } | ||
464 | } | ||
465 | |||
409 | static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | 466 | static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, |
410 | struct perf_probe_arg_field *field, | 467 | struct perf_probe_arg_field *field, |
411 | struct kprobe_trace_arg_ref **ref_ptr) | 468 | struct kprobe_trace_arg_ref **ref_ptr, |
469 | Dwarf_Die *die_mem) | ||
412 | { | 470 | { |
413 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 471 | struct kprobe_trace_arg_ref *ref = *ref_ptr; |
414 | Dwarf_Attribute attr; | 472 | Dwarf_Attribute attr; |
415 | Dwarf_Die member; | ||
416 | Dwarf_Die type; | 473 | Dwarf_Die type; |
417 | Dwarf_Word offs; | 474 | Dwarf_Word offs; |
418 | 475 | ||
@@ -450,26 +507,27 @@ static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
450 | die("Structure on a register is not supported yet."); | 507 | die("Structure on a register is not supported yet."); |
451 | } | 508 | } |
452 | 509 | ||
453 | if (die_find_member(&type, field->name, &member) == NULL) | 510 | if (die_find_member(&type, field->name, die_mem) == NULL) |
454 | die("%s(tyep:%s) has no member %s.", varname, | 511 | die("%s(tyep:%s) has no member %s.", varname, |
455 | dwarf_diename(&type), field->name); | 512 | dwarf_diename(&type), field->name); |
456 | 513 | ||
457 | /* Get the offset of the field */ | 514 | /* Get the offset of the field */ |
458 | if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || | 515 | if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL || |
459 | dwarf_formudata(&attr, &offs) != 0) | 516 | dwarf_formudata(&attr, &offs) != 0) |
460 | die("Failed to get the offset of %s.", field->name); | 517 | die("Failed to get the offset of %s.", field->name); |
461 | ref->offset += (long)offs; | 518 | ref->offset += (long)offs; |
462 | 519 | ||
463 | /* Converting next field */ | 520 | /* Converting next field */ |
464 | if (field->next) | 521 | if (field->next) |
465 | convert_variable_fields(&member, field->name, field->next, | 522 | convert_variable_fields(die_mem, field->name, field->next, |
466 | &ref); | 523 | &ref, die_mem); |
467 | } | 524 | } |
468 | 525 | ||
469 | /* Show a variables in kprobe event format */ | 526 | /* Show a variables in kprobe event format */ |
470 | static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 527 | static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
471 | { | 528 | { |
472 | Dwarf_Attribute attr; | 529 | Dwarf_Attribute attr; |
530 | Dwarf_Die die_mem; | ||
473 | Dwarf_Op *expr; | 531 | Dwarf_Op *expr; |
474 | size_t nexpr; | 532 | size_t nexpr; |
475 | int ret; | 533 | int ret; |
@@ -483,9 +541,13 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
483 | 541 | ||
484 | convert_location(expr, pf); | 542 | convert_location(expr, pf); |
485 | 543 | ||
486 | if (pf->pvar->field) | 544 | if (pf->pvar->field) { |
487 | convert_variable_fields(vr_die, pf->pvar->var, | 545 | convert_variable_fields(vr_die, pf->pvar->var, |
488 | pf->pvar->field, &pf->tvar->ref); | 546 | pf->pvar->field, &pf->tvar->ref, |
547 | &die_mem); | ||
548 | vr_die = &die_mem; | ||
549 | } | ||
550 | convert_variable_type(vr_die, pf->tvar); | ||
489 | /* *expr will be cached in libdw. Don't free it. */ | 551 | /* *expr will be cached in libdw. Don't free it. */ |
490 | return ; | 552 | return ; |
491 | error: | 553 | error: |