diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-11-30 19:20:17 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-12-01 02:20:02 -0500 |
commit | 4de189fe6e5ad8241f6f8709d2e2ba4c3aeae33a (patch) | |
tree | ba4d4d6771df1a7d92bd7f8d0c2bce6369e67124 | |
parent | e1c01d61a98703fcc80d15b8068ec36d5a215f7e (diff) |
perf probe: Add --list option for listing current probe events
Add --list option for listing currently defined probe events
in the kernel. This shows events in below format;
[group:event] <perf-probe probe-definition>
for example:
[probe:schedule_0] schedule+30 cpu
Note that source file/line information is not supported yet.
So even if you added a probe by line, it will be shown in
<symbol+offset>.
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20091201002017.10235.76575.stgit@harusame>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | tools/perf/builtin-probe.c | 12 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 231 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 5 |
3 files changed, 230 insertions, 18 deletions
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index bf20df2e816d..b5d15cf25471 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -62,6 +62,8 @@ static struct { | |||
62 | struct probe_point probes[MAX_PROBES]; | 62 | struct probe_point probes[MAX_PROBES]; |
63 | } session; | 63 | } session; |
64 | 64 | ||
65 | static bool listing; | ||
66 | |||
65 | /* Parse an event definition. Note that any error must die. */ | 67 | /* Parse an event definition. Note that any error must die. */ |
66 | static void parse_probe_event(const char *str) | 68 | static void parse_probe_event(const char *str) |
67 | { | 69 | { |
@@ -119,6 +121,7 @@ static int open_default_vmlinux(void) | |||
119 | static const char * const probe_usage[] = { | 121 | static const char * const probe_usage[] = { |
120 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", | 122 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", |
121 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | 123 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", |
124 | "perf probe --list", | ||
122 | NULL | 125 | NULL |
123 | }; | 126 | }; |
124 | 127 | ||
@@ -129,6 +132,7 @@ static const struct option options[] = { | |||
129 | OPT_STRING('k', "vmlinux", &session.vmlinux, "file", | 132 | OPT_STRING('k', "vmlinux", &session.vmlinux, "file", |
130 | "vmlinux/module pathname"), | 133 | "vmlinux/module pathname"), |
131 | #endif | 134 | #endif |
135 | OPT_BOOLEAN('l', "list", &listing, "list up current probes"), | ||
132 | OPT_CALLBACK('a', "add", NULL, | 136 | OPT_CALLBACK('a', "add", NULL, |
133 | #ifdef NO_LIBDWARF | 137 | #ifdef NO_LIBDWARF |
134 | "FUNC[+OFFS|%return] [ARG ...]", | 138 | "FUNC[+OFFS|%return] [ARG ...]", |
@@ -164,9 +168,15 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
164 | for (i = 0; i < argc; i++) | 168 | for (i = 0; i < argc; i++) |
165 | parse_probe_event(argv[i]); | 169 | parse_probe_event(argv[i]); |
166 | 170 | ||
167 | if (session.nr_probe == 0) | 171 | if ((session.nr_probe == 0 && !listing) || |
172 | (session.nr_probe != 0 && listing)) | ||
168 | usage_with_options(probe_usage, options); | 173 | usage_with_options(probe_usage, options); |
169 | 174 | ||
175 | if (listing) { | ||
176 | show_perf_probe_events(); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
170 | if (session.need_dwarf) | 180 | if (session.need_dwarf) |
171 | #ifdef NO_LIBDWARF | 181 | #ifdef NO_LIBDWARF |
172 | die("Debuginfo-analysis is not supported"); | 182 | die("Debuginfo-analysis is not supported"); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index e3a683ab976f..7f4f288c642e 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -29,10 +29,13 @@ | |||
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | ||
33 | #include <limits.h> | ||
32 | 34 | ||
33 | #undef _GNU_SOURCE | 35 | #undef _GNU_SOURCE |
34 | #include "event.h" | 36 | #include "event.h" |
35 | #include "string.h" | 37 | #include "string.h" |
38 | #include "strlist.h" | ||
36 | #include "debug.h" | 39 | #include "debug.h" |
37 | #include "parse-events.h" /* For debugfs_path */ | 40 | #include "parse-events.h" /* For debugfs_path */ |
38 | #include "probe-event.h" | 41 | #include "probe-event.h" |
@@ -43,6 +46,19 @@ | |||
43 | 46 | ||
44 | #define semantic_error(msg ...) die("Semantic error :" msg) | 47 | #define semantic_error(msg ...) die("Semantic error :" msg) |
45 | 48 | ||
49 | /* If there is no space to write, returns -E2BIG. */ | ||
50 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
51 | { | ||
52 | int ret; | ||
53 | va_list ap; | ||
54 | va_start(ap, format); | ||
55 | ret = vsnprintf(str, size, format, ap); | ||
56 | va_end(ap); | ||
57 | if (ret >= (int)size) | ||
58 | ret = -E2BIG; | ||
59 | return ret; | ||
60 | } | ||
61 | |||
46 | /* Parse probepoint definition. */ | 62 | /* Parse probepoint definition. */ |
47 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | 63 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) |
48 | { | 64 | { |
@@ -166,6 +182,103 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | |||
166 | return need_dwarf; | 182 | return need_dwarf; |
167 | } | 183 | } |
168 | 184 | ||
185 | /* Parse kprobe_events event into struct probe_point */ | ||
186 | void parse_trace_kprobe_event(const char *str, char **group, char **event, | ||
187 | struct probe_point *pp) | ||
188 | { | ||
189 | char pr; | ||
190 | char *p; | ||
191 | int ret, i, argc; | ||
192 | char **argv; | ||
193 | |||
194 | pr_debug("Parsing kprobe_events: %s\n", str); | ||
195 | argv = argv_split(str, &argc); | ||
196 | if (!argv) | ||
197 | die("argv_split failed."); | ||
198 | if (argc < 2) | ||
199 | semantic_error("Too less arguments."); | ||
200 | |||
201 | /* Scan event and group name. */ | ||
202 | ret = sscanf(argv[0], "%c:%m[^/ \t]/%m[^ \t]", | ||
203 | &pr, group, event); | ||
204 | if (ret != 3) | ||
205 | semantic_error("Failed to parse event name: %s", argv[0]); | ||
206 | pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); | ||
207 | |||
208 | if (!pp) | ||
209 | goto end; | ||
210 | |||
211 | pp->retprobe = (pr == 'r'); | ||
212 | |||
213 | /* Scan function name and offset */ | ||
214 | ret = sscanf(argv[1], "%m[^+]+%d", &pp->function, &pp->offset); | ||
215 | if (ret == 1) | ||
216 | pp->offset = 0; | ||
217 | |||
218 | /* kprobe_events doesn't have this information */ | ||
219 | pp->line = 0; | ||
220 | pp->file = NULL; | ||
221 | |||
222 | pp->nr_args = argc - 2; | ||
223 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
224 | for (i = 0; i < pp->nr_args; i++) { | ||
225 | p = strchr(argv[i + 2], '='); | ||
226 | if (p) /* We don't need which register is assigned. */ | ||
227 | *p = '\0'; | ||
228 | pp->args[i] = strdup(argv[i + 2]); | ||
229 | if (!pp->args[i]) | ||
230 | die("Failed to copy argument."); | ||
231 | } | ||
232 | |||
233 | end: | ||
234 | argv_free(argv); | ||
235 | } | ||
236 | |||
237 | int synthesize_perf_probe_event(struct probe_point *pp) | ||
238 | { | ||
239 | char *buf; | ||
240 | char offs[64] = "", line[64] = ""; | ||
241 | int i, len, ret; | ||
242 | |||
243 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
244 | if (!buf) | ||
245 | die("Failed to allocate memory by zalloc."); | ||
246 | if (pp->offset) { | ||
247 | ret = e_snprintf(offs, 64, "+%d", pp->offset); | ||
248 | if (ret <= 0) | ||
249 | goto error; | ||
250 | } | ||
251 | if (pp->line) { | ||
252 | ret = e_snprintf(line, 64, ":%d", pp->line); | ||
253 | if (ret <= 0) | ||
254 | goto error; | ||
255 | } | ||
256 | |||
257 | if (pp->function) | ||
258 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | ||
259 | offs, pp->retprobe ? "%return" : "", line); | ||
260 | else | ||
261 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line); | ||
262 | if (ret <= 0) | ||
263 | goto error; | ||
264 | len = ret; | ||
265 | |||
266 | for (i = 0; i < pp->nr_args; i++) { | ||
267 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
268 | pp->args[i]); | ||
269 | if (ret <= 0) | ||
270 | goto error; | ||
271 | len += ret; | ||
272 | } | ||
273 | pp->found = 1; | ||
274 | |||
275 | return pp->found; | ||
276 | error: | ||
277 | free(pp->probes[0]); | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
169 | int synthesize_trace_kprobe_event(struct probe_point *pp) | 282 | int synthesize_trace_kprobe_event(struct probe_point *pp) |
170 | { | 283 | { |
171 | char *buf; | 284 | char *buf; |
@@ -174,15 +287,15 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) | |||
174 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 287 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); |
175 | if (!buf) | 288 | if (!buf) |
176 | die("Failed to allocate memory by zalloc."); | 289 | die("Failed to allocate memory by zalloc."); |
177 | ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | 290 | ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); |
178 | if (ret <= 0 || ret >= MAX_CMDLEN) | 291 | if (ret <= 0) |
179 | goto error; | 292 | goto error; |
180 | len = ret; | 293 | len = ret; |
181 | 294 | ||
182 | for (i = 0; i < pp->nr_args; i++) { | 295 | for (i = 0; i < pp->nr_args; i++) { |
183 | ret = snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 296 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
184 | pp->args[i]); | 297 | pp->args[i]); |
185 | if (ret <= 0 || ret >= MAX_CMDLEN - len) | 298 | if (ret <= 0) |
186 | goto error; | 299 | goto error; |
187 | len += ret; | 300 | len += ret; |
188 | } | 301 | } |
@@ -191,12 +304,105 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) | |||
191 | return pp->found; | 304 | return pp->found; |
192 | error: | 305 | error: |
193 | free(pp->probes[0]); | 306 | free(pp->probes[0]); |
194 | if (ret > 0) | ||
195 | ret = -E2BIG; | ||
196 | 307 | ||
197 | return ret; | 308 | return ret; |
198 | } | 309 | } |
199 | 310 | ||
311 | static int open_kprobe_events(int flags, int mode) | ||
312 | { | ||
313 | char buf[PATH_MAX]; | ||
314 | int ret; | ||
315 | |||
316 | ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); | ||
317 | if (ret < 0) | ||
318 | die("Failed to make kprobe_events path."); | ||
319 | |||
320 | ret = open(buf, flags, mode); | ||
321 | if (ret < 0) { | ||
322 | if (errno == ENOENT) | ||
323 | die("kprobe_events file does not exist -" | ||
324 | " please rebuild with CONFIG_KPROBE_TRACER."); | ||
325 | else | ||
326 | die("Could not open kprobe_events file: %s", | ||
327 | strerror(errno)); | ||
328 | } | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /* Get raw string list of current kprobe_events */ | ||
333 | static struct strlist *get_trace_kprobe_event_rawlist(int fd) | ||
334 | { | ||
335 | int ret, idx; | ||
336 | FILE *fp; | ||
337 | char buf[MAX_CMDLEN]; | ||
338 | char *p; | ||
339 | struct strlist *sl; | ||
340 | |||
341 | sl = strlist__new(true, NULL); | ||
342 | |||
343 | fp = fdopen(dup(fd), "r"); | ||
344 | while (!feof(fp)) { | ||
345 | p = fgets(buf, MAX_CMDLEN, fp); | ||
346 | if (!p) | ||
347 | break; | ||
348 | |||
349 | idx = strlen(p) - 1; | ||
350 | if (p[idx] == '\n') | ||
351 | p[idx] = '\0'; | ||
352 | ret = strlist__add(sl, buf); | ||
353 | if (ret < 0) | ||
354 | die("strlist__add failed: %s", strerror(-ret)); | ||
355 | } | ||
356 | fclose(fp); | ||
357 | |||
358 | return sl; | ||
359 | } | ||
360 | |||
361 | /* Free and zero clear probe_point */ | ||
362 | static void clear_probe_point(struct probe_point *pp) | ||
363 | { | ||
364 | int i; | ||
365 | |||
366 | if (pp->function) | ||
367 | free(pp->function); | ||
368 | if (pp->file) | ||
369 | free(pp->file); | ||
370 | for (i = 0; i < pp->nr_args; i++) | ||
371 | free(pp->args[i]); | ||
372 | if (pp->args) | ||
373 | free(pp->args); | ||
374 | for (i = 0; i < pp->found; i++) | ||
375 | free(pp->probes[i]); | ||
376 | memset(pp, 0, sizeof(pp)); | ||
377 | } | ||
378 | |||
379 | /* List up current perf-probe events */ | ||
380 | void show_perf_probe_events(void) | ||
381 | { | ||
382 | unsigned int i; | ||
383 | int fd; | ||
384 | char *group, *event; | ||
385 | struct probe_point pp; | ||
386 | struct strlist *rawlist; | ||
387 | struct str_node *ent; | ||
388 | |||
389 | fd = open_kprobe_events(O_RDONLY, 0); | ||
390 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
391 | close(fd); | ||
392 | |||
393 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | ||
394 | ent = strlist__entry(rawlist, i); | ||
395 | parse_trace_kprobe_event(ent->s, &group, &event, &pp); | ||
396 | synthesize_perf_probe_event(&pp); | ||
397 | printf("[%s:%s]\t%s\n", group, event, pp.probes[0]); | ||
398 | free(group); | ||
399 | free(event); | ||
400 | clear_probe_point(&pp); | ||
401 | } | ||
402 | |||
403 | strlist__delete(rawlist); | ||
404 | } | ||
405 | |||
200 | static int write_trace_kprobe_event(int fd, const char *buf) | 406 | static int write_trace_kprobe_event(int fd, const char *buf) |
201 | { | 407 | { |
202 | int ret; | 408 | int ret; |
@@ -216,16 +422,7 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | |||
216 | struct probe_point *pp; | 422 | struct probe_point *pp; |
217 | char buf[MAX_CMDLEN]; | 423 | char buf[MAX_CMDLEN]; |
218 | 424 | ||
219 | snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path); | 425 | fd = open_kprobe_events(O_WRONLY, O_APPEND); |
220 | fd = open(buf, O_WRONLY, O_APPEND); | ||
221 | if (fd < 0) { | ||
222 | if (errno == ENOENT) | ||
223 | die("kprobe_events file does not exist -" | ||
224 | " please rebuild with CONFIG_KPROBE_TRACER."); | ||
225 | else | ||
226 | die("Could not open kprobe_events file: %s", | ||
227 | strerror(errno)); | ||
228 | } | ||
229 | 426 | ||
230 | for (j = 0; j < nr_probes; j++) { | 427 | for (j = 0; j < nr_probes; j++) { |
231 | pp = probes + j; | 428 | pp = probes + j; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 0089c455ecac..88db7d1a9472 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -2,9 +2,14 @@ | |||
2 | #define _PROBE_EVENT_H | 2 | #define _PROBE_EVENT_H |
3 | 3 | ||
4 | #include "probe-finder.h" | 4 | #include "probe-finder.h" |
5 | #include "strlist.h" | ||
5 | 6 | ||
6 | extern int parse_perf_probe_event(const char *str, struct probe_point *pp); | 7 | extern int parse_perf_probe_event(const char *str, struct probe_point *pp); |
8 | extern int synthesize_perf_probe_event(struct probe_point *pp); | ||
9 | extern void parse_trace_kprobe_event(const char *str, char **group, | ||
10 | char **event, struct probe_point *pp); | ||
7 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | 11 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); |
8 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); | 12 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); |
13 | extern void show_perf_probe_events(void); | ||
9 | 14 | ||
10 | #endif /*_PROBE_EVENT_H */ | 15 | #endif /*_PROBE_EVENT_H */ |