aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@redhat.com>2010-01-06 09:45:34 -0500
committerIngo Molnar <mingo@elte.hu>2010-01-13 04:09:14 -0500
commit631c9def804b2c92b5cca04fb9ff7b5df9e35094 (patch)
tree3ce09a1894f765bbea6538335ef11950287d66ee
parent6964cd2c8efe6e048401f1fe3952a06c563c34c1 (diff)
perf probe: Support --line option to show probable source-code lines
Add --line option to support showing probable source-code lines. perf probe --line SRC:LN[-LN|+NUM] or perf probe --line FUNC[:LN[-LN|+NUM]] This option shows source-code with line number if the line can be probed. Lines without line number (and blue color) means that the line can not be probed, because debuginfo doesn't have the information of those lines. The argument specifies the range of lines, "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. e.g. # ./perf probe --line kernel/sched.c:1080 <kernel/sched.c:1080> * * called with rq->lock held and irqs disabled */ static void hrtick_start(struct rq *rq, u64 delay) { struct hrtimer *timer = &rq->hrtick_timer; 1086 ktime_t time = ktime_add_ns(timer->base->get_time(), delay); hrtimer_set_expires(timer, time); 1090 if (rq == this_rq()) { 1091 hrtimer_restart(timer); 1092 } else if (!rq->hrtick_csd_pending) { 1093 __smp_call_function_single(cpu_of(rq), &rq->hrtick_csd, 1094 rq->hrtick_csd_pending = 1; If you specifying function name, this shows function-relative line number. # ./perf probe --line schedule <schedule:0> asmlinkage void __sched schedule(void) 1 { struct task_struct *prev, *next; unsigned long *switch_count; struct rq *rq; int cpu; need_resched: preempt_disable(); 9 cpu = smp_processor_id(); 10 rq = cpu_rq(cpu); 11 rcu_sched_qs(cpu); 12 prev = rq->curr; 13 switch_count = &prev->nivcsw; Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: systemtap <systemtap@sources.redhat.com> Cc: DLE <dle-develop@lists.sourceforge.net> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Mike Galbraith <efault@gmx.de> LKML-Reference: <20100106144534.27218.77939.stgit@dhcp-100-2-132.bos.redhat.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/Documentation/perf-probe.txt20
-rw-r--r--tools/perf/builtin-probe.c76
-rw-r--r--tools/perf/util/probe-event.c100
-rw-r--r--tools/perf/util/probe-event.h2
-rw-r--r--tools/perf/util/probe-finder.c191
-rw-r--r--tools/perf/util/probe-finder.h31
6 files changed, 402 insertions, 18 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 250e391b4bc8..2de34075f6a4 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' [...]
16or 16or
17'perf probe' --list 17'perf probe' --list
18or
19'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
18 20
19DESCRIPTION 21DESCRIPTION
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
48PROBE SYNTAX 55PROBE SYNTAX
49------------ 56------------
50Probe points are defined by following syntax. 57Probe points are defined by following syntax.
@@ -56,6 +63,19 @@ Probe points are defined by following syntax.
56It 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. 63It 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
66LINE SYNTAX
67-----------
68Line range is descripted by following syntax.
69
70 "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]"
71
72FUNC specifies the function name of showing lines. 'RLN' is the start line
73number from function entry line, and 'RLN2' is the end line number. As same as
74probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
75and 'ALN2' is end line number in the file. It is also possible to specify how
76many lines to show by using 'NUM'.
77So, "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
59SEE ALSO 79SEE ALSO
60-------- 80--------
61linkperf:perf-trace[1], linkperf:perf-record[1] 81linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index ffdd3fe87b4a..1d3a99ea5ce1 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
121static 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 */
120static void evaluate_probe_point(struct probe_point *pp) 131static 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 */
206static 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
188int cmd_probe(int argc, const char **argv, const char *prefix __used) 223int 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 a22141a773bc..71b0dd590a37 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
67void 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 */
67static bool check_event_name(const char *name) 104static 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
720static 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;
746error:
747 if (feof(fp))
748 die("Source file is shorter than expected.");
749 else
750 die("File read error: %s", strerror(errno));
751}
752
753void 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 7f1d499118c0..711287d4baea 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
8extern void parse_line_range_desc(const char *arg, struct line_range *lr);
8extern void parse_perf_probe_event(const char *str, struct probe_point *pp, 9extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
9 bool *need_dwarf); 10 bool *need_dwarf);
10extern int synthesize_perf_probe_point(struct probe_point *pp); 11extern 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);
16extern void del_trace_kprobe_events(struct strlist *dellist); 17extern void del_trace_kprobe_events(struct strlist *dellist);
17extern void show_perf_probe_events(void); 18extern void show_perf_probe_events(void);
19extern 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 6402798337c8..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
143static 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 */
144static int die_compare_name(Dwarf_Die dw_die, const char *tname) 169static 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 */
570static void find_by_line(struct probe_finder *pf) 595static 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
676static void find_by_func(struct probe_finder *pf) 701static 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
757static 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;
772found:
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 */
782static 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 */
820static 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
858static void find_line_range_by_func(struct line_finder *lf)
859{
860 search_die_from_children(lf->cu_die, linefunc_callback, lf);
861}
862
863int 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 e3f396806e6e..972b386116f1 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 */
38struct line_node {
39 struct list_head list;
40 unsigned int line;
41};
42
43/* Line range */
44struct 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
38extern int find_probepoint(int fd, struct probe_point *pp); 55extern int find_probepoint(int fd, struct probe_point *pp);
56extern 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
84struct 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 */