aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@redhat.com>2009-10-08 17:17:38 -0400
committerFrederic Weisbecker <fweisbec@gmail.com>2009-10-12 17:31:52 -0400
commit4ea42b181434bfc6a0a18d32214130a242d489bf (patch)
tree2c467d795d90440e0293951087c41caca8397584 /tools
parente93f4d8539d5e9dd59f4af9d8ef4e9b62cfa1f81 (diff)
perf: Add perf probe subcommand, a kprobe-event setup helper
Add perf probe subcommand that implements a kprobe-event setup helper to the perf command. This allows user to define kprobe events using C expressions (C line numbers, C function names, and C local variables). Usage ----- perf probe [<options>] -P 'PROBEDEF' [-P 'PROBEDEF' ...] -k, --vmlinux <file> vmlinux/module pathname -P, --probe <p|r:[GRP/]NAME FUNC[+OFFS][@SRC]|@SRC:LINE [ARG ...]> probe point definition, where p: kprobe probe r: kretprobe probe GRP: Group name (optional) NAME: Event name FUNC: Function name OFFS: Offset from function entry (in byte) SRC: Source code path LINE: Line number ARG: Probe argument (local variable name or kprobe-tracer argument format is supported.) Changes in v4: - Add _GNU_SOURCE macro for strndup(). Changes in v3: - Remove -r option because perf always be used for online kernel. - Check malloc/calloc results. Changes in v2: - Check synthesized string length. - Rename perf kprobe to perf probe. - Use spaces for separator and update usage comment. - Check error paths in parse_probepoint(). - Check optimized-out variables. Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Christoph Hellwig <hch@infradead.org> Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: Jim Keniston <jkenisto@us.ibm.com> Cc: Frank Ch. Eigler <fche@redhat.com> LKML-Reference: <20091008211737.29299.14784.stgit@dhcp-100-2-132.bos.redhat.com> Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Makefile10
-rw-r--r--tools/perf/builtin-probe.c358
-rw-r--r--tools/perf/builtin.h1
-rw-r--r--tools/perf/perf.c3
-rw-r--r--tools/perf/util/probe-finder.c690
-rw-r--r--tools/perf/util/probe-finder.h68
6 files changed, 1130 insertions, 0 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index b5f1953b6144..6dabcf1a4df6 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -419,6 +419,16 @@ ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf *
419 msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); 419 msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
420endif 420endif
421 421
422ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <libdwarf/libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y)
423 msg := $(warning No libdwarf.h found, disables probe subcommand. Please install libdwarf-dev/libdwarf-devel);
424else
425 EXTLIBS += -lelf -ldwarf
426 LIB_H += util/probe-finder.h
427 LIB_OBJS += util/probe-finder.o
428 BUILTIN_OBJS += builtin-probe.o
429 BASIC_CFLAGS += -DSUPPORT_DWARF
430endif
431
422ifdef NO_DEMANGLE 432ifdef NO_DEMANGLE
423 BASIC_CFLAGS += -DNO_DEMANGLE 433 BASIC_CFLAGS += -DNO_DEMANGLE
424else 434else
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
new file mode 100644
index 000000000000..24b64b5cefce
--- /dev/null
+++ b/tools/perf/builtin-probe.c
@@ -0,0 +1,358 @@
1/*
2 * builtin-probe.c
3 *
4 * Builtin probe command: Set up probe events by C expression
5 *
6 * Written by Masami Hiramatsu <mhiramat@redhat.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 */
23#define _GNU_SOURCE
24#include <sys/utsname.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <errno.h>
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <string.h>
33
34#undef _GNU_SOURCE
35#include "perf.h"
36#include "builtin.h"
37#include "util/util.h"
38#include "util/parse-options.h"
39#include "util/parse-events.h" /* For debugfs_path */
40#include "util/probe-finder.h"
41
42/* Default vmlinux search paths */
43#define NR_SEARCH_PATH 3
44const char *default_search_path[NR_SEARCH_PATH] = {
45"/lib/modules/%s/build/vmlinux", /* Custom build kernel */
46"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
47"/boot/vmlinux-debug-%s", /* Ubuntu */
48};
49
50#define MAX_PATH_LEN 256
51#define MAX_PROBES 128
52
53/* Session management structure */
54static struct {
55 char *vmlinux;
56 char *release;
57 int nr_probe;
58 struct probe_point probes[MAX_PROBES];
59 char *events[MAX_PROBES];
60} session;
61
62static void semantic_error(const char *msg)
63{
64 fprintf(stderr, "Semantic error: %s\n", msg);
65 exit(1);
66}
67
68static void perror_exit(const char *msg)
69{
70 perror(msg);
71 exit(1);
72}
73
74#define MAX_PROBE_ARGS 128
75
76static int parse_probepoint(const struct option *opt __used,
77 const char *str, int unset __used)
78{
79 char *argv[MAX_PROBE_ARGS + 2]; /* Event + probe + args */
80 int argc, i;
81 char *arg, *ptr;
82 struct probe_point *pp = &session.probes[session.nr_probe];
83 char **event = &session.events[session.nr_probe];
84 int retp = 0;
85
86 if (!str) /* The end of probe points */
87 return 0;
88
89 debug("Probe-define(%d): %s\n", session.nr_probe, str);
90 if (++session.nr_probe == MAX_PROBES)
91 semantic_error("Too many probes");
92
93 /* Separate arguments, similar to argv_split */
94 argc = 0;
95 do {
96 /* Skip separators */
97 while (isspace(*str))
98 str++;
99
100 /* Add an argument */
101 if (*str != '\0') {
102 const char *s = str;
103
104 /* Skip the argument */
105 while (!isspace(*str) && *str != '\0')
106 str++;
107
108 /* Duplicate the argument */
109 argv[argc] = strndup(s, str - s);
110 if (argv[argc] == NULL)
111 perror_exit("strndup");
112 if (++argc == MAX_PROBE_ARGS)
113 semantic_error("Too many arguments");
114 debug("argv[%d]=%s\n", argc, argv[argc - 1]);
115 }
116 } while (*str != '\0');
117 if (argc < 2)
118 semantic_error("Need event-name and probe-point at least.");
119
120 /* Parse the event name */
121 if (argv[0][0] == 'r')
122 retp = 1;
123 else if (argv[0][0] != 'p')
124 semantic_error("You must specify 'p'(kprobe) or"
125 " 'r'(kretprobe) first.");
126 /* TODO: check event name */
127 *event = argv[0];
128
129 /* Parse probe point */
130 arg = argv[1];
131 if (arg[0] == '@') {
132 /* Source Line */
133 arg++;
134 ptr = strchr(arg, ':');
135 if (!ptr || !isdigit(ptr[1]))
136 semantic_error("Line number is required.");
137 *ptr++ = '\0';
138 if (strlen(arg) == 0)
139 semantic_error("No file name.");
140 pp->file = strdup(arg);
141 pp->line = atoi(ptr);
142 if (!pp->file || !pp->line)
143 semantic_error("Failed to parse line.");
144 debug("file:%s line:%d\n", pp->file, pp->line);
145 } else {
146 /* Function name */
147 ptr = strchr(arg, '+');
148 if (ptr) {
149 if (!isdigit(ptr[1]))
150 semantic_error("Offset is required.");
151 *ptr++ = '\0';
152 pp->offset = atoi(ptr);
153 } else
154 ptr = arg;
155 ptr = strchr(ptr, '@');
156 if (ptr) {
157 *ptr++ = '\0';
158 pp->file = strdup(ptr);
159 }
160 pp->function = strdup(arg);
161 debug("symbol:%s file:%s offset:%d\n",
162 pp->function, pp->file, pp->offset);
163 }
164 free(argv[1]);
165
166 /* Copy arguments */
167 pp->nr_args = argc - 2;
168 if (pp->nr_args > 0) {
169 pp->args = (char **)malloc(sizeof(char *) * pp->nr_args);
170 if (!pp->args)
171 perror_exit("malloc");
172 memcpy(pp->args, &argv[2], sizeof(char *) * pp->nr_args);
173 }
174
175 /* Ensure return probe has no C argument */
176 if (retp)
177 for (i = 0; i < pp->nr_args; i++)
178 if (is_c_varname(pp->args[i]))
179 semantic_error("You can't specify local"
180 " variable for kretprobe");
181 debug("%d arguments\n", pp->nr_args);
182 return 0;
183}
184
185static int open_default_vmlinux(void)
186{
187 struct utsname uts;
188 char fname[MAX_PATH_LEN];
189 int fd, ret, i;
190
191 ret = uname(&uts);
192 if (ret) {
193 debug("uname() failed.\n");
194 return -errno;
195 }
196 session.release = uts.release;
197 for (i = 0; i < NR_SEARCH_PATH; i++) {
198 ret = snprintf(fname, MAX_PATH_LEN,
199 default_search_path[i], session.release);
200 if (ret >= MAX_PATH_LEN || ret < 0) {
201 debug("Filename(%d,%s) is too long.\n", i, uts.release);
202 errno = E2BIG;
203 return -E2BIG;
204 }
205 debug("try to open %s\n", fname);
206 fd = open(fname, O_RDONLY);
207 if (fd >= 0)
208 break;
209 }
210 return fd;
211}
212
213static const char * const probe_usage[] = {
214 "perf probe [<options>] -P 'PROBEDEF' [-P 'PROBEDEF' ...]",
215 NULL
216};
217
218static const struct option options[] = {
219 OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
220 "vmlinux/module pathname"),
221 OPT_CALLBACK('P', "probe", NULL,
222 "p|r:[GRP/]NAME FUNC[+OFFS][@SRC]|@SRC:LINE [ARG ...]",
223 "probe point definition, where\n"
224 "\t\tp:\tkprobe probe\n"
225 "\t\tr:\tkretprobe probe\n"
226 "\t\tGRP:\tGroup name (optional)\n"
227 "\t\tNAME:\tEvent name\n"
228 "\t\tFUNC:\tFunction name\n"
229 "\t\tOFFS:\tOffset from function entry (in byte)\n"
230 "\t\tSRC:\tSource code path\n"
231 "\t\tLINE:\tLine number\n"
232 "\t\tARG:\tProbe argument (local variable name or\n"
233 "\t\t\tkprobe-tracer argument format is supported.)\n",
234 parse_probepoint),
235 OPT_END()
236};
237
238static int write_new_event(int fd, const char *buf)
239{
240 int ret;
241
242 printf("Adding new event: %s\n", buf);
243 ret = write(fd, buf, strlen(buf));
244 if (ret <= 0)
245 perror("Error: Failed to create event");
246
247 return ret;
248}
249
250#define MAX_CMDLEN 256
251
252static int synthesize_probepoint(struct probe_point *pp)
253{
254 char *buf;
255 int i, len, ret;
256 pp->probes[0] = buf = (char *)calloc(MAX_CMDLEN, sizeof(char));
257 if (!buf)
258 perror_exit("calloc");
259 ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
260 if (ret <= 0 || ret >= MAX_CMDLEN)
261 goto error;
262 len = ret;
263
264 for (i = 0; i < pp->nr_args; i++) {
265 ret = snprintf(&buf[len], MAX_CMDLEN - len, " %s",
266 pp->args[i]);
267 if (ret <= 0 || ret >= MAX_CMDLEN - len)
268 goto error;
269 len += ret;
270 }
271 pp->found = 1;
272 return pp->found;
273error:
274 free(pp->probes[0]);
275 if (ret > 0)
276 ret = -E2BIG;
277 return ret;
278}
279
280int cmd_probe(int argc, const char **argv, const char *prefix __used)
281{
282 int i, j, fd, ret, need_dwarf = 0;
283 struct probe_point *pp;
284 char buf[MAX_CMDLEN];
285
286 argc = parse_options(argc, argv, options, probe_usage,
287 PARSE_OPT_STOP_AT_NON_OPTION);
288 if (argc || session.nr_probe == 0)
289 usage_with_options(probe_usage, options);
290
291 /* Synthesize return probes */
292 for (j = 0; j < session.nr_probe; j++) {
293 if (session.events[j][0] != 'r') {
294 need_dwarf = 1;
295 continue;
296 }
297 ret = synthesize_probepoint(&session.probes[j]);
298 if (ret == -E2BIG)
299 semantic_error("probe point is too long.");
300 else if (ret < 0) {
301 perror("snprintf");
302 return -1;
303 }
304 }
305
306 if (!need_dwarf)
307 goto setup_probes;
308
309 if (session.vmlinux)
310 fd = open(session.vmlinux, O_RDONLY);
311 else
312 fd = open_default_vmlinux();
313 if (fd < 0) {
314 perror("vmlinux/module file open");
315 return -1;
316 }
317
318 /* Searching probe points */
319 for (j = 0; j < session.nr_probe; j++) {
320 pp = &session.probes[j];
321 if (pp->found)
322 continue;
323
324 lseek(fd, SEEK_SET, 0);
325 ret = find_probepoint(fd, pp);
326 if (ret <= 0) {
327 fprintf(stderr, "Error: No probe point found.\n");
328 return -1;
329 }
330 debug("probe event %s found\n", session.events[j]);
331 }
332 close(fd);
333
334setup_probes:
335 /* Settng up probe points */
336 snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path);
337 fd = open(buf, O_WRONLY, O_APPEND);
338 if (fd < 0) {
339 perror("kprobe_events open");
340 return -1;
341 }
342 for (j = 0; j < session.nr_probe; j++) {
343 pp = &session.probes[j];
344 if (pp->found == 1) {
345 snprintf(buf, MAX_CMDLEN, "%s %s\n",
346 session.events[j], pp->probes[0]);
347 write_new_event(fd, buf);
348 } else
349 for (i = 0; i < pp->found; i++) {
350 snprintf(buf, MAX_CMDLEN, "%s%d %s\n",
351 session.events[j], i, pp->probes[i]);
352 write_new_event(fd, buf);
353 }
354 }
355 close(fd);
356 return 0;
357}
358
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index e11d8d231c3b..ad5f0f4c49ee 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -25,5 +25,6 @@ extern int cmd_timechart(int argc, const char **argv, const char *prefix);
25extern int cmd_top(int argc, const char **argv, const char *prefix); 25extern int cmd_top(int argc, const char **argv, const char *prefix);
26extern int cmd_trace(int argc, const char **argv, const char *prefix); 26extern int cmd_trace(int argc, const char **argv, const char *prefix);
27extern int cmd_version(int argc, const char **argv, const char *prefix); 27extern int cmd_version(int argc, const char **argv, const char *prefix);
28extern int cmd_probe(int argc, const char **argv, const char *prefix);
28 29
29#endif 30#endif
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 19fc7feb9d59..f598120c0097 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -295,6 +295,9 @@ static void handle_internal_command(int argc, const char **argv)
295 { "version", cmd_version, 0 }, 295 { "version", cmd_version, 0 },
296 { "trace", cmd_trace, 0 }, 296 { "trace", cmd_trace, 0 },
297 { "sched", cmd_sched, 0 }, 297 { "sched", cmd_sched, 0 },
298#ifdef SUPPORT_DWARF
299 { "probe", cmd_probe, 0 },
300#endif
298 }; 301 };
299 unsigned int i; 302 unsigned int i;
300 static const char ext[] = STRIP_EXTENSION; 303 static const char ext[] = STRIP_EXTENSION;
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
new file mode 100644
index 000000000000..ec6f53f29e00
--- /dev/null
+++ b/tools/perf/util/probe-finder.c
@@ -0,0 +1,690 @@
1/*
2 * probe-finder.c : C expression to kprobe event converter
3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 */
21
22#include <sys/utsname.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <errno.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <getopt.h>
30#include <stdlib.h>
31#include <string.h>
32#include <stdarg.h>
33#include <ctype.h>
34#include "probe-finder.h"
35
36
37/* Dwarf_Die Linkage to parent Die */
38struct die_link {
39 struct die_link *parent; /* Parent die */
40 Dwarf_Die die; /* Current die */
41};
42
43static Dwarf_Debug __dw_debug;
44static Dwarf_Error __dw_error;
45
46static void msg_exit(int ret, const char *fmt, ...)
47{
48 va_list ap;
49
50 va_start(ap, fmt);
51 fprintf(stderr, "Error: ");
52 vfprintf(stderr, fmt, ap);
53 va_end(ap);
54
55 fprintf(stderr, "\n");
56 exit(ret);
57}
58
59
60/*
61 * Generic dwarf analysis helpers
62 */
63
64#define X86_32_MAX_REGS 8
65const char *x86_32_regs_table[X86_32_MAX_REGS] = {
66 "%ax",
67 "%cx",
68 "%dx",
69 "%bx",
70 "$stack", /* Stack address instead of %sp */
71 "%bp",
72 "%si",
73 "%di",
74};
75
76#define X86_64_MAX_REGS 16
77const char *x86_64_regs_table[X86_64_MAX_REGS] = {
78 "%ax",
79 "%dx",
80 "%cx",
81 "%bx",
82 "%si",
83 "%di",
84 "%bp",
85 "%sp",
86 "%r8",
87 "%r9",
88 "%r10",
89 "%r11",
90 "%r12",
91 "%r13",
92 "%r14",
93 "%r15",
94};
95
96/* TODO: switching by dwarf address size */
97#ifdef __x86_64__
98#define ARCH_MAX_REGS X86_64_MAX_REGS
99#define arch_regs_table x86_64_regs_table
100#else
101#define ARCH_MAX_REGS X86_32_MAX_REGS
102#define arch_regs_table x86_32_regs_table
103#endif
104
105/* Return architecture dependent register string (for kprobe-tracer) */
106static const char *get_arch_regstr(unsigned int n)
107{
108 return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
109}
110
111/*
112 * Compare the tail of two strings.
113 * Return 0 if whole of either string is same as another's tail part.
114 */
115static int strtailcmp(const char *s1, const char *s2)
116{
117 int i1 = strlen(s1);
118 int i2 = strlen(s2);
119 while (--i1 > 0 && --i2 > 0) {
120 if (s1[i1] != s2[i2])
121 return s1[i1] - s2[i2];
122 }
123 return 0;
124}
125
126/* Find the fileno of the target file. */
127static Dwarf_Unsigned die_get_fileno(Dwarf_Die cu_die, const char *fname)
128{
129 Dwarf_Signed cnt, i;
130 Dwarf_Unsigned found = 0;
131 char **srcs;
132 int ret;
133
134 if (!fname)
135 return 0;
136
137 ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
138 if (ret == DW_DLV_OK) {
139 for (i = 0; i < cnt && !found; i++) {
140 if (strtailcmp(srcs[i], fname) == 0)
141 found = i + 1;
142 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
143 }
144 for (; i < cnt; i++)
145 dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
146 dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
147 }
148 if (found)
149 debug("found fno: %d\n", (int)found);
150 return found;
151}
152
153/* Compare diename and tname */
154static int die_compare_name(Dwarf_Die die, const char *tname)
155{
156 char *name;
157 int ret;
158 ret = dwarf_diename(die, &name, &__dw_error);
159 ERR_IF(ret == DW_DLV_ERROR);
160 if (ret == DW_DLV_OK) {
161 ret = strcmp(tname, name);
162 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
163 } else
164 ret = -1;
165 return ret;
166}
167
168/* Check the address is in the subprogram(function). */
169static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr,
170 Dwarf_Signed *offs)
171{
172 Dwarf_Addr lopc, hipc;
173 int ret;
174
175 /* TODO: check ranges */
176 ret = dwarf_lowpc(sp_die, &lopc, &__dw_error);
177 ERR_IF(ret == DW_DLV_ERROR);
178 if (ret == DW_DLV_NO_ENTRY)
179 return 0;
180 ret = dwarf_highpc(sp_die, &hipc, &__dw_error);
181 ERR_IF(ret != DW_DLV_OK);
182 if (lopc <= addr && addr < hipc) {
183 *offs = addr - lopc;
184 return 1;
185 } else
186 return 0;
187}
188
189/* Check the die is inlined function */
190static Dwarf_Bool die_inlined_subprogram(Dwarf_Die die)
191{
192 /* TODO: check strictly */
193 Dwarf_Bool inl;
194 int ret;
195
196 ret = dwarf_hasattr(die, DW_AT_inline, &inl, &__dw_error);
197 ERR_IF(ret == DW_DLV_ERROR);
198 return inl;
199}
200
201/* Get the offset of abstruct_origin */
202static Dwarf_Off die_get_abstract_origin(Dwarf_Die die)
203{
204 Dwarf_Attribute attr;
205 Dwarf_Off cu_offs;
206 int ret;
207
208 ret = dwarf_attr(die, DW_AT_abstract_origin, &attr, &__dw_error);
209 ERR_IF(ret != DW_DLV_OK);
210 ret = dwarf_formref(attr, &cu_offs, &__dw_error);
211 ERR_IF(ret != DW_DLV_OK);
212 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
213 return cu_offs;
214}
215
216/* Get entry pc(or low pc, 1st entry of ranges) of the die */
217static Dwarf_Addr die_get_entrypc(Dwarf_Die die)
218{
219 Dwarf_Attribute attr;
220 Dwarf_Addr addr;
221 Dwarf_Off offs;
222 Dwarf_Ranges *ranges;
223 Dwarf_Signed cnt;
224 int ret;
225
226 /* Try to get entry pc */
227 ret = dwarf_attr(die, DW_AT_entry_pc, &attr, &__dw_error);
228 ERR_IF(ret == DW_DLV_ERROR);
229 if (ret == DW_DLV_OK) {
230 ret = dwarf_formaddr(attr, &addr, &__dw_error);
231 ERR_IF(ret != DW_DLV_OK);
232 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
233 return addr;
234 }
235
236 /* Try to get low pc */
237 ret = dwarf_lowpc(die, &addr, &__dw_error);
238 ERR_IF(ret == DW_DLV_ERROR);
239 if (ret == DW_DLV_OK)
240 return addr;
241
242 /* Try to get ranges */
243 ret = dwarf_attr(die, DW_AT_ranges, &attr, &__dw_error);
244 ERR_IF(ret != DW_DLV_OK);
245 ret = dwarf_formref(attr, &offs, &__dw_error);
246 ERR_IF(ret != DW_DLV_OK);
247 ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL,
248 &__dw_error);
249 ERR_IF(ret != DW_DLV_OK);
250 addr = ranges[0].dwr_addr1;
251 dwarf_ranges_dealloc(__dw_debug, ranges, cnt);
252 return addr;
253}
254
255/*
256 * Search a Die from Die tree.
257 * Note: cur_link->die should be deallocated in this function.
258 */
259static int __search_die_tree(struct die_link *cur_link,
260 int (*die_cb)(struct die_link *, void *),
261 void *data)
262{
263 Dwarf_Die new_die;
264 struct die_link new_link;
265 int ret;
266
267 if (!die_cb)
268 return 0;
269
270 /* Check current die */
271 while (!(ret = die_cb(cur_link, data))) {
272 /* Check child die */
273 ret = dwarf_child(cur_link->die, &new_die, &__dw_error);
274 ERR_IF(ret == DW_DLV_ERROR);
275 if (ret == DW_DLV_OK) {
276 new_link.parent = cur_link;
277 new_link.die = new_die;
278 ret = __search_die_tree(&new_link, die_cb, data);
279 if (ret)
280 break;
281 }
282
283 /* Move to next sibling */
284 ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die,
285 &__dw_error);
286 ERR_IF(ret == DW_DLV_ERROR);
287 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
288 cur_link->die = new_die;
289 if (ret == DW_DLV_NO_ENTRY)
290 return 0;
291 }
292 dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE);
293 return ret;
294}
295
296/* Search a die in its children's die tree */
297static int search_die_from_children(Dwarf_Die parent_die,
298 int (*die_cb)(struct die_link *, void *),
299 void *data)
300{
301 struct die_link new_link;
302 int ret;
303
304 new_link.parent = NULL;
305 ret = dwarf_child(parent_die, &new_link.die, &__dw_error);
306 ERR_IF(ret == DW_DLV_ERROR);
307 if (ret == DW_DLV_OK)
308 return __search_die_tree(&new_link, die_cb, data);
309 else
310 return 0;
311}
312
313/* Find a locdesc corresponding to the address */
314static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc,
315 Dwarf_Addr addr)
316{
317 Dwarf_Signed lcnt;
318 Dwarf_Locdesc **llbuf;
319 int ret, i;
320
321 ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error);
322 ERR_IF(ret != DW_DLV_OK);
323 ret = DW_DLV_NO_ENTRY;
324 for (i = 0; i < lcnt; ++i) {
325 if (llbuf[i]->ld_lopc <= addr &&
326 llbuf[i]->ld_hipc > addr) {
327 memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc));
328 desc->ld_s =
329 malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
330 ERR_IF(desc->ld_s == NULL);
331 memcpy(desc->ld_s, llbuf[i]->ld_s,
332 sizeof(Dwarf_Loc) * llbuf[i]->ld_cents);
333 ret = DW_DLV_OK;
334 break;
335 }
336 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
337 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
338 }
339 /* Releasing loop */
340 for (; i < lcnt; ++i) {
341 dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK);
342 dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC);
343 }
344 dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST);
345 return ret;
346}
347
348/*
349 * Probe finder related functions
350 */
351
352/* Show a location */
353static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
354{
355 Dwarf_Small op;
356 Dwarf_Unsigned regn;
357 Dwarf_Signed offs;
358 int deref = 0, ret;
359 const char *regs;
360
361 op = loc->lr_atom;
362
363 /* If this is based on frame buffer, set the offset */
364 if (op == DW_OP_fbreg) {
365 deref = 1;
366 offs = (Dwarf_Signed)loc->lr_number;
367 op = pf->fbloc.ld_s[0].lr_atom;
368 loc = &pf->fbloc.ld_s[0];
369 } else
370 offs = 0;
371
372 if (op >= DW_OP_breg0 && op <= DW_OP_breg31) {
373 regn = op - DW_OP_breg0;
374 offs += (Dwarf_Signed)loc->lr_number;
375 deref = 1;
376 } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) {
377 regn = op - DW_OP_reg0;
378 } else if (op == DW_OP_bregx) {
379 regn = loc->lr_number;
380 offs += (Dwarf_Signed)loc->lr_number2;
381 deref = 1;
382 } else if (op == DW_OP_regx) {
383 regn = loc->lr_number;
384 } else
385 msg_exit(-EINVAL, "Dwarf_OP %d is not supported.\n", op);
386
387 regs = get_arch_regstr(regn);
388 if (!regs)
389 msg_exit(-EINVAL, "%lld exceeds max register number.\n", regn);
390
391 if (deref)
392 ret = snprintf(pf->buf, pf->len,
393 " %s=%+lld(%s)", pf->var, offs, regs);
394 else
395 ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs);
396 ERR_IF(ret < 0);
397 ERR_IF(ret >= pf->len);
398}
399
400/* Show a variables in kprobe event format */
401static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
402{
403 Dwarf_Attribute attr;
404 Dwarf_Locdesc ld;
405 int ret;
406
407 ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error);
408 if (ret != DW_DLV_OK)
409 goto error;
410 ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base));
411 if (ret != DW_DLV_OK)
412 goto error;
413 /* TODO? */
414 ERR_IF(ld.ld_cents != 1);
415 show_location(&ld.ld_s[0], pf);
416 free(ld.ld_s);
417 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
418 return ;
419error:
420 msg_exit(-1, "Failed to find the location of %s at this address.\n"
421 " Perhaps, it was optimized out.\n", pf->var);
422}
423
424static int variable_callback(struct die_link *dlink, void *data)
425{
426 struct probe_finder *pf = (struct probe_finder *)data;
427 Dwarf_Half tag;
428 int ret;
429
430 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
431 ERR_IF(ret == DW_DLV_ERROR);
432 if ((tag == DW_TAG_formal_parameter ||
433 tag == DW_TAG_variable) &&
434 (die_compare_name(dlink->die, pf->var) == 0)) {
435 show_variable(dlink->die, pf);
436 return 1;
437 }
438 /* TODO: Support struct members and arrays */
439 return 0;
440}
441
442/* Find a variable in a subprogram die */
443static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
444{
445 int ret;
446
447 if (!is_c_varname(pf->var)) {
448 /* Output raw parameters */
449 ret = snprintf(pf->buf, pf->len, " %s", pf->var);
450 ERR_IF(ret < 0);
451 ERR_IF(ret >= pf->len);
452 return ;
453 }
454
455 debug("Searching '%s' variable in context.\n", pf->var);
456 /* Search child die for local variables and parameters. */
457 ret = search_die_from_children(sp_die, variable_callback, pf);
458 if (!ret)
459 msg_exit(-1, "Failed to find '%s' in this function.\n",
460 pf->var);
461}
462
463/* Get a frame base on the address */
464static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf)
465{
466 Dwarf_Attribute attr;
467 int ret;
468
469 ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error);
470 ERR_IF(ret != DW_DLV_OK);
471 ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base));
472 ERR_IF(ret != DW_DLV_OK);
473 dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR);
474}
475
476static void free_current_frame_base(struct probe_finder *pf)
477{
478 free(pf->fbloc.ld_s);
479 memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc));
480}
481
482/* Show a probe point to output buffer */
483static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs,
484 struct probe_finder *pf)
485{
486 struct probe_point *pp = pf->pp;
487 char *name;
488 char tmp[MAX_PROBE_BUFFER];
489 int ret, i, len;
490
491 /* Output name of probe point */
492 ret = dwarf_diename(sp_die, &name, &__dw_error);
493 ERR_IF(ret == DW_DLV_ERROR);
494 if (ret == DW_DLV_OK) {
495 ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name,
496 (unsigned int)offs);
497 dwarf_dealloc(__dw_debug, name, DW_DLA_STRING);
498 } else {
499 /* This function has no name. */
500 ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr);
501 }
502 ERR_IF(ret < 0);
503 ERR_IF(ret >= MAX_PROBE_BUFFER);
504 len = ret;
505
506 /* Find each argument */
507 get_current_frame_base(sp_die, pf);
508 for (i = 0; i < pp->nr_args; i++) {
509 pf->var = pp->args[i];
510 pf->buf = &tmp[len];
511 pf->len = MAX_PROBE_BUFFER - len;
512 find_variable(sp_die, pf);
513 len += strlen(pf->buf);
514 }
515 free_current_frame_base(pf);
516
517 pp->probes[pp->found] = strdup(tmp);
518 pp->found++;
519}
520
521static int probeaddr_callback(struct die_link *dlink, void *data)
522{
523 struct probe_finder *pf = (struct probe_finder *)data;
524 Dwarf_Half tag;
525 Dwarf_Signed offs;
526 int ret;
527
528 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
529 ERR_IF(ret == DW_DLV_ERROR);
530 /* Check the address is in this subprogram */
531 if (tag == DW_TAG_subprogram &&
532 die_within_subprogram(dlink->die, pf->addr, &offs)) {
533 show_probepoint(dlink->die, offs, pf);
534 return 1;
535 }
536 return 0;
537}
538
539/* Find probe point from its line number */
540static void find_by_line(Dwarf_Die cu_die, struct probe_finder *pf)
541{
542 struct probe_point *pp = pf->pp;
543 Dwarf_Signed cnt, i;
544 Dwarf_Line *lines;
545 Dwarf_Unsigned lineno = 0;
546 Dwarf_Addr addr;
547 Dwarf_Unsigned fno;
548 int ret;
549
550 ret = dwarf_srclines(cu_die, &lines, &cnt, &__dw_error);
551 ERR_IF(ret != DW_DLV_OK);
552
553 for (i = 0; i < cnt; i++) {
554 ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
555 ERR_IF(ret != DW_DLV_OK);
556 if (fno != pf->fno)
557 continue;
558
559 ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
560 ERR_IF(ret != DW_DLV_OK);
561 if (lineno != (Dwarf_Unsigned)pp->line)
562 continue;
563
564 ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
565 ERR_IF(ret != DW_DLV_OK);
566 debug("Probe point found: 0x%llx\n", addr);
567 pf->addr = addr;
568 /* Search a real subprogram including this line, */
569 ret = search_die_from_children(cu_die, probeaddr_callback, pf);
570 if (ret == 0)
571 msg_exit(-1,
572 "Probe point is not found in subprograms.\n");
573 /* Continuing, because target line might be inlined. */
574 }
575 dwarf_srclines_dealloc(__dw_debug, lines, cnt);
576}
577
578/* Search function from function name */
579static int probefunc_callback(struct die_link *dlink, void *data)
580{
581 struct probe_finder *pf = (struct probe_finder *)data;
582 struct probe_point *pp = pf->pp;
583 struct die_link *lk;
584 Dwarf_Signed offs;
585 Dwarf_Half tag;
586 int ret;
587
588 ret = dwarf_tag(dlink->die, &tag, &__dw_error);
589 ERR_IF(ret == DW_DLV_ERROR);
590 if (tag == DW_TAG_subprogram) {
591 if (die_compare_name(dlink->die, pp->function) == 0) {
592 if (die_inlined_subprogram(dlink->die)) {
593 /* Inlined function, save it. */
594 ret = dwarf_die_CU_offset(dlink->die,
595 &pf->inl_offs,
596 &__dw_error);
597 ERR_IF(ret != DW_DLV_OK);
598 debug("inline definition offset %lld\n",
599 pf->inl_offs);
600 return 0;
601 }
602 /* Get probe address */
603 pf->addr = die_get_entrypc(dlink->die);
604 pf->addr += pp->offset;
605 /* TODO: Check the address in this function */
606 show_probepoint(dlink->die, pp->offset, pf);
607 /* Continue to search */
608 }
609 } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) {
610 if (die_get_abstract_origin(dlink->die) == pf->inl_offs) {
611 /* Get probe address */
612 pf->addr = die_get_entrypc(dlink->die);
613 pf->addr += pp->offset;
614 debug("found inline addr: 0x%llx\n", pf->addr);
615 /* Inlined function. Get a real subprogram */
616 for (lk = dlink->parent; lk != NULL; lk = lk->parent) {
617 tag = 0;
618 dwarf_tag(lk->die, &tag, &__dw_error);
619 ERR_IF(ret == DW_DLV_ERROR);
620 if (tag == DW_TAG_subprogram &&
621 !die_inlined_subprogram(lk->die))
622 goto found;
623 }
624 msg_exit(-1, "Failed to find real subprogram.\n");
625found:
626 /* Get offset from subprogram */
627 ret = die_within_subprogram(lk->die, pf->addr, &offs);
628 ERR_IF(!ret);
629 show_probepoint(lk->die, offs, pf);
630 /* Continue to search */
631 }
632 }
633 return 0;
634}
635
636static void find_by_func(Dwarf_Die cu_die, struct probe_finder *pf)
637{
638 search_die_from_children(cu_die, probefunc_callback, pf);
639}
640
641/* Find a probe point */
642int find_probepoint(int fd, struct probe_point *pp)
643{
644 Dwarf_Half addr_size = 0;
645 Dwarf_Unsigned next_cuh = 0;
646 Dwarf_Die cu_die = 0;
647 int cu_number = 0, ret;
648 struct probe_finder pf = {.pp = pp};
649
650 ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
651 if (ret != DW_DLV_OK)
652 msg_exit(-1, "Failed to call dwarf_init(). "
653 "Maybe, not a dwarf file?\n");
654
655 pp->found = 0;
656 while (++cu_number) {
657 /* Search CU (Compilation Unit) */
658 ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
659 &addr_size, &next_cuh, &__dw_error);
660 ERR_IF(ret == DW_DLV_ERROR);
661 if (ret == DW_DLV_NO_ENTRY)
662 break;
663
664 /* Get the DIE(Debugging Information Entry) of this CU */
665 ret = dwarf_siblingof(__dw_debug, 0, &cu_die, &__dw_error);
666 ERR_IF(ret != DW_DLV_OK);
667
668 /* Check if target file is included. */
669 if (pp->file)
670 pf.fno = die_get_fileno(cu_die, pp->file);
671
672 if (!pp->file || pf.fno) {
673 /* Save CU base address (for frame_base) */
674 ret = dwarf_lowpc(cu_die, &pf.cu_base, &__dw_error);
675 ERR_IF(ret == DW_DLV_ERROR);
676 if (ret == DW_DLV_NO_ENTRY)
677 pf.cu_base = 0;
678 if (pp->line)
679 find_by_line(cu_die, &pf);
680 if (pp->function)
681 find_by_func(cu_die, &pf);
682 }
683 dwarf_dealloc(__dw_debug, cu_die, DW_DLA_DIE);
684 }
685 ret = dwarf_finish(__dw_debug, &__dw_error);
686 ERR_IF(ret != DW_DLV_OK);
687
688 return pp->found;
689}
690
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
new file mode 100644
index 000000000000..af920de64866
--- /dev/null
+++ b/tools/perf/util/probe-finder.h
@@ -0,0 +1,68 @@
1#ifndef _PROBE_FINDER_H
2#define _PROBE_FINDER_H
3
4#define _stringify(n) #n
5#define stringify(n) _stringify(n)
6
7#ifdef DEBUG
8#define debug(fmt ...) \
9 fprintf(stderr, "DBG(" __FILE__ ":" stringify(__LINE__) "): " fmt)
10#else
11#define debug(fmt ...) do {} while (0)
12#endif
13
14#define ERR_IF(cnd) \
15 do { if (cnd) { \
16 fprintf(stderr, "Error (" __FILE__ ":" stringify(__LINE__) \
17 "): " stringify(cnd) "\n"); \
18 exit(1); \
19 } } while (0)
20
21#define MAX_PATH_LEN 256
22#define MAX_PROBE_BUFFER 1024
23#define MAX_PROBES 128
24
25static inline int is_c_varname(const char *name)
26{
27 /* TODO */
28 return isalpha(name[0]) || name[0] == '_';
29}
30
31struct probe_point {
32 /* Inputs */
33 char *file; /* File name */
34 int line; /* Line number */
35
36 char *function; /* Function name */
37 int offset; /* Offset bytes */
38
39 int nr_args; /* Number of arguments */
40 char **args; /* Arguments */
41
42 /* Output */
43 int found; /* Number of found probe points */
44 char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/
45};
46
47extern int find_probepoint(int fd, struct probe_point *pp);
48
49#include <libdwarf/dwarf.h>
50#include <libdwarf/libdwarf.h>
51
52struct probe_finder {
53 struct probe_point *pp; /* Target probe point */
54
55 /* For function searching */
56 Dwarf_Addr addr; /* Address */
57 Dwarf_Unsigned fno; /* File number */
58 Dwarf_Off inl_offs; /* Inline offset */
59
60 /* For variable searching */
61 Dwarf_Addr cu_base; /* Current CU base address */
62 Dwarf_Locdesc fbloc; /* Location of Current Frame Base */
63 const char *var; /* Current variable name */
64 char *buf; /* Current output buffer */
65 int len; /* Length of output buffer */
66};
67
68#endif /*_PROBE_FINDER_H */