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 */ |
