perf trace/scripting: List available scripts
authorTom Zanussi <tzanussi@gmail.com>
Tue, 15 Dec 2009 08:53:38 +0000 (02:53 -0600)
committerIngo Molnar <mingo@elte.hu>
Tue, 15 Dec 2009 09:31:32 +0000 (10:31 +0100)
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>
tools/perf/builtin-trace.c
tools/perf/scripts/perl/bin/check-perf-trace-report
tools/perf/scripts/perl/bin/rw-by-file-report
tools/perf/scripts/perl/bin/rw-by-pid-report
tools/perf/scripts/perl/bin/wakeup-latency-report
tools/perf/scripts/perl/bin/workqueue-stats-report

index 88b0353..7674153 100644 (file)
@@ -274,6 +274,201 @@ static int parse_scriptname(const struct option *opt __used,
        return 0;
 }
 
+#define for_each_lang(scripts_dir, lang_dirent, lang_next)             \
+       while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) &&     \
+              lang_next)                                               \
+               if (lang_dirent.d_type == DT_DIR &&                     \
+                   (strcmp(lang_dirent.d_name, ".")) &&                \
+                   (strcmp(lang_dirent.d_name, "..")))
+
+#define for_each_script(lang_dir, script_dirent, script_next)          \
+       while (!readdir_r(lang_dir, &script_dirent, &script_next) &&    \
+              script_next)                                             \
+               if (script_dirent.d_type != DT_DIR)
+
+
+#define RECORD_SUFFIX                  "-record"
+#define REPORT_SUFFIX                  "-report"
+
+struct script_desc {
+       struct list_head        node;
+       char                    *name;
+       char                    *half_liner;
+       char                    *args;
+};
+
+LIST_HEAD(script_descs);
+
+static struct script_desc *script_desc__new(const char *name)
+{
+       struct script_desc *s = zalloc(sizeof(*s));
+
+       if (s != NULL)
+               s->name = strdup(name);
+
+       return s;
+}
+
+static void script_desc__delete(struct script_desc *s)
+{
+       free(s->name);
+       free(s);
+}
+
+static void script_desc__add(struct script_desc *s)
+{
+       list_add_tail(&s->node, &script_descs);
+}
+
+static struct script_desc *script_desc__find(const char *name)
+{
+       struct script_desc *s;
+
+       list_for_each_entry(s, &script_descs, node)
+               if (strcasecmp(s->name, name) == 0)
+                       return s;
+       return NULL;
+}
+
+static struct script_desc *script_desc__findnew(const char *name)
+{
+       struct script_desc *s = script_desc__find(name);
+
+       if (s)
+               return s;
+
+       s = script_desc__new(name);
+       if (!s)
+               goto out_delete_desc;
+
+       script_desc__add(s);
+
+       return s;
+
+out_delete_desc:
+       script_desc__delete(s);
+
+       return NULL;
+}
+
+static char *ends_with(char *str, const char *suffix)
+{
+       size_t suffix_len = strlen(suffix);
+       char *p = str;
+
+       if (strlen(str) > suffix_len) {
+               p = str + strlen(str) - suffix_len;
+               if (!strncmp(p, suffix, suffix_len))
+                       return p;
+       }
+
+       return NULL;
+}
+
+static char *ltrim(char *str)
+{
+       int len = strlen(str);
+
+       while (len && isspace(*str)) {
+               len--;
+               str++;
+       }
+
+       return str;
+}
+
+static int read_script_info(struct script_desc *desc, const char *filename)
+{
+       char line[BUFSIZ], *p;
+       FILE *fp;
+
+       fp = fopen(filename, "r");
+       if (!fp)
+               return -1;
+
+       while (fgets(line, sizeof(line), fp)) {
+               p = ltrim(line);
+               if (strlen(p) == 0)
+                       continue;
+               if (*p != '#')
+                       continue;
+               p++;
+               if (strlen(p) && *p == '!')
+                       continue;
+
+               p = ltrim(p);
+               if (strlen(p) && p[strlen(p) - 1] == '\n')
+                       p[strlen(p) - 1] = '\0';
+
+               if (!strncmp(p, "description:", strlen("description:"))) {
+                       p += strlen("description:");
+                       desc->half_liner = strdup(ltrim(p));
+                       continue;
+               }
+
+               if (!strncmp(p, "args:", strlen("args:"))) {
+                       p += strlen("args:");
+                       desc->args = strdup(ltrim(p));
+                       continue;
+               }
+       }
+
+       fclose(fp);
+
+       return 0;
+}
+
+static int list_available_scripts(const struct option *opt __used,
+                                 const char *s __used, int unset __used)
+{
+       struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+       char scripts_path[MAXPATHLEN];
+       DIR *scripts_dir, *lang_dir;
+       char script_path[MAXPATHLEN];
+       char lang_path[MAXPATHLEN];
+       struct script_desc *desc;
+       char first_half[BUFSIZ];
+       char *script_root;
+       char *str;
+
+       snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+       scripts_dir = opendir(scripts_path);
+       if (!scripts_dir)
+               return -1;
+
+       for_each_lang(scripts_dir, lang_dirent, lang_next) {
+               snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
+                        lang_dirent.d_name);
+               lang_dir = opendir(lang_path);
+               if (!lang_dir)
+                       continue;
+
+               for_each_script(lang_dir, script_dirent, script_next) {
+                       script_root = strdup(script_dirent.d_name);
+                       str = ends_with(script_root, REPORT_SUFFIX);
+                       if (str) {
+                               *str = '\0';
+                               desc = script_desc__findnew(script_root);
+                               snprintf(script_path, MAXPATHLEN, "%s/%s",
+                                        lang_path, script_dirent.d_name);
+                               read_script_info(desc, script_path);
+                       }
+                       free(script_root);
+               }
+       }
+
+       fprintf(stdout, "List of available trace scripts:\n");
+       list_for_each_entry(desc, &script_descs, node) {
+               sprintf(first_half, "%s %s", desc->name,
+                       desc->args ? desc->args : "");
+               fprintf(stdout, "  %-36s %s\n", first_half,
+                       desc->half_liner ? desc->half_liner : "");
+       }
+
+       exit(0);
+}
+
 static const char * const annotate_usage[] = {
        "perf trace [<options>] <command>",
        NULL
@@ -284,8 +479,10 @@ static const struct option options[] = {
                    "dump raw trace in ASCII"),
        OPT_BOOLEAN('v', "verbose", &verbose,
                    "be more verbose (show symbol address, etc)"),
-       OPT_BOOLEAN('l', "latency", &latency_format,
+       OPT_BOOLEAN('L', "Latency", &latency_format,
                    "show latency attributes (irqs/preemption disabled, etc)"),
+       OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
+                          list_available_scripts),
        OPT_CALLBACK('s', "script", NULL, "name",
                     "script file name (lang:script name, script name, or *)",
                     parse_scriptname),
index 89948b0..7fc4a03 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/bash
+# description: useless but exhaustive test script
 perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl
 
 
index 1c05675..eddb9cc 100644 (file)
@@ -1,4 +1,6 @@
 #!/bin/bash
+# description: r/w activity for a program, by file
+# args: <comm>
 perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1
 
 
index cea16f7..7f44c25 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/bash
+# description: system-wide r/w activity
 perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl
 
 
index 85769dc..fce3adc 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/bash
+# description: system-wide min/max/avg wakeup latency
 perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl
 
 
index aa68435..71cfbd1 100644 (file)
@@ -1,4 +1,5 @@
 #!/bin/bash
+# description: workqueue stats (ins/exe/create/destroy)
 perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl