diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 203 |
1 files changed, 192 insertions, 11 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4b852c0d16a5..1b2124d12f68 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | |||
140 | return found; | 140 | return found; |
141 | } | 141 | } |
142 | 142 | ||
143 | static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) | ||
144 | { | ||
145 | Dwarf_Signed cnt, i; | ||
146 | char **srcs; | ||
147 | int ret = 0; | ||
148 | |||
149 | if (!buf || !fno) | ||
150 | return -EINVAL; | ||
151 | |||
152 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | ||
153 | if (ret == DW_DLV_OK) { | ||
154 | if ((Dwarf_Unsigned)cnt > fno - 1) { | ||
155 | *buf = strdup(srcs[fno - 1]); | ||
156 | ret = 0; | ||
157 | pr_debug("found filename: %s\n", *buf); | ||
158 | } else | ||
159 | ret = -ENOENT; | ||
160 | for (i = 0; i < cnt; i++) | ||
161 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
162 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
163 | } else | ||
164 | ret = -EINVAL; | ||
165 | return ret; | ||
166 | } | ||
167 | |||
143 | /* Compare diename and tname */ | 168 | /* Compare diename and tname */ |
144 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | 169 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) |
145 | { | 170 | { |
@@ -402,11 +427,11 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | |||
402 | } else if (op == DW_OP_regx) { | 427 | } else if (op == DW_OP_regx) { |
403 | regn = loc->lr_number; | 428 | regn = loc->lr_number; |
404 | } else | 429 | } else |
405 | die("Dwarf_OP %d is not supported.\n", op); | 430 | die("Dwarf_OP %d is not supported.", op); |
406 | 431 | ||
407 | regs = get_arch_regstr(regn); | 432 | regs = get_arch_regstr(regn); |
408 | if (!regs) | 433 | if (!regs) |
409 | die("%lld exceeds max register number.\n", regn); | 434 | die("%lld exceeds max register number.", regn); |
410 | 435 | ||
411 | if (deref) | 436 | if (deref) |
412 | ret = snprintf(pf->buf, pf->len, | 437 | ret = snprintf(pf->buf, pf->len, |
@@ -438,7 +463,7 @@ static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | |||
438 | return ; | 463 | return ; |
439 | error: | 464 | error: |
440 | die("Failed to find the location of %s at this address.\n" | 465 | die("Failed to find the location of %s at this address.\n" |
441 | " Perhaps, it has been optimized out.\n", pf->var); | 466 | " Perhaps, it has been optimized out.", pf->var); |
442 | } | 467 | } |
443 | 468 | ||
444 | static int variable_callback(struct die_link *dlink, void *data) | 469 | static int variable_callback(struct die_link *dlink, void *data) |
@@ -476,7 +501,7 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | |||
476 | /* Search child die for local variables and parameters. */ | 501 | /* Search child die for local variables and parameters. */ |
477 | ret = search_die_from_children(sp_die, variable_callback, pf); | 502 | ret = search_die_from_children(sp_die, variable_callback, pf); |
478 | if (!ret) | 503 | if (!ret) |
479 | die("Failed to find '%s' in this function.\n", pf->var); | 504 | die("Failed to find '%s' in this function.", pf->var); |
480 | } | 505 | } |
481 | 506 | ||
482 | /* Get a frame base on the address */ | 507 | /* Get a frame base on the address */ |
@@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data) | |||
567 | } | 592 | } |
568 | 593 | ||
569 | /* Find probe point from its line number */ | 594 | /* Find probe point from its line number */ |
570 | static void find_by_line(struct probe_finder *pf) | 595 | static void find_probe_point_by_line(struct probe_finder *pf) |
571 | { | 596 | { |
572 | Dwarf_Signed cnt, i, clm; | 597 | Dwarf_Signed cnt, i, clm; |
573 | Dwarf_Line *lines; | 598 | Dwarf_Line *lines; |
@@ -602,7 +627,7 @@ static void find_by_line(struct probe_finder *pf) | |||
602 | ret = search_die_from_children(pf->cu_die, | 627 | ret = search_die_from_children(pf->cu_die, |
603 | probeaddr_callback, pf); | 628 | probeaddr_callback, pf); |
604 | if (ret == 0) | 629 | if (ret == 0) |
605 | die("Probe point is not found in subprograms.\n"); | 630 | die("Probe point is not found in subprograms."); |
606 | /* Continuing, because target line might be inlined. */ | 631 | /* Continuing, because target line might be inlined. */ |
607 | } | 632 | } |
608 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 633 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); |
@@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data) | |||
626 | pf->fno = die_get_decl_file(dlink->die); | 651 | pf->fno = die_get_decl_file(dlink->die); |
627 | pf->lno = die_get_decl_line(dlink->die) | 652 | pf->lno = die_get_decl_line(dlink->die) |
628 | + pp->line; | 653 | + pp->line; |
629 | find_by_line(pf); | 654 | find_probe_point_by_line(pf); |
630 | return 1; | 655 | return 1; |
631 | } | 656 | } |
632 | if (die_inlined_subprogram(dlink->die)) { | 657 | if (die_inlined_subprogram(dlink->die)) { |
@@ -661,7 +686,7 @@ static int probefunc_callback(struct die_link *dlink, void *data) | |||
661 | !die_inlined_subprogram(lk->die)) | 686 | !die_inlined_subprogram(lk->die)) |
662 | goto found; | 687 | goto found; |
663 | } | 688 | } |
664 | die("Failed to find real subprogram.\n"); | 689 | die("Failed to find real subprogram."); |
665 | found: | 690 | found: |
666 | /* Get offset from subprogram */ | 691 | /* Get offset from subprogram */ |
667 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | 692 | ret = die_within_subprogram(lk->die, pf->addr, &offs); |
@@ -673,7 +698,7 @@ found: | |||
673 | return 0; | 698 | return 0; |
674 | } | 699 | } |
675 | 700 | ||
676 | static void find_by_func(struct probe_finder *pf) | 701 | static void find_probe_point_by_func(struct probe_finder *pf) |
677 | { | 702 | { |
678 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | 703 | search_die_from_children(pf->cu_die, probefunc_callback, pf); |
679 | } | 704 | } |
@@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
714 | if (ret == DW_DLV_NO_ENTRY) | 739 | if (ret == DW_DLV_NO_ENTRY) |
715 | pf.cu_base = 0; | 740 | pf.cu_base = 0; |
716 | if (pp->function) | 741 | if (pp->function) |
717 | find_by_func(&pf); | 742 | find_probe_point_by_func(&pf); |
718 | else { | 743 | else { |
719 | pf.lno = pp->line; | 744 | pf.lno = pp->line; |
720 | find_by_line(&pf); | 745 | find_probe_point_by_line(&pf); |
721 | } | 746 | } |
722 | } | 747 | } |
723 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | 748 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); |
@@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
728 | return pp->found; | 753 | return pp->found; |
729 | } | 754 | } |
730 | 755 | ||
756 | |||
757 | static void line_range_add_line(struct line_range *lr, unsigned int line) | ||
758 | { | ||
759 | struct line_node *ln; | ||
760 | struct list_head *p; | ||
761 | |||
762 | /* Reverse search, because new line will be the last one */ | ||
763 | list_for_each_entry_reverse(ln, &lr->line_list, list) { | ||
764 | if (ln->line < line) { | ||
765 | p = &ln->list; | ||
766 | goto found; | ||
767 | } else if (ln->line == line) /* Already exist */ | ||
768 | return ; | ||
769 | } | ||
770 | /* List is empty, or the smallest entry */ | ||
771 | p = &lr->line_list; | ||
772 | found: | ||
773 | pr_debug("Debug: add a line %u\n", line); | ||
774 | ln = zalloc(sizeof(struct line_node)); | ||
775 | DIE_IF(ln == NULL); | ||
776 | ln->line = line; | ||
777 | INIT_LIST_HEAD(&ln->list); | ||
778 | list_add(&ln->list, p); | ||
779 | } | ||
780 | |||
781 | /* Find line range from its line number */ | ||
782 | static void find_line_range_by_line(struct line_finder *lf) | ||
783 | { | ||
784 | Dwarf_Signed cnt, i; | ||
785 | Dwarf_Line *lines; | ||
786 | Dwarf_Unsigned lineno = 0; | ||
787 | Dwarf_Unsigned fno; | ||
788 | Dwarf_Addr addr; | ||
789 | int ret; | ||
790 | |||
791 | ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error); | ||
792 | DIE_IF(ret != DW_DLV_OK); | ||
793 | |||
794 | for (i = 0; i < cnt; i++) { | ||
795 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | ||
796 | DIE_IF(ret != DW_DLV_OK); | ||
797 | if (fno != lf->fno) | ||
798 | continue; | ||
799 | |||
800 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | ||
801 | DIE_IF(ret != DW_DLV_OK); | ||
802 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
803 | continue; | ||
804 | |||
805 | /* Filter line in the function address range */ | ||
806 | if (lf->addr_s && lf->addr_e) { | ||
807 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | ||
808 | DIE_IF(ret != DW_DLV_OK); | ||
809 | if (lf->addr_s > addr || lf->addr_e <= addr) | ||
810 | continue; | ||
811 | } | ||
812 | line_range_add_line(lf->lr, (unsigned int)lineno); | ||
813 | } | ||
814 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | ||
815 | if (!list_empty(&lf->lr->line_list)) | ||
816 | lf->found = 1; | ||
817 | } | ||
818 | |||
819 | /* Search function from function name */ | ||
820 | static int linefunc_callback(struct die_link *dlink, void *data) | ||
821 | { | ||
822 | struct line_finder *lf = (struct line_finder *)data; | ||
823 | struct line_range *lr = lf->lr; | ||
824 | Dwarf_Half tag; | ||
825 | int ret; | ||
826 | |||
827 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
828 | DIE_IF(ret == DW_DLV_ERROR); | ||
829 | if (tag == DW_TAG_subprogram && | ||
830 | die_compare_name(dlink->die, lr->function) == 0) { | ||
831 | /* Get the address range of this function */ | ||
832 | ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error); | ||
833 | if (ret == DW_DLV_OK) | ||
834 | ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error); | ||
835 | DIE_IF(ret == DW_DLV_ERROR); | ||
836 | if (ret == DW_DLV_NO_ENTRY) { | ||
837 | lf->addr_s = 0; | ||
838 | lf->addr_e = 0; | ||
839 | } | ||
840 | |||
841 | lf->fno = die_get_decl_file(dlink->die); | ||
842 | lr->offset = die_get_decl_line(dlink->die);; | ||
843 | lf->lno_s = lr->offset + lr->start; | ||
844 | if (!lr->end) | ||
845 | lf->lno_e = (Dwarf_Unsigned)-1; | ||
846 | else | ||
847 | lf->lno_e = lr->offset + lr->end; | ||
848 | lr->start = lf->lno_s; | ||
849 | lr->end = lf->lno_e; | ||
850 | find_line_range_by_line(lf); | ||
851 | /* If we find a target function, this should be end. */ | ||
852 | lf->found = 1; | ||
853 | return 1; | ||
854 | } | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | static void find_line_range_by_func(struct line_finder *lf) | ||
859 | { | ||
860 | search_die_from_children(lf->cu_die, linefunc_callback, lf); | ||
861 | } | ||
862 | |||
863 | int find_line_range(int fd, struct line_range *lr) | ||
864 | { | ||
865 | Dwarf_Half addr_size = 0; | ||
866 | Dwarf_Unsigned next_cuh = 0; | ||
867 | int ret; | ||
868 | struct line_finder lf = {.lr = lr}; | ||
869 | |||
870 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | ||
871 | if (ret != DW_DLV_OK) | ||
872 | return -ENOENT; | ||
873 | |||
874 | while (!lf.found) { | ||
875 | /* Search CU (Compilation Unit) */ | ||
876 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | ||
877 | &addr_size, &next_cuh, &__dw_error); | ||
878 | DIE_IF(ret == DW_DLV_ERROR); | ||
879 | if (ret == DW_DLV_NO_ENTRY) | ||
880 | break; | ||
881 | |||
882 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
883 | ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error); | ||
884 | DIE_IF(ret != DW_DLV_OK); | ||
885 | |||
886 | /* Check if target file is included. */ | ||
887 | if (lr->file) | ||
888 | lf.fno = cu_find_fileno(lf.cu_die, lr->file); | ||
889 | |||
890 | if (!lr->file || lf.fno) { | ||
891 | if (lr->function) | ||
892 | find_line_range_by_func(&lf); | ||
893 | else { | ||
894 | lf.lno_s = lr->start; | ||
895 | if (!lr->end) | ||
896 | lf.lno_e = (Dwarf_Unsigned)-1; | ||
897 | else | ||
898 | lf.lno_e = lr->end; | ||
899 | find_line_range_by_line(&lf); | ||
900 | } | ||
901 | /* Get the real file path */ | ||
902 | if (lf.found) | ||
903 | cu_get_filename(lf.cu_die, lf.fno, &lr->path); | ||
904 | } | ||
905 | dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE); | ||
906 | } | ||
907 | ret = dwarf_finish(__dw_debug, &__dw_error); | ||
908 | DIE_IF(ret != DW_DLV_OK); | ||
909 | return lf.found; | ||
910 | } | ||
911 | |||