diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2010-05-19 15:57:49 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-07-05 17:51:33 -0400 |
commit | b7dcb857cc3eb89136111fefe89780129c1af1d7 (patch) | |
tree | 21350796c508db4fb782f3e61eb63f62c3527f69 /tools/perf | |
parent | b2a3c12b7442247c440f7083d48ef05716753ec1 (diff) |
perf probe: Support static and global variables
Add static and global variables support to perf probe.
This allows user to trace non-local variables (and
structure members) at probe points.
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: <20100519195749.2885.17451.stgit@localhost6.localdomain6>
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/util/probe-event.c | 15 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 85 |
2 files changed, 73 insertions, 27 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 351baa9a3695..09cf5465e10a 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -926,6 +926,7 @@ out: | |||
926 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | 926 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, |
927 | char *buf, size_t buflen) | 927 | char *buf, size_t buflen) |
928 | { | 928 | { |
929 | struct kprobe_trace_arg_ref *ref = arg->ref; | ||
929 | int ret, depth = 0; | 930 | int ret, depth = 0; |
930 | char *tmp = buf; | 931 | char *tmp = buf; |
931 | 932 | ||
@@ -939,16 +940,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
939 | buf += ret; | 940 | buf += ret; |
940 | buflen -= ret; | 941 | buflen -= ret; |
941 | 942 | ||
943 | /* Special case: @XXX */ | ||
944 | if (arg->value[0] == '@' && arg->ref) | ||
945 | ref = ref->next; | ||
946 | |||
942 | /* Dereferencing arguments */ | 947 | /* Dereferencing arguments */ |
943 | if (arg->ref) { | 948 | if (ref) { |
944 | depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, | 949 | depth = __synthesize_kprobe_trace_arg_ref(ref, &buf, |
945 | &buflen, 1); | 950 | &buflen, 1); |
946 | if (depth < 0) | 951 | if (depth < 0) |
947 | return depth; | 952 | return depth; |
948 | } | 953 | } |
949 | 954 | ||
950 | /* Print argument value */ | 955 | /* Print argument value */ |
951 | ret = e_snprintf(buf, buflen, "%s", arg->value); | 956 | if (arg->value[0] == '@' && arg->ref) |
957 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, | ||
958 | arg->ref->offset); | ||
959 | else | ||
960 | ret = e_snprintf(buf, buflen, "%s", arg->value); | ||
952 | if (ret < 0) | 961 | if (ret < 0) |
953 | return ret; | 962 | return ret; |
954 | buf += ret; | 963 | buf += ret; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 308664deb857..3e64e1fa1051 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -406,14 +406,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | |||
406 | * Probe finder related functions | 406 | * Probe finder related functions |
407 | */ | 407 | */ |
408 | 408 | ||
409 | static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs) | ||
410 | { | ||
411 | struct kprobe_trace_arg_ref *ref; | ||
412 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
413 | if (ref != NULL) | ||
414 | ref->offset = offs; | ||
415 | return ref; | ||
416 | } | ||
417 | |||
409 | /* Show a location */ | 418 | /* Show a location */ |
410 | static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | 419 | static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) |
411 | { | 420 | { |
421 | Dwarf_Attribute attr; | ||
422 | Dwarf_Op *op; | ||
423 | size_t nops; | ||
412 | unsigned int regn; | 424 | unsigned int regn; |
413 | Dwarf_Word offs = 0; | 425 | Dwarf_Word offs = 0; |
414 | bool ref = false; | 426 | bool ref = false; |
415 | const char *regs; | 427 | const char *regs; |
416 | struct kprobe_trace_arg *tvar = pf->tvar; | 428 | struct kprobe_trace_arg *tvar = pf->tvar; |
429 | int ret; | ||
430 | |||
431 | /* TODO: handle more than 1 exprs */ | ||
432 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || | ||
433 | dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || | ||
434 | nops == 0) { | ||
435 | /* TODO: Support const_value */ | ||
436 | pr_err("Failed to find the location of %s at this address.\n" | ||
437 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
438 | return -ENOENT; | ||
439 | } | ||
440 | |||
441 | if (op->atom == DW_OP_addr) { | ||
442 | /* Static variables on memory (not stack), make @varname */ | ||
443 | ret = strlen(dwarf_diename(vr_die)); | ||
444 | tvar->value = zalloc(ret + 2); | ||
445 | if (tvar->value == NULL) | ||
446 | return -ENOMEM; | ||
447 | snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); | ||
448 | tvar->ref = alloc_trace_arg_ref((long)offs); | ||
449 | if (tvar->ref == NULL) | ||
450 | return -ENOMEM; | ||
451 | return 0; | ||
452 | } | ||
417 | 453 | ||
418 | /* If this is based on frame buffer, set the offset */ | 454 | /* If this is based on frame buffer, set the offset */ |
419 | if (op->atom == DW_OP_fbreg) { | 455 | if (op->atom == DW_OP_fbreg) { |
@@ -455,10 +491,9 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | |||
455 | return -ENOMEM; | 491 | return -ENOMEM; |
456 | 492 | ||
457 | if (ref) { | 493 | if (ref) { |
458 | tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 494 | tvar->ref = alloc_trace_arg_ref((long)offs); |
459 | if (tvar->ref == NULL) | 495 | if (tvar->ref == NULL) |
460 | return -ENOMEM; | 496 | return -ENOMEM; |
461 | tvar->ref->offset = (long)offs; | ||
462 | } | 497 | } |
463 | return 0; | 498 | return 0; |
464 | } | 499 | } |
@@ -666,20 +701,13 @@ next: | |||
666 | /* Show a variables in kprobe event format */ | 701 | /* Show a variables in kprobe event format */ |
667 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 702 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
668 | { | 703 | { |
669 | Dwarf_Attribute attr; | ||
670 | Dwarf_Die die_mem; | 704 | Dwarf_Die die_mem; |
671 | Dwarf_Op *expr; | ||
672 | size_t nexpr; | ||
673 | int ret; | 705 | int ret; |
674 | 706 | ||
675 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | 707 | pr_debug("Converting variable %s into trace event.\n", |
676 | goto error; | 708 | dwarf_diename(vr_die)); |
677 | /* TODO: handle more than 1 exprs */ | ||
678 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); | ||
679 | if (ret <= 0 || nexpr == 0) | ||
680 | goto error; | ||
681 | 709 | ||
682 | ret = convert_location(expr, pf); | 710 | ret = convert_variable_location(vr_die, pf); |
683 | if (ret == 0 && pf->pvar->field) { | 711 | if (ret == 0 && pf->pvar->field) { |
684 | ret = convert_variable_fields(vr_die, pf->pvar->var, | 712 | ret = convert_variable_fields(vr_die, pf->pvar->var, |
685 | pf->pvar->field, &pf->tvar->ref, | 713 | pf->pvar->field, &pf->tvar->ref, |
@@ -690,19 +718,14 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
690 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); | 718 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); |
691 | /* *expr will be cached in libdw. Don't free it. */ | 719 | /* *expr will be cached in libdw. Don't free it. */ |
692 | return ret; | 720 | return ret; |
693 | error: | ||
694 | /* TODO: Support const_value */ | ||
695 | pr_err("Failed to find the location of %s at this address.\n" | ||
696 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
697 | return -ENOENT; | ||
698 | } | 721 | } |
699 | 722 | ||
700 | /* Find a variable in a subprogram die */ | 723 | /* Find a variable in a subprogram die */ |
701 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 724 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
702 | { | 725 | { |
703 | Dwarf_Die vr_die; | 726 | Dwarf_Die vr_die, *scopes; |
704 | char buf[32], *ptr; | 727 | char buf[32], *ptr; |
705 | int ret; | 728 | int ret, nscopes; |
706 | 729 | ||
707 | if (pf->pvar->name) | 730 | if (pf->pvar->name) |
708 | pf->tvar->name = strdup(pf->pvar->name); | 731 | pf->tvar->name = strdup(pf->pvar->name); |
@@ -730,12 +753,26 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
730 | pr_debug("Searching '%s' variable in context.\n", | 753 | pr_debug("Searching '%s' variable in context.\n", |
731 | pf->pvar->var); | 754 | pf->pvar->var); |
732 | /* Search child die for local variables and parameters. */ | 755 | /* Search child die for local variables and parameters. */ |
733 | if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { | 756 | if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) |
757 | ret = convert_variable(&vr_die, pf); | ||
758 | else { | ||
759 | /* Search upper class */ | ||
760 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | ||
761 | if (nscopes > 0) { | ||
762 | ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, | ||
763 | 0, NULL, 0, 0, &vr_die); | ||
764 | if (ret >= 0) | ||
765 | ret = convert_variable(&vr_die, pf); | ||
766 | else | ||
767 | ret = -ENOENT; | ||
768 | free(scopes); | ||
769 | } else | ||
770 | ret = -ENOENT; | ||
771 | } | ||
772 | if (ret < 0) | ||
734 | pr_warning("Failed to find '%s' in this function.\n", | 773 | pr_warning("Failed to find '%s' in this function.\n", |
735 | pf->pvar->var); | 774 | pf->pvar->var); |
736 | return -ENOENT; | 775 | return ret; |
737 | } | ||
738 | return convert_variable(&vr_die, pf); | ||
739 | } | 776 | } |
740 | 777 | ||
741 | /* Show a probe point to output buffer */ | 778 | /* Show a probe point to output buffer */ |