diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2010-05-19 15:57:42 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2010-07-05 17:50:27 -0400 |
commit | b2a3c12b7442247c440f7083d48ef05716753ec1 (patch) | |
tree | c3a3d0bb442e285979b0dbeecfbf0c04a8b3743e /tools/perf/util | |
parent | 73317b954041031249e8968d2e9023ff4e960d99 (diff) |
perf probe: Support tracing an entry of array
Add array-entry tracing support to perf probe. This enables to trace an entry
of array which is indexed by constant value, e.g. array[0].
For example:
$ perf probe -a 'bio_split bi->bi_io_vec[0]'
Cc: Paul Mackerras <paulus@samba.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20100519195742.2885.5344.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/util')
-rw-r--r-- | tools/perf/util/probe-event.c | 56 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 1 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 48 |
3 files changed, 82 insertions, 23 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 914c67095d96..351baa9a3695 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -557,7 +557,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
557 | /* Parse perf-probe event argument */ | 557 | /* Parse perf-probe event argument */ |
558 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | 558 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) |
559 | { | 559 | { |
560 | char *tmp; | 560 | char *tmp, *goodname; |
561 | struct perf_probe_arg_field **fieldp; | 561 | struct perf_probe_arg_field **fieldp; |
562 | 562 | ||
563 | pr_debug("parsing arg: %s into ", str); | 563 | pr_debug("parsing arg: %s into ", str); |
@@ -580,7 +580,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
580 | pr_debug("type:%s ", arg->type); | 580 | pr_debug("type:%s ", arg->type); |
581 | } | 581 | } |
582 | 582 | ||
583 | tmp = strpbrk(str, "-."); | 583 | tmp = strpbrk(str, "-.["); |
584 | if (!is_c_varname(str) || !tmp) { | 584 | if (!is_c_varname(str) || !tmp) { |
585 | /* A variable, register, symbol or special value */ | 585 | /* A variable, register, symbol or special value */ |
586 | arg->var = strdup(str); | 586 | arg->var = strdup(str); |
@@ -590,10 +590,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
590 | return 0; | 590 | return 0; |
591 | } | 591 | } |
592 | 592 | ||
593 | /* Structure fields */ | 593 | /* Structure fields or array element */ |
594 | arg->var = strndup(str, tmp - str); | 594 | arg->var = strndup(str, tmp - str); |
595 | if (arg->var == NULL) | 595 | if (arg->var == NULL) |
596 | return -ENOMEM; | 596 | return -ENOMEM; |
597 | goodname = arg->var; | ||
597 | pr_debug("%s, ", arg->var); | 598 | pr_debug("%s, ", arg->var); |
598 | fieldp = &arg->field; | 599 | fieldp = &arg->field; |
599 | 600 | ||
@@ -601,22 +602,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
601 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); | 602 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); |
602 | if (*fieldp == NULL) | 603 | if (*fieldp == NULL) |
603 | return -ENOMEM; | 604 | return -ENOMEM; |
604 | if (*tmp == '.') { | 605 | if (*tmp == '[') { /* Array */ |
605 | str = tmp + 1; | 606 | str = tmp; |
606 | (*fieldp)->ref = false; | 607 | (*fieldp)->index = strtol(str + 1, &tmp, 0); |
607 | } else if (tmp[1] == '>') { | ||
608 | str = tmp + 2; | ||
609 | (*fieldp)->ref = true; | 608 | (*fieldp)->ref = true; |
610 | } else { | 609 | if (*tmp != ']' || tmp == str + 1) { |
611 | semantic_error("Argument parse error: %s\n", str); | 610 | semantic_error("Array index must be a" |
612 | return -EINVAL; | 611 | " number.\n"); |
612 | return -EINVAL; | ||
613 | } | ||
614 | tmp++; | ||
615 | if (*tmp == '\0') | ||
616 | tmp = NULL; | ||
617 | } else { /* Structure */ | ||
618 | if (*tmp == '.') { | ||
619 | str = tmp + 1; | ||
620 | (*fieldp)->ref = false; | ||
621 | } else if (tmp[1] == '>') { | ||
622 | str = tmp + 2; | ||
623 | (*fieldp)->ref = true; | ||
624 | } else { | ||
625 | semantic_error("Argument parse error: %s\n", | ||
626 | str); | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | tmp = strpbrk(str, "-.["); | ||
613 | } | 630 | } |
614 | |||
615 | tmp = strpbrk(str, "-."); | ||
616 | if (tmp) { | 631 | if (tmp) { |
617 | (*fieldp)->name = strndup(str, tmp - str); | 632 | (*fieldp)->name = strndup(str, tmp - str); |
618 | if ((*fieldp)->name == NULL) | 633 | if ((*fieldp)->name == NULL) |
619 | return -ENOMEM; | 634 | return -ENOMEM; |
635 | if (*str != '[') | ||
636 | goodname = (*fieldp)->name; | ||
620 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | 637 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); |
621 | fieldp = &(*fieldp)->next; | 638 | fieldp = &(*fieldp)->next; |
622 | } | 639 | } |
@@ -624,11 +641,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
624 | (*fieldp)->name = strdup(str); | 641 | (*fieldp)->name = strdup(str); |
625 | if ((*fieldp)->name == NULL) | 642 | if ((*fieldp)->name == NULL) |
626 | return -ENOMEM; | 643 | return -ENOMEM; |
644 | if (*str != '[') | ||
645 | goodname = (*fieldp)->name; | ||
627 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | 646 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); |
628 | 647 | ||
629 | /* If no name is specified, set the last field name */ | 648 | /* If no name is specified, set the last field name (not array index)*/ |
630 | if (!arg->name) { | 649 | if (!arg->name) { |
631 | arg->name = strdup((*fieldp)->name); | 650 | arg->name = strdup(goodname); |
632 | if (arg->name == NULL) | 651 | if (arg->name == NULL) |
633 | return -ENOMEM; | 652 | return -ENOMEM; |
634 | } | 653 | } |
@@ -776,8 +795,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
776 | len -= ret; | 795 | len -= ret; |
777 | 796 | ||
778 | while (field) { | 797 | while (field) { |
779 | ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", | 798 | if (field->name[0] == '[') |
780 | field->name); | 799 | ret = e_snprintf(tmp, len, "%s", field->name); |
800 | else | ||
801 | ret = e_snprintf(tmp, len, "%s%s", | ||
802 | field->ref ? "->" : ".", field->name); | ||
781 | if (ret <= 0) | 803 | if (ret <= 0) |
782 | goto error; | 804 | goto error; |
783 | tmp += ret; | 805 | tmp += ret; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e9db1a214ca4..bc06d3e8bafa 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -50,6 +50,7 @@ struct perf_probe_point { | |||
50 | struct perf_probe_arg_field { | 50 | struct perf_probe_arg_field { |
51 | struct perf_probe_arg_field *next; /* Next field */ | 51 | struct perf_probe_arg_field *next; /* Next field */ |
52 | char *name; /* Name of the field */ | 52 | char *name; /* Name of the field */ |
53 | long index; /* Array index number */ | ||
53 | bool ref; /* Referencing flag */ | 54 | bool ref; /* Referencing flag */ |
54 | }; | 55 | }; |
55 | 56 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index aaea16b1c60b..308664deb857 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -485,6 +485,9 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
485 | return -ENOENT; | 485 | return -ENOENT; |
486 | } | 486 | } |
487 | 487 | ||
488 | pr_debug("%s type is %s.\n", | ||
489 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
490 | |||
488 | if (cast && strcmp(cast, "string") == 0) { /* String type */ | 491 | if (cast && strcmp(cast, "string") == 0) { /* String type */ |
489 | ret = dwarf_tag(&type); | 492 | ret = dwarf_tag(&type); |
490 | if (ret != DW_TAG_pointer_type && | 493 | if (ret != DW_TAG_pointer_type && |
@@ -553,16 +556,44 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
553 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 556 | struct kprobe_trace_arg_ref *ref = *ref_ptr; |
554 | Dwarf_Die type; | 557 | Dwarf_Die type; |
555 | Dwarf_Word offs; | 558 | Dwarf_Word offs; |
556 | int ret; | 559 | int ret, tag; |
557 | 560 | ||
558 | pr_debug("converting %s in %s\n", field->name, varname); | 561 | pr_debug("converting %s in %s\n", field->name, varname); |
559 | if (die_get_real_type(vr_die, &type) == NULL) { | 562 | if (die_get_real_type(vr_die, &type) == NULL) { |
560 | pr_warning("Failed to get the type of %s.\n", varname); | 563 | pr_warning("Failed to get the type of %s.\n", varname); |
561 | return -ENOENT; | 564 | return -ENOENT; |
562 | } | 565 | } |
563 | 566 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); | |
564 | /* Check the pointer and dereference */ | 567 | tag = dwarf_tag(&type); |
565 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | 568 | |
569 | if (field->name[0] == '[' && | ||
570 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { | ||
571 | if (field->next) | ||
572 | /* Save original type for next field */ | ||
573 | memcpy(die_mem, &type, sizeof(*die_mem)); | ||
574 | /* Get the type of this array */ | ||
575 | if (die_get_real_type(&type, &type) == NULL) { | ||
576 | pr_warning("Failed to get the type of %s.\n", varname); | ||
577 | return -ENOENT; | ||
578 | } | ||
579 | pr_debug2("Array real type: (%x)\n", | ||
580 | (unsigned)dwarf_dieoffset(&type)); | ||
581 | if (tag == DW_TAG_pointer_type) { | ||
582 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
583 | if (ref == NULL) | ||
584 | return -ENOMEM; | ||
585 | if (*ref_ptr) | ||
586 | (*ref_ptr)->next = ref; | ||
587 | else | ||
588 | *ref_ptr = ref; | ||
589 | } | ||
590 | ref->offset += die_get_byte_size(&type) * field->index; | ||
591 | if (!field->next) | ||
592 | /* Save vr_die for converting types */ | ||
593 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | ||
594 | goto next; | ||
595 | } else if (tag == DW_TAG_pointer_type) { | ||
596 | /* Check the pointer and dereference */ | ||
566 | if (!field->ref) { | 597 | if (!field->ref) { |
567 | pr_err("Semantic error: %s must be referred by '->'\n", | 598 | pr_err("Semantic error: %s must be referred by '->'\n", |
568 | field->name); | 599 | field->name); |
@@ -588,10 +619,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
588 | *ref_ptr = ref; | 619 | *ref_ptr = ref; |
589 | } else { | 620 | } else { |
590 | /* Verify it is a data structure */ | 621 | /* Verify it is a data structure */ |
591 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | 622 | if (tag != DW_TAG_structure_type) { |
592 | pr_warning("%s is not a data structure.\n", varname); | 623 | pr_warning("%s is not a data structure.\n", varname); |
593 | return -EINVAL; | 624 | return -EINVAL; |
594 | } | 625 | } |
626 | if (field->name[0] == '[') { | ||
627 | pr_err("Semantic error: %s is not a pointor nor array.", | ||
628 | varname); | ||
629 | return -EINVAL; | ||
630 | } | ||
595 | if (field->ref) { | 631 | if (field->ref) { |
596 | pr_err("Semantic error: %s must be referred by '.'\n", | 632 | pr_err("Semantic error: %s must be referred by '.'\n", |
597 | field->name); | 633 | field->name); |
@@ -618,6 +654,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
618 | } | 654 | } |
619 | ref->offset += (long)offs; | 655 | ref->offset += (long)offs; |
620 | 656 | ||
657 | next: | ||
621 | /* Converting next field */ | 658 | /* Converting next field */ |
622 | if (field->next) | 659 | if (field->next) |
623 | return convert_variable_fields(die_mem, field->name, | 660 | return convert_variable_fields(die_mem, field->name, |
@@ -667,7 +704,6 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
667 | char buf[32], *ptr; | 704 | char buf[32], *ptr; |
668 | int ret; | 705 | int ret; |
669 | 706 | ||
670 | /* TODO: Support arrays */ | ||
671 | if (pf->pvar->name) | 707 | if (pf->pvar->name) |
672 | pf->tvar->name = strdup(pf->pvar->name); | 708 | pf->tvar->name = strdup(pf->pvar->name); |
673 | else { | 709 | else { |