diff options
author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2011-08-11 07:02:59 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-08-12 08:28:45 -0400 |
commit | 221d061182b8ff5507d5768aeeecbc74f01c5dfa (patch) | |
tree | 79cd224ca76fd520e089860a3a0482ed2b718b0d /tools | |
parent | 13e27d7686c457c625242fc2c20be30eef942408 (diff) |
perf probe: Fix to search local variables in appropriate scope
Fix perf probe to search local variables in appropriate local inlined
function scope. For example, pre_schedule() has only 2 local variables,
as below;
$ perf probe -L pre_schedule
<pre_schedule@/home/mhiramat/ksrc/linux-2.6/kernel/sched.c:0>
0 static inline void pre_schedule(struct rq *rq, struct task_struct *prev)
{
2 if (prev->sched_class->pre_schedule)
3 prev->sched_class->pre_schedule(rq, prev);
}
However, current perf probe shows 4 local variables on pre_schedule(),
because it searches variables in the caller(schedule()) scope.
$ perf probe -V pre_schedule
Available variables at pre_schedule
@<schedule+445>
int cpu
long unsigned int* switch_count
struct rq* rq
struct task_struct* prev
This patch fixes this issue by searching variables in the local scope of
the instance of inlined function. Here is the result.
$ perf probe -V pre_schedule
Available variables at pre_schedule
@<schedule+445>
struct rq* rq
struct task_struct* prev
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110259.19900.85664.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/util/dwarf-aux.c | 33 | ||||
-rw-r--r-- | tools/perf/util/dwarf-aux.h | 4 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 132 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 2 |
4 files changed, 144 insertions, 27 deletions
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index d9b8ad098498..425703a58638 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -96,6 +96,39 @@ int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | |||
96 | return *lineno ?: -ENOENT; | 96 | return *lineno ?: -ENOENT; |
97 | } | 97 | } |
98 | 98 | ||
99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); | ||
100 | |||
101 | /** | ||
102 | * cu_walk_functions_at - Walk on function DIEs at given address | ||
103 | * @cu_die: A CU DIE | ||
104 | * @addr: An address | ||
105 | * @callback: A callback which called with found DIEs | ||
106 | * @data: A user data | ||
107 | * | ||
108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs | ||
109 | * should be subprogram or inlined-subroutines. | ||
110 | */ | ||
111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
112 | int (*callback)(Dwarf_Die *, void *), void *data) | ||
113 | { | ||
114 | Dwarf_Die die_mem; | ||
115 | Dwarf_Die *sc_die; | ||
116 | int ret = -ENOENT; | ||
117 | |||
118 | /* Inlined function could be recursive. Trace it until fail */ | ||
119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); | ||
120 | sc_die != NULL; | ||
121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, | ||
122 | &die_mem)) { | ||
123 | ret = callback(sc_die, data); | ||
124 | if (ret) | ||
125 | break; | ||
126 | } | ||
127 | |||
128 | return ret; | ||
129 | |||
130 | } | ||
131 | |||
99 | /** | 132 | /** |
100 | * die_compare_name - Compare diename and tname | 133 | * die_compare_name - Compare diename and tname |
101 | * @dw_die: a DIE | 134 | * @dw_die: a DIE |
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index c8e491bc133f..6f46106fd1d5 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -34,6 +34,10 @@ extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | |||
34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | 34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, |
35 | const char **fname, int *lineno); | 35 | const char **fname, int *lineno); |
36 | 36 | ||
37 | /* Walk on funcitons at given address */ | ||
38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
39 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
40 | |||
37 | /* Compare diename and tname */ | 41 | /* Compare diename and tname */ |
38 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | 42 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); |
39 | 43 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d6d57682473a..5c83b7d3d8ef 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -612,8 +612,8 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
612 | return ret; | 612 | return ret; |
613 | } | 613 | } |
614 | 614 | ||
615 | /* Find a variable in a subprogram die */ | 615 | /* Find a variable in a scope DIE */ |
616 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 616 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) |
617 | { | 617 | { |
618 | Dwarf_Die vr_die, *scopes; | 618 | Dwarf_Die vr_die, *scopes; |
619 | char buf[32], *ptr; | 619 | char buf[32], *ptr; |
@@ -655,11 +655,11 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
655 | pr_debug("Searching '%s' variable in context.\n", | 655 | pr_debug("Searching '%s' variable in context.\n", |
656 | pf->pvar->var); | 656 | pf->pvar->var); |
657 | /* Search child die for local variables and parameters. */ | 657 | /* Search child die for local variables and parameters. */ |
658 | if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die)) | 658 | if (die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) |
659 | ret = convert_variable(&vr_die, pf); | 659 | ret = convert_variable(&vr_die, pf); |
660 | else { | 660 | else { |
661 | /* Search upper class */ | 661 | /* Search upper class */ |
662 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 662 | nscopes = dwarf_getscopes_die(sc_die, &scopes); |
663 | ret = -ENOENT; | 663 | ret = -ENOENT; |
664 | while (nscopes-- > 1) { | 664 | while (nscopes-- > 1) { |
665 | pr_debug("Searching variables in %s\n", | 665 | pr_debug("Searching variables in %s\n", |
@@ -717,26 +717,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
717 | return 0; | 717 | return 0; |
718 | } | 718 | } |
719 | 719 | ||
720 | /* Call probe_finder callback with real subprogram DIE */ | 720 | /* Call probe_finder callback with scope DIE */ |
721 | static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | 721 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) |
722 | { | 722 | { |
723 | Dwarf_Die die_mem; | ||
724 | Dwarf_Attribute fb_attr; | 723 | Dwarf_Attribute fb_attr; |
725 | size_t nops; | 724 | size_t nops; |
726 | int ret; | 725 | int ret; |
727 | 726 | ||
728 | /* If no real subprogram, find a real one */ | 727 | if (!sc_die) { |
729 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 728 | pr_err("Caller must pass a scope DIE. Program error.\n"); |
730 | sp_die = die_find_realfunc(&pf->cu_die, pf->addr, &die_mem); | 729 | return -EINVAL; |
731 | if (!sp_die) { | 730 | } |
731 | |||
732 | /* If not a real subprogram, find a real one */ | ||
733 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | ||
734 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | ||
732 | pr_warning("Failed to find probe point in any " | 735 | pr_warning("Failed to find probe point in any " |
733 | "functions.\n"); | 736 | "functions.\n"); |
734 | return -ENOENT; | 737 | return -ENOENT; |
735 | } | 738 | } |
736 | } | 739 | } else |
740 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); | ||
737 | 741 | ||
738 | /* Get the frame base attribute/ops */ | 742 | /* Get the frame base attribute/ops from subprogram */ |
739 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 743 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); |
740 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 744 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
741 | if (ret <= 0 || nops == 0) { | 745 | if (ret <= 0 || nops == 0) { |
742 | pf->fb_ops = NULL; | 746 | pf->fb_ops = NULL; |
@@ -754,7 +758,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
754 | } | 758 | } |
755 | 759 | ||
756 | /* Call finder's callback handler */ | 760 | /* Call finder's callback handler */ |
757 | ret = pf->callback(sp_die, pf); | 761 | ret = pf->callback(sc_die, pf); |
758 | 762 | ||
759 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 763 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
760 | pf->fb_ops = NULL; | 764 | pf->fb_ops = NULL; |
@@ -762,17 +766,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
762 | return ret; | 766 | return ret; |
763 | } | 767 | } |
764 | 768 | ||
769 | struct find_scope_param { | ||
770 | const char *function; | ||
771 | const char *file; | ||
772 | int line; | ||
773 | int diff; | ||
774 | Dwarf_Die *die_mem; | ||
775 | bool found; | ||
776 | }; | ||
777 | |||
778 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) | ||
779 | { | ||
780 | struct find_scope_param *fsp = data; | ||
781 | const char *file; | ||
782 | int lno; | ||
783 | |||
784 | /* Skip if declared file name does not match */ | ||
785 | if (fsp->file) { | ||
786 | file = dwarf_decl_file(fn_die); | ||
787 | if (!file || strcmp(fsp->file, file) != 0) | ||
788 | return 0; | ||
789 | } | ||
790 | /* If the function name is given, that's what user expects */ | ||
791 | if (fsp->function) { | ||
792 | if (die_compare_name(fn_die, fsp->function)) { | ||
793 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
794 | fsp->found = true; | ||
795 | return 1; | ||
796 | } | ||
797 | } else { | ||
798 | /* With the line number, find the nearest declared DIE */ | ||
799 | dwarf_decl_line(fn_die, &lno); | ||
800 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { | ||
801 | /* Keep a candidate and continue */ | ||
802 | fsp->diff = fsp->line - lno; | ||
803 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
804 | fsp->found = true; | ||
805 | } | ||
806 | } | ||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | /* Find an appropriate scope fits to given conditions */ | ||
811 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) | ||
812 | { | ||
813 | struct find_scope_param fsp = { | ||
814 | .function = pf->pev->point.function, | ||
815 | .file = pf->fname, | ||
816 | .line = pf->lno, | ||
817 | .diff = INT_MAX, | ||
818 | .die_mem = die_mem, | ||
819 | .found = false, | ||
820 | }; | ||
821 | |||
822 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); | ||
823 | |||
824 | return fsp.found ? die_mem : NULL; | ||
825 | } | ||
826 | |||
765 | static int probe_point_line_walker(const char *fname, int lineno, | 827 | static int probe_point_line_walker(const char *fname, int lineno, |
766 | Dwarf_Addr addr, void *data) | 828 | Dwarf_Addr addr, void *data) |
767 | { | 829 | { |
768 | struct probe_finder *pf = data; | 830 | struct probe_finder *pf = data; |
831 | Dwarf_Die *sc_die, die_mem; | ||
769 | int ret; | 832 | int ret; |
770 | 833 | ||
771 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) | 834 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
772 | return 0; | 835 | return 0; |
773 | 836 | ||
774 | pf->addr = addr; | 837 | pf->addr = addr; |
775 | ret = call_probe_finder(NULL, pf); | 838 | sc_die = find_best_scope(pf, &die_mem); |
839 | if (!sc_die) { | ||
840 | pr_warning("Failed to find scope of probe point.\n"); | ||
841 | return -ENOENT; | ||
842 | } | ||
843 | |||
844 | ret = call_probe_finder(sc_die, pf); | ||
776 | 845 | ||
777 | /* Continue if no error, because the line will be in inline function */ | 846 | /* Continue if no error, because the line will be in inline function */ |
778 | return ret < 0 ? ret : 0; | 847 | return ret < 0 ? ret : 0; |
@@ -826,6 +895,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
826 | Dwarf_Addr addr, void *data) | 895 | Dwarf_Addr addr, void *data) |
827 | { | 896 | { |
828 | struct probe_finder *pf = data; | 897 | struct probe_finder *pf = data; |
898 | Dwarf_Die *sc_die, die_mem; | ||
829 | int ret; | 899 | int ret; |
830 | 900 | ||
831 | if (!line_list__has_line(&pf->lcache, lineno) || | 901 | if (!line_list__has_line(&pf->lcache, lineno) || |
@@ -835,7 +905,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
835 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | 905 | pr_debug("Probe line found: line:%d addr:0x%llx\n", |
836 | lineno, (unsigned long long)addr); | 906 | lineno, (unsigned long long)addr); |
837 | pf->addr = addr; | 907 | pf->addr = addr; |
838 | ret = call_probe_finder(NULL, pf); | 908 | pf->lno = lineno; |
909 | sc_die = find_best_scope(pf, &die_mem); | ||
910 | if (!sc_die) { | ||
911 | pr_warning("Failed to find scope of probe point.\n"); | ||
912 | return -ENOENT; | ||
913 | } | ||
914 | |||
915 | ret = call_probe_finder(sc_die, pf); | ||
839 | 916 | ||
840 | /* | 917 | /* |
841 | * Continue if no error, because the lazy pattern will match | 918 | * Continue if no error, because the lazy pattern will match |
@@ -1059,7 +1136,7 @@ found: | |||
1059 | } | 1136 | } |
1060 | 1137 | ||
1061 | /* Add a found probe point into trace event list */ | 1138 | /* Add a found probe point into trace event list */ |
1062 | static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | 1139 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
1063 | { | 1140 | { |
1064 | struct trace_event_finder *tf = | 1141 | struct trace_event_finder *tf = |
1065 | container_of(pf, struct trace_event_finder, pf); | 1142 | container_of(pf, struct trace_event_finder, pf); |
@@ -1074,8 +1151,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1074 | } | 1151 | } |
1075 | tev = &tf->tevs[tf->ntevs++]; | 1152 | tev = &tf->tevs[tf->ntevs++]; |
1076 | 1153 | ||
1077 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1154 | /* Trace point should be converted from subprogram DIE */ |
1078 | &tev->point); | 1155 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1156 | pf->pev->point.retprobe, &tev->point); | ||
1079 | if (ret < 0) | 1157 | if (ret < 0) |
1080 | return ret; | 1158 | return ret; |
1081 | 1159 | ||
@@ -1090,7 +1168,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1090 | for (i = 0; i < pf->pev->nargs; i++) { | 1168 | for (i = 0; i < pf->pev->nargs; i++) { |
1091 | pf->pvar = &pf->pev->args[i]; | 1169 | pf->pvar = &pf->pev->args[i]; |
1092 | pf->tvar = &tev->args[i]; | 1170 | pf->tvar = &tev->args[i]; |
1093 | ret = find_variable(sp_die, pf); | 1171 | /* Variable should be found from scope DIE */ |
1172 | ret = find_variable(sc_die, pf); | ||
1094 | if (ret != 0) | 1173 | if (ret != 0) |
1095 | return ret; | 1174 | return ret; |
1096 | } | 1175 | } |
@@ -1158,7 +1237,7 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
1158 | } | 1237 | } |
1159 | 1238 | ||
1160 | /* Add a found vars into available variables list */ | 1239 | /* Add a found vars into available variables list */ |
1161 | static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | 1240 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) |
1162 | { | 1241 | { |
1163 | struct available_var_finder *af = | 1242 | struct available_var_finder *af = |
1164 | container_of(pf, struct available_var_finder, pf); | 1243 | container_of(pf, struct available_var_finder, pf); |
@@ -1173,8 +1252,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1173 | } | 1252 | } |
1174 | vl = &af->vls[af->nvls++]; | 1253 | vl = &af->vls[af->nvls++]; |
1175 | 1254 | ||
1176 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1255 | /* Trace point should be converted from subprogram DIE */ |
1177 | &vl->point); | 1256 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1257 | pf->pev->point.retprobe, &vl->point); | ||
1178 | if (ret < 0) | 1258 | if (ret < 0) |
1179 | return ret; | 1259 | return ret; |
1180 | 1260 | ||
@@ -1186,14 +1266,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1186 | if (vl->vars == NULL) | 1266 | if (vl->vars == NULL) |
1187 | return -ENOMEM; | 1267 | return -ENOMEM; |
1188 | af->child = true; | 1268 | af->child = true; |
1189 | die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem); | 1269 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); |
1190 | 1270 | ||
1191 | /* Find external variables */ | 1271 | /* Find external variables */ |
1192 | if (!af->externs) | 1272 | if (!af->externs) |
1193 | goto out; | 1273 | goto out; |
1194 | /* Don't need to search child DIE for externs. */ | 1274 | /* Don't need to search child DIE for externs. */ |
1195 | af->child = false; | 1275 | af->child = false; |
1196 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 1276 | nscopes = dwarf_getscopes_die(sc_die, &scopes); |
1197 | while (nscopes-- > 1) | 1277 | while (nscopes-- > 1) |
1198 | die_find_child(&scopes[nscopes], collect_variables_cb, | 1278 | die_find_child(&scopes[nscopes], collect_variables_cb, |
1199 | (void *)af, &die_mem); | 1279 | (void *)af, &die_mem); |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index c478b42a2473..1132c8f0ce89 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -57,7 +57,7 @@ struct probe_finder { | |||
57 | struct perf_probe_event *pev; /* Target probe event */ | 57 | struct perf_probe_event *pev; /* Target probe event */ |
58 | 58 | ||
59 | /* Callback when a probe point is found */ | 59 | /* Callback when a probe point is found */ |
60 | int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf); | 60 | int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf); |
61 | 61 | ||
62 | /* For function searching */ | 62 | /* For function searching */ |
63 | int lno; /* Line number */ | 63 | int lno; /* Line number */ |