diff options
author | Ingo Molnar <mingo@elte.hu> | 2010-07-06 03:00:32 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-07-06 03:00:32 -0400 |
commit | 8bd0e1be25c43a9fbd33a8de06449088879d527a (patch) | |
tree | 8f24493dfd45df467faa69e2ecef67b8321932ef /tools/perf | |
parent | 39ef13a4ac28aa64cfe1bc36e6e00f1096707a28 (diff) | |
parent | b7dcb857cc3eb89136111fefe89780129c1af1d7 (diff) |
Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-probe.txt | 4 | ||||
-rw-r--r-- | tools/perf/Makefile | 48 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 71 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 1 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 192 |
5 files changed, 230 insertions, 86 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index ea531d9d975c..27d52dae5a43 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -94,8 +94,8 @@ Each probe argument follows below syntax. | |||
94 | 94 | ||
95 | [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] | 95 | [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] |
96 | 96 | ||
97 | 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) | 97 | 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) |
98 | 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. | 98 | 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type. |
99 | 99 | ||
100 | LINE SYNTAX | 100 | LINE SYNTAX |
101 | ----------- | 101 | ----------- |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 17a3692397c5..26f626d45a9e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -605,33 +605,35 @@ endif | |||
605 | 605 | ||
606 | ifdef NO_DEMANGLE | 606 | ifdef NO_DEMANGLE |
607 | BASIC_CFLAGS += -DNO_DEMANGLE | 607 | BASIC_CFLAGS += -DNO_DEMANGLE |
608 | else ifdef HAVE_CPLUS_DEMANGLE | ||
609 | EXTLIBS += -liberty | ||
610 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
611 | else | 608 | else |
612 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd | 609 | ifdef HAVE_CPLUS_DEMANGLE |
613 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) | 610 | EXTLIBS += -liberty |
614 | ifeq ($(has_bfd),y) | 611 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
615 | EXTLIBS += -lbfd | 612 | else |
616 | else | 613 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd |
617 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | 614 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) |
618 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) | 615 | ifeq ($(has_bfd),y) |
619 | ifeq ($(has_bfd_iberty),y) | 616 | EXTLIBS += -lbfd |
620 | EXTLIBS += -lbfd -liberty | ||
621 | else | 617 | else |
622 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | 618 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty |
623 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) | 619 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) |
624 | ifeq ($(has_bfd_iberty_z),y) | 620 | ifeq ($(has_bfd_iberty),y) |
625 | EXTLIBS += -lbfd -liberty -lz | 621 | EXTLIBS += -lbfd -liberty |
626 | else | 622 | else |
627 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty | 623 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz |
628 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) | 624 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) |
629 | ifeq ($(has_cplus_demangle),y) | 625 | ifeq ($(has_bfd_iberty_z),y) |
630 | EXTLIBS += -liberty | 626 | EXTLIBS += -lbfd -liberty -lz |
631 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
632 | else | 627 | else |
633 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) | 628 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty |
634 | BASIC_CFLAGS += -DNO_DEMANGLE | 629 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) |
630 | ifeq ($(has_cplus_demangle),y) | ||
631 | EXTLIBS += -liberty | ||
632 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | ||
633 | else | ||
634 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) | ||
635 | BASIC_CFLAGS += -DNO_DEMANGLE | ||
636 | endif | ||
635 | endif | 637 | endif |
636 | endif | 638 | endif |
637 | endif | 639 | endif |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 914c67095d96..09cf5465e10a 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; |
@@ -904,6 +926,7 @@ out: | |||
904 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | 926 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, |
905 | char *buf, size_t buflen) | 927 | char *buf, size_t buflen) |
906 | { | 928 | { |
929 | struct kprobe_trace_arg_ref *ref = arg->ref; | ||
907 | int ret, depth = 0; | 930 | int ret, depth = 0; |
908 | char *tmp = buf; | 931 | char *tmp = buf; |
909 | 932 | ||
@@ -917,16 +940,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
917 | buf += ret; | 940 | buf += ret; |
918 | buflen -= ret; | 941 | buflen -= ret; |
919 | 942 | ||
943 | /* Special case: @XXX */ | ||
944 | if (arg->value[0] == '@' && arg->ref) | ||
945 | ref = ref->next; | ||
946 | |||
920 | /* Dereferencing arguments */ | 947 | /* Dereferencing arguments */ |
921 | if (arg->ref) { | 948 | if (ref) { |
922 | depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, | 949 | depth = __synthesize_kprobe_trace_arg_ref(ref, &buf, |
923 | &buflen, 1); | 950 | &buflen, 1); |
924 | if (depth < 0) | 951 | if (depth < 0) |
925 | return depth; | 952 | return depth; |
926 | } | 953 | } |
927 | 954 | ||
928 | /* Print argument value */ | 955 | /* Print argument value */ |
929 | 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); | ||
930 | if (ret < 0) | 961 | if (ret < 0) |
931 | return ret; | 962 | return ret; |
932 | buf += ret; | 963 | buf += 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 baf665383498..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,27 +491,72 @@ 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 | } |
465 | 500 | ||
466 | static int convert_variable_type(Dwarf_Die *vr_die, | 501 | static int convert_variable_type(Dwarf_Die *vr_die, |
467 | struct kprobe_trace_arg *targ) | 502 | struct kprobe_trace_arg *tvar, |
503 | const char *cast) | ||
468 | { | 504 | { |
505 | struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref; | ||
469 | Dwarf_Die type; | 506 | Dwarf_Die type; |
470 | char buf[16]; | 507 | char buf[16]; |
471 | int ret; | 508 | int ret; |
472 | 509 | ||
510 | /* TODO: check all types */ | ||
511 | if (cast && strcmp(cast, "string") != 0) { | ||
512 | /* Non string type is OK */ | ||
513 | tvar->type = strdup(cast); | ||
514 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
515 | } | ||
516 | |||
473 | if (die_get_real_type(vr_die, &type) == NULL) { | 517 | if (die_get_real_type(vr_die, &type) == NULL) { |
474 | pr_warning("Failed to get a type information of %s.\n", | 518 | pr_warning("Failed to get a type information of %s.\n", |
475 | dwarf_diename(vr_die)); | 519 | dwarf_diename(vr_die)); |
476 | return -ENOENT; | 520 | return -ENOENT; |
477 | } | 521 | } |
478 | 522 | ||
523 | pr_debug("%s type is %s.\n", | ||
524 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
525 | |||
526 | if (cast && strcmp(cast, "string") == 0) { /* String type */ | ||
527 | ret = dwarf_tag(&type); | ||
528 | if (ret != DW_TAG_pointer_type && | ||
529 | ret != DW_TAG_array_type) { | ||
530 | pr_warning("Failed to cast into string: " | ||
531 | "%s(%s) is not a pointer nor array.", | ||
532 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
533 | return -EINVAL; | ||
534 | } | ||
535 | if (ret == DW_TAG_pointer_type) { | ||
536 | if (die_get_real_type(&type, &type) == NULL) { | ||
537 | pr_warning("Failed to get a type information."); | ||
538 | return -ENOENT; | ||
539 | } | ||
540 | while (*ref_ptr) | ||
541 | ref_ptr = &(*ref_ptr)->next; | ||
542 | /* Add new reference with offset +0 */ | ||
543 | *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
544 | if (*ref_ptr == NULL) { | ||
545 | pr_warning("Out of memory error\n"); | ||
546 | return -ENOMEM; | ||
547 | } | ||
548 | } | ||
549 | if (die_compare_name(&type, "char") != 0 && | ||
550 | die_compare_name(&type, "unsigned char") != 0) { | ||
551 | pr_warning("Failed to cast into string: " | ||
552 | "%s is not (unsigned) char *.", | ||
553 | dwarf_diename(vr_die)); | ||
554 | return -EINVAL; | ||
555 | } | ||
556 | tvar->type = strdup(cast); | ||
557 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
558 | } | ||
559 | |||
479 | ret = die_get_byte_size(&type) * 8; | 560 | ret = die_get_byte_size(&type) * 8; |
480 | if (ret) { | 561 | if (ret) { |
481 | /* Check the bitwidth */ | 562 | /* Check the bitwidth */ |
@@ -495,8 +576,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
495 | strerror(-ret)); | 576 | strerror(-ret)); |
496 | return ret; | 577 | return ret; |
497 | } | 578 | } |
498 | targ->type = strdup(buf); | 579 | tvar->type = strdup(buf); |
499 | if (targ->type == NULL) | 580 | if (tvar->type == NULL) |
500 | return -ENOMEM; | 581 | return -ENOMEM; |
501 | } | 582 | } |
502 | return 0; | 583 | return 0; |
@@ -510,16 +591,44 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
510 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 591 | struct kprobe_trace_arg_ref *ref = *ref_ptr; |
511 | Dwarf_Die type; | 592 | Dwarf_Die type; |
512 | Dwarf_Word offs; | 593 | Dwarf_Word offs; |
513 | int ret; | 594 | int ret, tag; |
514 | 595 | ||
515 | pr_debug("converting %s in %s\n", field->name, varname); | 596 | pr_debug("converting %s in %s\n", field->name, varname); |
516 | if (die_get_real_type(vr_die, &type) == NULL) { | 597 | if (die_get_real_type(vr_die, &type) == NULL) { |
517 | pr_warning("Failed to get the type of %s.\n", varname); | 598 | pr_warning("Failed to get the type of %s.\n", varname); |
518 | return -ENOENT; | 599 | return -ENOENT; |
519 | } | 600 | } |
520 | 601 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); | |
521 | /* Check the pointer and dereference */ | 602 | tag = dwarf_tag(&type); |
522 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | 603 | |
604 | if (field->name[0] == '[' && | ||
605 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { | ||
606 | if (field->next) | ||
607 | /* Save original type for next field */ | ||
608 | memcpy(die_mem, &type, sizeof(*die_mem)); | ||
609 | /* Get the type of this array */ | ||
610 | if (die_get_real_type(&type, &type) == NULL) { | ||
611 | pr_warning("Failed to get the type of %s.\n", varname); | ||
612 | return -ENOENT; | ||
613 | } | ||
614 | pr_debug2("Array real type: (%x)\n", | ||
615 | (unsigned)dwarf_dieoffset(&type)); | ||
616 | if (tag == DW_TAG_pointer_type) { | ||
617 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
618 | if (ref == NULL) | ||
619 | return -ENOMEM; | ||
620 | if (*ref_ptr) | ||
621 | (*ref_ptr)->next = ref; | ||
622 | else | ||
623 | *ref_ptr = ref; | ||
624 | } | ||
625 | ref->offset += die_get_byte_size(&type) * field->index; | ||
626 | if (!field->next) | ||
627 | /* Save vr_die for converting types */ | ||
628 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | ||
629 | goto next; | ||
630 | } else if (tag == DW_TAG_pointer_type) { | ||
631 | /* Check the pointer and dereference */ | ||
523 | if (!field->ref) { | 632 | if (!field->ref) { |
524 | pr_err("Semantic error: %s must be referred by '->'\n", | 633 | pr_err("Semantic error: %s must be referred by '->'\n", |
525 | field->name); | 634 | field->name); |
@@ -545,10 +654,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
545 | *ref_ptr = ref; | 654 | *ref_ptr = ref; |
546 | } else { | 655 | } else { |
547 | /* Verify it is a data structure */ | 656 | /* Verify it is a data structure */ |
548 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | 657 | if (tag != DW_TAG_structure_type) { |
549 | pr_warning("%s is not a data structure.\n", varname); | 658 | pr_warning("%s is not a data structure.\n", varname); |
550 | return -EINVAL; | 659 | return -EINVAL; |
551 | } | 660 | } |
661 | if (field->name[0] == '[') { | ||
662 | pr_err("Semantic error: %s is not a pointor nor array.", | ||
663 | varname); | ||
664 | return -EINVAL; | ||
665 | } | ||
552 | if (field->ref) { | 666 | if (field->ref) { |
553 | pr_err("Semantic error: %s must be referred by '.'\n", | 667 | pr_err("Semantic error: %s must be referred by '.'\n", |
554 | field->name); | 668 | field->name); |
@@ -575,6 +689,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
575 | } | 689 | } |
576 | ref->offset += (long)offs; | 690 | ref->offset += (long)offs; |
577 | 691 | ||
692 | next: | ||
578 | /* Converting next field */ | 693 | /* Converting next field */ |
579 | if (field->next) | 694 | if (field->next) |
580 | return convert_variable_fields(die_mem, field->name, | 695 | return convert_variable_fields(die_mem, field->name, |
@@ -586,51 +701,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
586 | /* Show a variables in kprobe event format */ | 701 | /* Show a variables in kprobe event format */ |
587 | 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) |
588 | { | 703 | { |
589 | Dwarf_Attribute attr; | ||
590 | Dwarf_Die die_mem; | 704 | Dwarf_Die die_mem; |
591 | Dwarf_Op *expr; | ||
592 | size_t nexpr; | ||
593 | int ret; | 705 | int ret; |
594 | 706 | ||
595 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | 707 | pr_debug("Converting variable %s into trace event.\n", |
596 | goto error; | 708 | dwarf_diename(vr_die)); |
597 | /* TODO: handle more than 1 exprs */ | ||
598 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); | ||
599 | if (ret <= 0 || nexpr == 0) | ||
600 | goto error; | ||
601 | 709 | ||
602 | ret = convert_location(expr, pf); | 710 | ret = convert_variable_location(vr_die, pf); |
603 | if (ret == 0 && pf->pvar->field) { | 711 | if (ret == 0 && pf->pvar->field) { |
604 | ret = convert_variable_fields(vr_die, pf->pvar->var, | 712 | ret = convert_variable_fields(vr_die, pf->pvar->var, |
605 | pf->pvar->field, &pf->tvar->ref, | 713 | pf->pvar->field, &pf->tvar->ref, |
606 | &die_mem); | 714 | &die_mem); |
607 | vr_die = &die_mem; | 715 | vr_die = &die_mem; |
608 | } | 716 | } |
609 | if (ret == 0) { | 717 | if (ret == 0) |
610 | if (pf->pvar->type) { | 718 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); |
611 | pf->tvar->type = strdup(pf->pvar->type); | ||
612 | if (pf->tvar->type == NULL) | ||
613 | ret = -ENOMEM; | ||
614 | } else | ||
615 | ret = convert_variable_type(vr_die, pf->tvar); | ||
616 | } | ||
617 | /* *expr will be cached in libdw. Don't free it. */ | 719 | /* *expr will be cached in libdw. Don't free it. */ |
618 | return ret; | 720 | return ret; |
619 | error: | ||
620 | /* TODO: Support const_value */ | ||
621 | pr_err("Failed to find the location of %s at this address.\n" | ||
622 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
623 | return -ENOENT; | ||
624 | } | 721 | } |
625 | 722 | ||
626 | /* Find a variable in a subprogram die */ | 723 | /* Find a variable in a subprogram die */ |
627 | 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) |
628 | { | 725 | { |
629 | Dwarf_Die vr_die; | 726 | Dwarf_Die vr_die, *scopes; |
630 | char buf[32], *ptr; | 727 | char buf[32], *ptr; |
631 | int ret; | 728 | int ret, nscopes; |
632 | 729 | ||
633 | /* TODO: Support arrays */ | ||
634 | if (pf->pvar->name) | 730 | if (pf->pvar->name) |
635 | pf->tvar->name = strdup(pf->pvar->name); | 731 | pf->tvar->name = strdup(pf->pvar->name); |
636 | else { | 732 | else { |
@@ -657,12 +753,26 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
657 | pr_debug("Searching '%s' variable in context.\n", | 753 | pr_debug("Searching '%s' variable in context.\n", |
658 | pf->pvar->var); | 754 | pf->pvar->var); |
659 | /* Search child die for local variables and parameters. */ | 755 | /* Search child die for local variables and parameters. */ |
660 | 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) | ||
661 | pr_warning("Failed to find '%s' in this function.\n", | 773 | pr_warning("Failed to find '%s' in this function.\n", |
662 | pf->pvar->var); | 774 | pf->pvar->var); |
663 | return -ENOENT; | 775 | return ret; |
664 | } | ||
665 | return convert_variable(&vr_die, pf); | ||
666 | } | 776 | } |
667 | 777 | ||
668 | /* Show a probe point to output buffer */ | 778 | /* Show a probe point to output buffer */ |