diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-10-08 17:17:38 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-10-12 17:31:52 -0400 |
commit | 4ea42b181434bfc6a0a18d32214130a242d489bf (patch) | |
tree | 2c467d795d90440e0293951087c41caca8397584 /tools/perf/builtin-probe.c | |
parent | e93f4d8539d5e9dd59f4af9d8ef4e9b62cfa1f81 (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/perf/builtin-probe.c')
-rw-r--r-- | tools/perf/builtin-probe.c | 358 |
1 files changed, 358 insertions, 0 deletions
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 | ||
44 | const 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 */ | ||
54 | static 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 | |||
62 | static void semantic_error(const char *msg) | ||
63 | { | ||
64 | fprintf(stderr, "Semantic error: %s\n", msg); | ||
65 | exit(1); | ||
66 | } | ||
67 | |||
68 | static void perror_exit(const char *msg) | ||
69 | { | ||
70 | perror(msg); | ||
71 | exit(1); | ||
72 | } | ||
73 | |||
74 | #define MAX_PROBE_ARGS 128 | ||
75 | |||
76 | static 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 | |||
185 | static 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 | |||
213 | static const char * const probe_usage[] = { | ||
214 | "perf probe [<options>] -P 'PROBEDEF' [-P 'PROBEDEF' ...]", | ||
215 | NULL | ||
216 | }; | ||
217 | |||
218 | static 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 | |||
238 | static 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 | |||
252 | static 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; | ||
273 | error: | ||
274 | free(pp->probes[0]); | ||
275 | if (ret > 0) | ||
276 | ret = -E2BIG; | ||
277 | return ret; | ||
278 | } | ||
279 | |||
280 | int 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 | |||
334 | setup_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 | |||