diff options
author | Tom Zanussi <tzanussi@gmail.com> | 2009-12-15 03:53:38 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-12-15 04:31:32 -0500 |
commit | 4b9c0c596ea826ef784eb83f663c5351ed01ba6d (patch) | |
tree | 8e22badb64e744d61d2ed4f6d12a0ebeb05a61c3 /tools/perf/builtin-trace.c | |
parent | 8f11d85a0e7e9025acea7493e6864089c8b52f42 (diff) |
perf trace/scripting: List available scripts
Lists the available perf trace scripts, one per line e.g.:
root@tropicana:~# perf trace -l
List of available trace scripts:
workqueue-stats workqueue stats (ins/exe/create/destroy)
wakeup-latency system-wide min/max/avg wakeup latency
rw-by-file <comm> r/w activity for a program, by file
check-perf-trace useless but exhaustive test script
rw-by-pid system-wide r/w activity
To be consistent with the other listing options in perf, the
current latency trace option was changed to '-L', and '-l' is
now used to access the script listing as:
To create the list, it searches each scripts/*/bin directory for
files ending with "-report" and reads information found in
certain comment lines contained in those shell scripts:
- if the comment line starts with "description:", the rest of the
line is used as a 'half-line' description. To keep each line in
the list to a single line, the description should be limited to 40
characters (the rest of the line contains the script name and
args)
- if the comment line starts with "args:", the rest of the line
names the args the script supports. Required args should be
surrounded by <> brackets, optional args by [] brackets.
The current scripts in scripts/perl/bin have also been updated
with description: and args: comments.
Signed-off-by: Tom Zanussi <tzanussi@gmail.com>
Cc: fweisbec@gmail.com
Cc: rostedt@goodmis.org
LKML-Reference: <1260867220-15699-5-git-send-email-tzanussi@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 199 |
1 files changed, 198 insertions, 1 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 88b0353d4019..7674153c4bbe 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -274,6 +274,201 @@ static int parse_scriptname(const struct option *opt __used, | |||
274 | return 0; | 274 | return 0; |
275 | } | 275 | } |
276 | 276 | ||
277 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ | ||
278 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ | ||
279 | lang_next) \ | ||
280 | if (lang_dirent.d_type == DT_DIR && \ | ||
281 | (strcmp(lang_dirent.d_name, ".")) && \ | ||
282 | (strcmp(lang_dirent.d_name, ".."))) | ||
283 | |||
284 | #define for_each_script(lang_dir, script_dirent, script_next) \ | ||
285 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ | ||
286 | script_next) \ | ||
287 | if (script_dirent.d_type != DT_DIR) | ||
288 | |||
289 | |||
290 | #define RECORD_SUFFIX "-record" | ||
291 | #define REPORT_SUFFIX "-report" | ||
292 | |||
293 | struct script_desc { | ||
294 | struct list_head node; | ||
295 | char *name; | ||
296 | char *half_liner; | ||
297 | char *args; | ||
298 | }; | ||
299 | |||
300 | LIST_HEAD(script_descs); | ||
301 | |||
302 | static struct script_desc *script_desc__new(const char *name) | ||
303 | { | ||
304 | struct script_desc *s = zalloc(sizeof(*s)); | ||
305 | |||
306 | if (s != NULL) | ||
307 | s->name = strdup(name); | ||
308 | |||
309 | return s; | ||
310 | } | ||
311 | |||
312 | static void script_desc__delete(struct script_desc *s) | ||
313 | { | ||
314 | free(s->name); | ||
315 | free(s); | ||
316 | } | ||
317 | |||
318 | static void script_desc__add(struct script_desc *s) | ||
319 | { | ||
320 | list_add_tail(&s->node, &script_descs); | ||
321 | } | ||
322 | |||
323 | static struct script_desc *script_desc__find(const char *name) | ||
324 | { | ||
325 | struct script_desc *s; | ||
326 | |||
327 | list_for_each_entry(s, &script_descs, node) | ||
328 | if (strcasecmp(s->name, name) == 0) | ||
329 | return s; | ||
330 | return NULL; | ||
331 | } | ||
332 | |||
333 | static struct script_desc *script_desc__findnew(const char *name) | ||
334 | { | ||
335 | struct script_desc *s = script_desc__find(name); | ||
336 | |||
337 | if (s) | ||
338 | return s; | ||
339 | |||
340 | s = script_desc__new(name); | ||
341 | if (!s) | ||
342 | goto out_delete_desc; | ||
343 | |||
344 | script_desc__add(s); | ||
345 | |||
346 | return s; | ||
347 | |||
348 | out_delete_desc: | ||
349 | script_desc__delete(s); | ||
350 | |||
351 | return NULL; | ||
352 | } | ||
353 | |||
354 | static char *ends_with(char *str, const char *suffix) | ||
355 | { | ||
356 | size_t suffix_len = strlen(suffix); | ||
357 | char *p = str; | ||
358 | |||
359 | if (strlen(str) > suffix_len) { | ||
360 | p = str + strlen(str) - suffix_len; | ||
361 | if (!strncmp(p, suffix, suffix_len)) | ||
362 | return p; | ||
363 | } | ||
364 | |||
365 | return NULL; | ||
366 | } | ||
367 | |||
368 | static char *ltrim(char *str) | ||
369 | { | ||
370 | int len = strlen(str); | ||
371 | |||
372 | while (len && isspace(*str)) { | ||
373 | len--; | ||
374 | str++; | ||
375 | } | ||
376 | |||
377 | return str; | ||
378 | } | ||
379 | |||
380 | static int read_script_info(struct script_desc *desc, const char *filename) | ||
381 | { | ||
382 | char line[BUFSIZ], *p; | ||
383 | FILE *fp; | ||
384 | |||
385 | fp = fopen(filename, "r"); | ||
386 | if (!fp) | ||
387 | return -1; | ||
388 | |||
389 | while (fgets(line, sizeof(line), fp)) { | ||
390 | p = ltrim(line); | ||
391 | if (strlen(p) == 0) | ||
392 | continue; | ||
393 | if (*p != '#') | ||
394 | continue; | ||
395 | p++; | ||
396 | if (strlen(p) && *p == '!') | ||
397 | continue; | ||
398 | |||
399 | p = ltrim(p); | ||
400 | if (strlen(p) && p[strlen(p) - 1] == '\n') | ||
401 | p[strlen(p) - 1] = '\0'; | ||
402 | |||
403 | if (!strncmp(p, "description:", strlen("description:"))) { | ||
404 | p += strlen("description:"); | ||
405 | desc->half_liner = strdup(ltrim(p)); | ||
406 | continue; | ||
407 | } | ||
408 | |||
409 | if (!strncmp(p, "args:", strlen("args:"))) { | ||
410 | p += strlen("args:"); | ||
411 | desc->args = strdup(ltrim(p)); | ||
412 | continue; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | fclose(fp); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int list_available_scripts(const struct option *opt __used, | ||
422 | const char *s __used, int unset __used) | ||
423 | { | ||
424 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
425 | char scripts_path[MAXPATHLEN]; | ||
426 | DIR *scripts_dir, *lang_dir; | ||
427 | char script_path[MAXPATHLEN]; | ||
428 | char lang_path[MAXPATHLEN]; | ||
429 | struct script_desc *desc; | ||
430 | char first_half[BUFSIZ]; | ||
431 | char *script_root; | ||
432 | char *str; | ||
433 | |||
434 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
435 | |||
436 | scripts_dir = opendir(scripts_path); | ||
437 | if (!scripts_dir) | ||
438 | return -1; | ||
439 | |||
440 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
441 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
442 | lang_dirent.d_name); | ||
443 | lang_dir = opendir(lang_path); | ||
444 | if (!lang_dir) | ||
445 | continue; | ||
446 | |||
447 | for_each_script(lang_dir, script_dirent, script_next) { | ||
448 | script_root = strdup(script_dirent.d_name); | ||
449 | str = ends_with(script_root, REPORT_SUFFIX); | ||
450 | if (str) { | ||
451 | *str = '\0'; | ||
452 | desc = script_desc__findnew(script_root); | ||
453 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
454 | lang_path, script_dirent.d_name); | ||
455 | read_script_info(desc, script_path); | ||
456 | } | ||
457 | free(script_root); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | fprintf(stdout, "List of available trace scripts:\n"); | ||
462 | list_for_each_entry(desc, &script_descs, node) { | ||
463 | sprintf(first_half, "%s %s", desc->name, | ||
464 | desc->args ? desc->args : ""); | ||
465 | fprintf(stdout, " %-36s %s\n", first_half, | ||
466 | desc->half_liner ? desc->half_liner : ""); | ||
467 | } | ||
468 | |||
469 | exit(0); | ||
470 | } | ||
471 | |||
277 | static const char * const annotate_usage[] = { | 472 | static const char * const annotate_usage[] = { |
278 | "perf trace [<options>] <command>", | 473 | "perf trace [<options>] <command>", |
279 | NULL | 474 | NULL |
@@ -284,8 +479,10 @@ static const struct option options[] = { | |||
284 | "dump raw trace in ASCII"), | 479 | "dump raw trace in ASCII"), |
285 | OPT_BOOLEAN('v', "verbose", &verbose, | 480 | OPT_BOOLEAN('v', "verbose", &verbose, |
286 | "be more verbose (show symbol address, etc)"), | 481 | "be more verbose (show symbol address, etc)"), |
287 | OPT_BOOLEAN('l', "latency", &latency_format, | 482 | OPT_BOOLEAN('L', "Latency", &latency_format, |
288 | "show latency attributes (irqs/preemption disabled, etc)"), | 483 | "show latency attributes (irqs/preemption disabled, etc)"), |
484 | OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", | ||
485 | list_available_scripts), | ||
289 | OPT_CALLBACK('s', "script", NULL, "name", | 486 | OPT_CALLBACK('s', "script", NULL, "name", |
290 | "script file name (lang:script name, script name, or *)", | 487 | "script file name (lang:script name, script name, or *)", |
291 | parse_scriptname), | 488 | parse_scriptname), |