diff options
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Documentation/perf-probe.txt | 20 | ||||
-rw-r--r-- | tools/perf/builtin-probe.c | 76 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 100 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 2 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 191 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 31 |
6 files changed, 402 insertions, 18 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 250e391b4bc..2de34075f6a 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -15,6 +15,8 @@ or | |||
15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] | 15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] |
16 | or | 16 | or |
17 | 'perf probe' --list | 17 | 'perf probe' --list |
18 | or | ||
19 | 'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' | ||
18 | 20 | ||
19 | DESCRIPTION | 21 | DESCRIPTION |
20 | ----------- | 22 | ----------- |
@@ -45,6 +47,11 @@ OPTIONS | |||
45 | --list:: | 47 | --list:: |
46 | List up current probe events. | 48 | List up current probe events. |
47 | 49 | ||
50 | -L:: | ||
51 | --line=:: | ||
52 | Show source code lines which can be probed. This needs an argument | ||
53 | which specifies a range of the source code. | ||
54 | |||
48 | PROBE SYNTAX | 55 | PROBE SYNTAX |
49 | ------------ | 56 | ------------ |
50 | Probe points are defined by following syntax. | 57 | Probe points are defined by following syntax. |
@@ -56,6 +63,19 @@ Probe points are defined by following syntax. | |||
56 | It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number. | 63 | It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number. |
57 | 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). | 64 | 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). |
58 | 65 | ||
66 | LINE SYNTAX | ||
67 | ----------- | ||
68 | Line range is descripted by following syntax. | ||
69 | |||
70 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]" | ||
71 | |||
72 | FUNC specifies the function name of showing lines. 'RLN' is the start line | ||
73 | number from function entry line, and 'RLN2' is the end line number. As same as | ||
74 | probe syntax, 'SRC' means the source file path, 'ALN' is start line number, | ||
75 | and 'ALN2' is end line number in the file. It is also possible to specify how | ||
76 | many lines to show by using 'NUM'. | ||
77 | So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. | ||
78 | |||
59 | SEE ALSO | 79 | SEE ALSO |
60 | -------- | 80 | -------- |
61 | linkperf:perf-trace[1], linkperf:perf-record[1] | 81 | linkperf:perf-trace[1], linkperf:perf-record[1] |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index ffdd3fe87b4..1d3a99ea5ce 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -55,11 +55,13 @@ static struct { | |||
55 | bool need_dwarf; | 55 | bool need_dwarf; |
56 | bool list_events; | 56 | bool list_events; |
57 | bool force_add; | 57 | bool force_add; |
58 | bool show_lines; | ||
58 | int nr_probe; | 59 | int nr_probe; |
59 | struct probe_point probes[MAX_PROBES]; | 60 | struct probe_point probes[MAX_PROBES]; |
60 | struct strlist *dellist; | 61 | struct strlist *dellist; |
61 | struct perf_session *psession; | 62 | struct perf_session *psession; |
62 | struct map *kmap; | 63 | struct map *kmap; |
64 | struct line_range line_range; | ||
63 | } session; | 65 | } session; |
64 | 66 | ||
65 | 67 | ||
@@ -116,6 +118,15 @@ static int opt_del_probe_event(const struct option *opt __used, | |||
116 | return 0; | 118 | return 0; |
117 | } | 119 | } |
118 | 120 | ||
121 | static int opt_show_lines(const struct option *opt __used, | ||
122 | const char *str, int unset __used) | ||
123 | { | ||
124 | if (str) | ||
125 | parse_line_range_desc(str, &session.line_range); | ||
126 | INIT_LIST_HEAD(&session.line_range.line_list); | ||
127 | session.show_lines = true; | ||
128 | return 0; | ||
129 | } | ||
119 | /* Currently just checking function name from symbol map */ | 130 | /* Currently just checking function name from symbol map */ |
120 | static void evaluate_probe_point(struct probe_point *pp) | 131 | static void evaluate_probe_point(struct probe_point *pp) |
121 | { | 132 | { |
@@ -144,6 +155,7 @@ static const char * const probe_usage[] = { | |||
144 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | 155 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", |
145 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", | 156 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", |
146 | "perf probe --list", | 157 | "perf probe --list", |
158 | "perf probe --line 'LINEDESC'", | ||
147 | NULL | 159 | NULL |
148 | }; | 160 | }; |
149 | 161 | ||
@@ -182,9 +194,32 @@ static const struct option options[] = { | |||
182 | opt_add_probe_event), | 194 | opt_add_probe_event), |
183 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" | 195 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" |
184 | " with existing name"), | 196 | " with existing name"), |
197 | #ifndef NO_LIBDWARF | ||
198 | OPT_CALLBACK('L', "line", NULL, | ||
199 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", | ||
200 | "Show source code lines.", opt_show_lines), | ||
201 | #endif | ||
185 | OPT_END() | 202 | OPT_END() |
186 | }; | 203 | }; |
187 | 204 | ||
205 | /* Initialize symbol maps for vmlinux */ | ||
206 | static void init_vmlinux(void) | ||
207 | { | ||
208 | symbol_conf.sort_by_name = true; | ||
209 | if (symbol_conf.vmlinux_name == NULL) | ||
210 | symbol_conf.try_vmlinux_path = true; | ||
211 | else | ||
212 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | ||
213 | if (symbol__init() < 0) | ||
214 | die("Failed to init symbol map."); | ||
215 | session.psession = perf_session__new(NULL, O_WRONLY, false); | ||
216 | if (session.psession == NULL) | ||
217 | die("Failed to init perf_session."); | ||
218 | session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION]; | ||
219 | if (!session.kmap) | ||
220 | die("Could not find kernel map.\n"); | ||
221 | } | ||
222 | |||
188 | int cmd_probe(int argc, const char **argv, const char *prefix __used) | 223 | int cmd_probe(int argc, const char **argv, const char *prefix __used) |
189 | { | 224 | { |
190 | int i, ret; | 225 | int i, ret; |
@@ -203,7 +238,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
203 | parse_probe_event_argv(argc, argv); | 238 | parse_probe_event_argv(argc, argv); |
204 | } | 239 | } |
205 | 240 | ||
206 | if ((!session.nr_probe && !session.dellist && !session.list_events)) | 241 | if ((!session.nr_probe && !session.dellist && !session.list_events && |
242 | !session.show_lines)) | ||
207 | usage_with_options(probe_usage, options); | 243 | usage_with_options(probe_usage, options); |
208 | 244 | ||
209 | if (debugfs_valid_mountpoint(debugfs_path) < 0) | 245 | if (debugfs_valid_mountpoint(debugfs_path) < 0) |
@@ -215,10 +251,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
215 | " --add/--del.\n"); | 251 | " --add/--del.\n"); |
216 | usage_with_options(probe_usage, options); | 252 | usage_with_options(probe_usage, options); |
217 | } | 253 | } |
254 | if (session.show_lines) { | ||
255 | pr_warning(" Error: Don't use --list with --line.\n"); | ||
256 | usage_with_options(probe_usage, options); | ||
257 | } | ||
218 | show_perf_probe_events(); | 258 | show_perf_probe_events(); |
219 | return 0; | 259 | return 0; |
220 | } | 260 | } |
221 | 261 | ||
262 | #ifndef NO_LIBDWARF | ||
263 | if (session.show_lines) { | ||
264 | if (session.nr_probe != 0 || session.dellist) { | ||
265 | pr_warning(" Error: Don't use --line with" | ||
266 | " --add/--del.\n"); | ||
267 | usage_with_options(probe_usage, options); | ||
268 | } | ||
269 | init_vmlinux(); | ||
270 | fd = open_vmlinux(); | ||
271 | if (fd < 0) | ||
272 | die("Could not open debuginfo file."); | ||
273 | ret = find_line_range(fd, &session.line_range); | ||
274 | if (ret <= 0) | ||
275 | die("Source line is not found.\n"); | ||
276 | close(fd); | ||
277 | show_line_range(&session.line_range); | ||
278 | return 0; | ||
279 | } | ||
280 | #endif | ||
281 | |||
222 | if (session.dellist) { | 282 | if (session.dellist) { |
223 | del_trace_kprobe_events(session.dellist); | 283 | del_trace_kprobe_events(session.dellist); |
224 | strlist__delete(session.dellist); | 284 | strlist__delete(session.dellist); |
@@ -226,18 +286,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
226 | return 0; | 286 | return 0; |
227 | } | 287 | } |
228 | 288 | ||
229 | /* Initialize symbol maps for vmlinux */ | 289 | /* Add probes */ |
230 | symbol_conf.sort_by_name = true; | 290 | init_vmlinux(); |
231 | if (symbol_conf.vmlinux_name == NULL) | ||
232 | symbol_conf.try_vmlinux_path = true; | ||
233 | if (symbol__init() < 0) | ||
234 | die("Failed to init symbol map."); | ||
235 | session.psession = perf_session__new(NULL, O_WRONLY, false); | ||
236 | if (session.psession == NULL) | ||
237 | die("Failed to init perf_session."); | ||
238 | session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION]; | ||
239 | if (!session.kmap) | ||
240 | die("Could not find kernel map.\n"); | ||
241 | 291 | ||
242 | if (session.need_dwarf) | 292 | if (session.need_dwarf) |
243 | #ifdef NO_LIBDWARF | 293 | #ifdef NO_LIBDWARF |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index a22141a773b..71b0dd590a3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include "strlist.h" | 38 | #include "strlist.h" |
39 | #include "debug.h" | 39 | #include "debug.h" |
40 | #include "cache.h" | 40 | #include "cache.h" |
41 | #include "color.h" | ||
41 | #include "parse-events.h" /* For debugfs_path */ | 42 | #include "parse-events.h" /* For debugfs_path */ |
42 | #include "probe-event.h" | 43 | #include "probe-event.h" |
43 | 44 | ||
@@ -63,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
63 | return ret; | 64 | return ret; |
64 | } | 65 | } |
65 | 66 | ||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | ||
68 | { | ||
69 | const char *ptr; | ||
70 | char *tmp; | ||
71 | /* | ||
72 | * <Syntax> | ||
73 | * SRC:SLN[+NUM|-ELN] | ||
74 | * FUNC[:SLN[+NUM|-ELN]] | ||
75 | */ | ||
76 | ptr = strchr(arg, ':'); | ||
77 | if (ptr) { | ||
78 | lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); | ||
79 | if (*tmp == '+') | ||
80 | lr->end = lr->start + (unsigned int)strtoul(tmp + 1, | ||
81 | &tmp, 0); | ||
82 | else if (*tmp == '-') | ||
83 | lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); | ||
84 | else | ||
85 | lr->end = 0; | ||
86 | pr_debug("Line range is %u to %u\n", lr->start, lr->end); | ||
87 | if (lr->end && lr->start > lr->end) | ||
88 | semantic_error("Start line must be smaller" | ||
89 | " than end line."); | ||
90 | if (*tmp != '\0') | ||
91 | semantic_error("Tailing with invalid character '%d'.", | ||
92 | *tmp); | ||
93 | tmp = strndup(arg, (ptr - arg)); | ||
94 | } else | ||
95 | tmp = strdup(arg); | ||
96 | |||
97 | if (strchr(tmp, '.')) | ||
98 | lr->file = tmp; | ||
99 | else | ||
100 | lr->function = tmp; | ||
101 | } | ||
102 | |||
66 | /* Check the name is good for event/group */ | 103 | /* Check the name is good for event/group */ |
67 | static bool check_event_name(const char *name) | 104 | static bool check_event_name(const char *name) |
68 | { | 105 | { |
@@ -678,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
678 | close(fd); | 715 | close(fd); |
679 | } | 716 | } |
680 | 717 | ||
718 | #define LINEBUF_SIZE 256 | ||
719 | |||
720 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
721 | { | ||
722 | char buf[LINEBUF_SIZE]; | ||
723 | const char *color = PERF_COLOR_BLUE; | ||
724 | |||
725 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
726 | goto error; | ||
727 | if (!skip) { | ||
728 | if (show_num) | ||
729 | fprintf(stdout, "%7u %s", l, buf); | ||
730 | else | ||
731 | color_fprintf(stdout, color, " %s", buf); | ||
732 | } | ||
733 | |||
734 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
735 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
736 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
737 | goto error; | ||
738 | if (!skip) { | ||
739 | if (show_num) | ||
740 | fprintf(stdout, "%s", buf); | ||
741 | else | ||
742 | color_fprintf(stdout, color, "%s", buf); | ||
743 | } | ||
744 | } | ||
745 | return; | ||
746 | error: | ||
747 | if (feof(fp)) | ||
748 | die("Source file is shorter than expected."); | ||
749 | else | ||
750 | die("File read error: %s", strerror(errno)); | ||
751 | } | ||
752 | |||
753 | void show_line_range(struct line_range *lr) | ||
754 | { | ||
755 | unsigned int l = 1; | ||
756 | struct line_node *ln; | ||
757 | FILE *fp; | ||
758 | |||
759 | setup_pager(); | ||
760 | |||
761 | if (lr->function) | ||
762 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
763 | lr->start - lr->offset); | ||
764 | else | ||
765 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
766 | |||
767 | fp = fopen(lr->path, "r"); | ||
768 | if (fp == NULL) | ||
769 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
770 | /* Skip to starting line number */ | ||
771 | while (l < lr->start) | ||
772 | show_one_line(fp, l++, true, false); | ||
773 | |||
774 | list_for_each_entry(ln, &lr->line_list, list) { | ||
775 | while (ln->line > l) | ||
776 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
777 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
778 | } | ||
779 | fclose(fp); | ||
780 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 7f1d499118c..711287d4bae 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "probe-finder.h" | 5 | #include "probe-finder.h" |
6 | #include "strlist.h" | 6 | #include "strlist.h" |
7 | 7 | ||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | ||
8 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | 9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, |
9 | bool *need_dwarf); | 10 | bool *need_dwarf); |
10 | extern int synthesize_perf_probe_point(struct probe_point *pp); | 11 | extern int synthesize_perf_probe_point(struct probe_point *pp); |
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | |||
15 | bool force_add); | 16 | bool force_add); |
16 | extern void del_trace_kprobe_events(struct strlist *dellist); | 17 | extern void del_trace_kprobe_events(struct strlist *dellist); |
17 | extern void show_perf_probe_events(void); | 18 | extern void show_perf_probe_events(void); |
19 | extern void show_line_range(struct line_range *lr); | ||
18 | 20 | ||
19 | /* Maximum index number of event-name postfix */ | 21 | /* Maximum index number of event-name postfix */ |
20 | #define MAX_EVENT_INDEX 1024 | 22 | #define MAX_EVENT_INDEX 1024 |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 6402798337c..1b2124d12f6 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 | { |
@@ -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; |
@@ -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)) { |
@@ -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 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index e3f396806e6..972b386116f 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -34,8 +34,26 @@ struct probe_point { | |||
34 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | 34 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ |
35 | }; | 35 | }; |
36 | 36 | ||
37 | /* Line number container */ | ||
38 | struct line_node { | ||
39 | struct list_head list; | ||
40 | unsigned int line; | ||
41 | }; | ||
42 | |||
43 | /* Line range */ | ||
44 | struct line_range { | ||
45 | char *file; /* File name */ | ||
46 | char *function; /* Function name */ | ||
47 | unsigned int start; /* Start line number */ | ||
48 | unsigned int end; /* End line number */ | ||
49 | unsigned int offset; /* Start line offset */ | ||
50 | char *path; /* Real path name */ | ||
51 | struct list_head line_list; /* Visible lines */ | ||
52 | }; | ||
53 | |||
37 | #ifndef NO_LIBDWARF | 54 | #ifndef NO_LIBDWARF |
38 | extern int find_probepoint(int fd, struct probe_point *pp); | 55 | extern int find_probepoint(int fd, struct probe_point *pp); |
56 | extern int find_line_range(int fd, struct line_range *lr); | ||
39 | 57 | ||
40 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | 58 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ |
41 | #ifndef _MIPS_SZLONG | 59 | #ifndef _MIPS_SZLONG |
@@ -62,6 +80,19 @@ struct probe_finder { | |||
62 | char *buf; /* Current output buffer */ | 80 | char *buf; /* Current output buffer */ |
63 | int len; /* Length of output buffer */ | 81 | int len; /* Length of output buffer */ |
64 | }; | 82 | }; |
83 | |||
84 | struct line_finder { | ||
85 | struct line_range *lr; /* Target line range */ | ||
86 | |||
87 | Dwarf_Unsigned fno; /* File number */ | ||
88 | Dwarf_Unsigned lno_s; /* Start line number */ | ||
89 | Dwarf_Unsigned lno_e; /* End line number */ | ||
90 | Dwarf_Addr addr_s; /* Start address */ | ||
91 | Dwarf_Addr addr_e; /* End address */ | ||
92 | Dwarf_Die cu_die; /* Current CU */ | ||
93 | int found; | ||
94 | }; | ||
95 | |||
65 | #endif /* NO_LIBDWARF */ | 96 | #endif /* NO_LIBDWARF */ |
66 | 97 | ||
67 | #endif /*_PROBE_FINDER_H */ | 98 | #endif /*_PROBE_FINDER_H */ |