2009-12-15 08:53:47

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 0/6] perf trace/scripting updates

This patchset fixes a few oversights and adds some options to aid
usability.

With these patches, the list of available trace scripts can be easily
displayed, and the script names shown in the list can be used to
record and invoke the individual scripts without forcing the user to
remember and enter long command-lines.

For example, getting r/w activity for a program e.g. firefox is as
easy as running 'perf trace -l', noting that the rw-by-file script
exists, and using that name to record data and display the results
using 'perf trace record rw-by-file' and 'perf trace report rw-by-file
firefox':

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

root@tropicana:~# perf trace record rw-by-file
^C[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 65.388 MB perf.data (~2856833 samples) ]

root@tropicana:~# perf trace report rw-by-file firefox
perf trace started with Perl script /root/libexec/perf-core/scripts/perl/rw-by-file.pl

file read counts for firefox:

fd # reads bytes_requested
------ ---------- -----------
3 3796 15548416
45 12 33075
32 28 28672
16 20 20480
58 6 17279
22 4 16384
46 2 11289
57 2 8192
26 2 8192
60 2 8192
47 5 4160
12 78 3874
44 2 606
19 179 179
42 8 128
53 1 16

file write counts for firefox:

fd # writes bytes_written
------ ---------- -----------
52 51 55528
26 13 53248
55 22 45100
45 8 23632
42 19 19456
59 28 10304
58 24 8760
61 1 7988
57 13 4640
20 179 179
33 28 28
17 26 26

Note that the perf trace -l option was previously used to display
latency information; this patchset changes that option to -L in the
interest of maintaining consistency with other perf -l options.

The other patches in this patchset fix some oversights, including
adding support for script args, which entails a change to
scripting_ops.


Tom Zanussi (6):
perf trace/scripting: add support for script args
perf trace/scripting: don't install unneeded files
perf trace/scripting: check return val of perl_run()
perf trace/scripting: list available scripts
perf trace/scripting: add 'record' and 'report' options
perf trace/scripting: update Documentation

tools/perf/Documentation/perf-trace.txt | 27 ++-
tools/perf/Makefile | 2 -
tools/perf/builtin-trace.c | 302 +++++++++++++++++++-
.../perf/scripts/perl/bin/check-perf-trace-report | 1 +
tools/perf/scripts/perl/bin/rw-by-file-report | 4 +-
tools/perf/scripts/perl/bin/rw-by-pid-report | 1 +
tools/perf/scripts/perl/bin/wakeup-latency-report | 1 +
tools/perf/scripts/perl/bin/workqueue-stats-report | 1 +
tools/perf/scripts/perl/rw-by-file.pl | 5 +-
tools/perf/util/trace-event-perl.c | 42 ++-
tools/perf/util/trace-event.h | 2 +-
11 files changed, 356 insertions(+), 32 deletions(-)


2009-12-15 08:55:15

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 1/6] perf trace/scripting: add support for script args

One oversight of the original scripting_ops patch was a lack of
support for passing args to handler scripts. This adds argc/argv to
the start_script() scripting_op, and changes the rw-by-file script to
take 'comm' arg rather than the 'perf' value currently hard-coded. It
also takes the opportunity to do some related minor cleanup.

Signed-off-by: Tom Zanussi <[email protected]>
---
tools/perf/builtin-trace.c | 19 +++++--------
tools/perf/scripts/perl/bin/rw-by-file-report | 2 +-
tools/perf/scripts/perl/rw-by-file.pl | 5 ++-
tools/perf/util/trace-event-perl.c | 36 ++++++++++++++++++-------
tools/perf/util/trace-event.h | 2 +-
5 files changed, 38 insertions(+), 26 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 9ee976d..e60e35e 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -12,7 +12,9 @@
static char const *script_name;
static char const *generate_script_lang;

-static int default_start_script(const char *script __attribute((unused)))
+static int default_start_script(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
{
return 0;
}
@@ -22,7 +24,7 @@ static int default_stop_script(void)
return 0;
}

-static int default_generate_script(const char *outfile __attribute ((unused)))
+static int default_generate_script(const char *outfile __unused)
{
return 0;
}
@@ -302,15 +304,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)

setup_scripting();

- argc = parse_options(argc, argv, options, annotate_usage, 0);
- if (argc) {
- /*
- * Special case: if there's an argument left then assume tha
- * it's a symbol filter:
- */
- if (argc > 1)
- usage_with_options(annotate_usage, options);
- }
+ argc = parse_options(argc, argv, options, annotate_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);

setup_pager();

@@ -350,7 +345,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
}

if (script_name) {
- err = scripting_ops->start_script(script_name);
+ err = scripting_ops->start_script(script_name, argc, argv);
if (err)
goto out;
}
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
index f5dcf9c..1c05675 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -1,5 +1,5 @@
#!/bin/bash
-perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl
+perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1



diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl
index 61f9156..2a39097 100644
--- a/tools/perf/scripts/perl/rw-by-file.pl
+++ b/tools/perf/scripts/perl/rw-by-file.pl
@@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib";
use Perf::Trace::Core;
use Perf::Trace::Util;

-# change this to the comm of the program you're interested in
-my $for_comm = "perf";
+my $usage = "perf trace -s rw-by-file.pl <comm>\n";
+
+my $for_comm = shift or die $usage;

my %reads;
my %writes;
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c
index a5ffe60..6f10e76 100644
--- a/tools/perf/util/trace-event-perl.c
+++ b/tools/perf/util/trace-event-perl.c
@@ -267,7 +267,7 @@ int common_lock_depth(struct scripting_context *context)
}

static void perl_process_event(int cpu, void *data,
- int size __attribute((unused)),
+ int size __unused,
unsigned long long nsecs, char *comm)
{
struct format_field *field;
@@ -359,28 +359,42 @@ static void run_start_sub(void)
/*
* Start trace script
*/
-static int perl_start_script(const char *script)
+static int perl_start_script(const char *script, int argc, const char **argv)
{
- const char *command_line[2] = { "", NULL };
+ const char **command_line;
+ int i, err = 0;

+ command_line = malloc((argc + 2) * sizeof(const char *));
+ command_line[0] = "";
command_line[1] = script;
+ for (i = 2; i < argc + 2; i++)
+ command_line[i] = argv[i - 2];

my_perl = perl_alloc();
perl_construct(my_perl);

- if (perl_parse(my_perl, xs_init, 2, (char **)command_line,
- (char **)NULL))
- return -1;
+ if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
+ (char **)NULL)) {
+ err = -1;
+ goto error;
+ }

perl_run(my_perl);
- if (SvTRUE(ERRSV))
- return -1;
+ if (SvTRUE(ERRSV)) {
+ err = -1;
+ goto error;
+ }

run_start_sub();

+ free(command_line);
fprintf(stderr, "perf trace started with Perl script %s\n\n", script);
-
return 0;
+error:
+ perl_free(my_perl);
+ free(command_line);
+
+ return err;
}

/*
@@ -579,7 +593,9 @@ static void print_unsupported_msg(void)
"\n etc.\n");
}

-static int perl_start_script_unsupported(const char *script __unused)
+static int perl_start_script_unsupported(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
{
print_unsupported_msg();

diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 81698d5..6ad4056 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -270,7 +270,7 @@ enum trace_flag_type {

struct scripting_ops {
const char *name;
- int (*start_script) (const char *);
+ int (*start_script) (const char *script, int argc, const char **argv);
int (*stop_script) (void);
void (*process_event) (int cpu, void *data, int size,
unsigned long long nsecs, char *comm);
--
1.6.4.GIT

2009-12-15 08:53:52

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 2/6] perf trace/scripting: don't install unneeded files

README and Makefile.PL don't need to be installed for Perl run-time
support.

Signed-off-by: Tom Zanussi <[email protected]>
---
tools/perf/Makefile | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index a4cb792..2953809 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -995,8 +995,6 @@ install: all
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
$(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
- $(INSTALL) scripts/perl/Perf-Trace-Util/Makefile.PL -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
- $(INSTALL) scripts/perl/Perf-Trace-Util/README -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
ifdef BUILT_INS
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
--
1.6.4.GIT

2009-12-15 08:54:41

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 3/6] perf trace/scripting: check return val of perl_run()

The return value from perl_run() is currently ignored, but it should
be checked and used to exit perf if there are problems loading the
script.

Signed-off-by: Tom Zanussi <[email protected]>
---
tools/perf/util/trace-event-perl.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c
index 6f10e76..6d6d76b 100644
--- a/tools/perf/util/trace-event-perl.c
+++ b/tools/perf/util/trace-event-perl.c
@@ -379,7 +379,11 @@ static int perl_start_script(const char *script, int argc, const char **argv)
goto error;
}

- perl_run(my_perl);
+ if (perl_run(my_perl)) {
+ err = -1;
+ goto error;
+ }
+
if (SvTRUE(ERRSV)) {
err = -1;
goto error;
--
1.6.4.GIT

2009-12-15 08:54:59

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 4/6] 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 <[email protected]>
---
tools/perf/builtin-trace.c | 199 +++++++++++++++++++-
.../perf/scripts/perl/bin/check-perf-trace-report | 1 +
tools/perf/scripts/perl/bin/rw-by-file-report | 2 +
tools/perf/scripts/perl/bin/rw-by-pid-report | 1 +
tools/perf/scripts/perl/bin/wakeup-latency-report | 1 +
tools/perf/scripts/perl/bin/workqueue-stats-report | 1 +
6 files changed, 204 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index e60e35e..f5fb3bf 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,
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),
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-report b/tools/perf/scripts/perl/bin/check-perf-trace-report
index 89948b0..7fc4a03 100644
--- a/tools/perf/scripts/perl/bin/check-perf-trace-report
+++ b/tools/perf/scripts/perl/bin/check-perf-trace-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: useless but exhaustive test script
perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl


diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
index 1c05675..eddb9cc 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -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


diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report
index cea16f7..7f44c25 100644
--- a/tools/perf/scripts/perl/bin/rw-by-pid-report
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: system-wide r/w activity
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl


diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report
index 85769dc..fce3adc 100644
--- a/tools/perf/scripts/perl/bin/wakeup-latency-report
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-report
@@ -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


diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report
index aa68435..71cfbd1 100644
--- a/tools/perf/scripts/perl/bin/workqueue-stats-report
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: workqueue stats (ins/exe/create/destroy)
perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl


--
1.6.4.GIT

2009-12-15 08:54:11

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 5/6] perf trace/scripting: add 'record' and 'report' options

Allow scripts to be recorded/executed by simply specifying the script
root name (the script name minus extension) along with 'record' or
'report' to 'perf trace'.

The script names shown by 'perf trace -l' can be directly used to run
the command-line contained within the corresponding '-record' and
'-report' versions of scripts in the scripts/*/bin directories.

For example, to record the trace data needed to run the
wakeup-latency.pl script, the user can easily find the name of the
corresponding script from the script list and invoke it using 'perf
trace record', without having to remember the details of how to do the
same thing using the lower-level perf trace command-line options:

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

root@tropicana:~# perf trace record wakeup-latency
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.296 MB perf.data (~12931 samples) ]

To run the wakeup-latency.pl script using the captured data, change
'record' to 'report' in the command-line:

root@tropicana:~# perf trace report wakeup-latency

wakeup_latency stats:

total_wakeups: 65
avg_wakeup_latency (ns): 22417
min_wakeup_latency (ns): 3470
max_wakeup_latency (ns): 223311

perf trace Perl script stopped

If the script takes options, thay can be simply added to the end of
the 'report' invocation:

root@tropicana:~# perf trace record rw-by-file
^C[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.782 MB perf.data (~34171 samples) ]

root@tropicana:~# perf trace report rw-by-file perf

file read counts for perf:

fd # reads bytes_requested
------ ---------- -----------
122 1934 1980416
120 1 32

file write counts for perf:

fd # writes bytes_written
------ ---------- -----------
3 4006 280568

perf trace Perl script stopped

Signed-off-by: Tom Zanussi <[email protected]>
---
tools/perf/builtin-trace.c | 84 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 83 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index f5fb3bf..0543409 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -469,6 +469,49 @@ static int list_available_scripts(const struct option *opt __used,
exit(0);
}

+static char *get_script_path(const char *script_root, const char *suffix)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ char script_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char lang_path[MAXPATHLEN];
+ char *str, *__script_root;
+ char *path = NULL;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return NULL;
+
+ 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, suffix);
+ if (str) {
+ *str = '\0';
+ if (strcmp(__script_root, script_root))
+ continue;
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ path = strdup(script_path);
+ free(__script_root);
+ break;
+ }
+ free(__script_root);
+ }
+ }
+
+ return path;
+}
+
static const char * const annotate_usage[] = {
"perf trace [<options>] <command>",
NULL
@@ -494,8 +537,47 @@ static const struct option options[] = {

int cmd_trace(int argc, const char **argv, const char *prefix __used)
{
- int err;
struct perf_session *session;
+ const char *suffix = NULL;
+ const char **__argv;
+ char *script_path;
+ int i, err;
+
+ if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a record script\n");
+ return -1;
+ }
+ suffix = RECORD_SUFFIX;
+ }
+
+ if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a report script\n");
+ return -1;
+ }
+ suffix = REPORT_SUFFIX;
+ }
+
+ if (suffix) {
+ script_path = get_script_path(argv[2], suffix);
+ if (!script_path) {
+ fprintf(stderr, "script not found\n");
+ return -1;
+ }
+
+ __argv = malloc((argc + 1) * sizeof(const char *));
+ __argv[0] = "/bin/sh";
+ __argv[1] = script_path;
+ for (i = 3; i < argc; i++)
+ __argv[i - 1] = argv[i];
+ __argv[argc - 1] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ exit(-1);
+ }

symbol__init(0);

--
1.6.4.GIT

2009-12-15 08:54:05

by Tom Zanussi

[permalink] [raw]
Subject: [PATCH 6/6] perf trace/scripting: update Documentation

Update the perf-trace page with new and missing options and remove
some unused ones.

Signed-off-by: Tom Zanussi <[email protected]>
---
tools/perf/Documentation/perf-trace.txt | 27 ++++++++++++++++++++++++++-
1 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 07065ef..60e5900 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -8,18 +8,43 @@ perf-trace - Read perf.data (created by perf record) and display trace output
SYNOPSIS
--------
[verse]
-'perf trace' [-i <file> | --input=file] symbol_name
+'perf trace' {record <script> | report <script> [args] }

DESCRIPTION
-----------
This command reads the input file and displays the trace recorded.

+There are several variants of perf trace:
+
+ 'perf trace' to see a detailed trace of the workload that was
+ recorded.
+
+ 'perf trace record <script>' to record the events required for 'perf
+ trace report'. <script> is the name displayed in the output of
+ 'perf trace --list' i.e. the actual script name minus any language
+ extension.
+
+ 'perf trace report <script>' to run and display the results of
+ <script>. <script> is the name displayed in the output of 'perf
+ trace --list' i.e. the actual script name minus any language
+ extension. The perf.data output from a previous run of 'perf trace
+ record <script>' is used and should be present for this command to
+ succeed.
+
OPTIONS
-------
-D::
--dump-raw-trace=::
Display verbose dump of the trace data.

+-L::
+--Latency=::
+ Show latency attributes (irqs/preemption disabled, etc).
+
+-l::
+--list=::
+ Display a list of available trace scripts.
+
-s::
--script=::
Process trace data with the given script ([lang]:script[.ext]).
--
1.6.4.GIT

2009-12-15 09:40:12

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: Add support for script args

Commit-ID: 586bc5cce88be993dad584c3936c49f945368551
Gitweb: http://git.kernel.org/tip/586bc5cce88be993dad584c3936c49f945368551
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:35 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:31 +0100

perf trace/scripting: Add support for script args

One oversight of the original scripting_ops patch was a lack of
support for passing args to handler scripts. This adds
argc/argv to the start_script() scripting_op, and changes the
rw-by-file script to take 'comm' arg rather than the 'perf'
value currently hard-coded. It also takes the opportunity to do
some related minor cleanup.

Signed-off-by: Tom Zanussi <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/builtin-trace.c | 19 +++++--------
tools/perf/scripts/perl/bin/rw-by-file-report | 2 +-
tools/perf/scripts/perl/rw-by-file.pl | 5 ++-
tools/perf/util/trace-event-perl.c | 36 ++++++++++++++++++-------
tools/perf/util/trace-event.h | 2 +-
5 files changed, 38 insertions(+), 26 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 07da665..88b0353 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -12,7 +12,9 @@
static char const *script_name;
static char const *generate_script_lang;

-static int default_start_script(const char *script __attribute((unused)))
+static int default_start_script(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
{
return 0;
}
@@ -22,7 +24,7 @@ static int default_stop_script(void)
return 0;
}

-static int default_generate_script(const char *outfile __attribute ((unused)))
+static int default_generate_script(const char *outfile __unused)
{
return 0;
}
@@ -302,15 +304,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)

setup_scripting();

- argc = parse_options(argc, argv, options, annotate_usage, 0);
- if (argc) {
- /*
- * Special case: if there's an argument left then assume tha
- * it's a symbol filter:
- */
- if (argc > 1)
- usage_with_options(annotate_usage, options);
- }
+ argc = parse_options(argc, argv, options, annotate_usage,
+ PARSE_OPT_STOP_AT_NON_OPTION);

setup_pager();

@@ -350,7 +345,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
}

if (script_name) {
- err = scripting_ops->start_script(script_name);
+ err = scripting_ops->start_script(script_name, argc, argv);
if (err)
goto out;
}
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
index f5dcf9c..1c05675 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -1,5 +1,5 @@
#!/bin/bash
-perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl
+perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1



diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl
index 61f9156..2a39097 100644
--- a/tools/perf/scripts/perl/rw-by-file.pl
+++ b/tools/perf/scripts/perl/rw-by-file.pl
@@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib";
use Perf::Trace::Core;
use Perf::Trace::Util;

-# change this to the comm of the program you're interested in
-my $for_comm = "perf";
+my $usage = "perf trace -s rw-by-file.pl <comm>\n";
+
+my $for_comm = shift or die $usage;

my %reads;
my %writes;
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c
index a5ffe60..6f10e76 100644
--- a/tools/perf/util/trace-event-perl.c
+++ b/tools/perf/util/trace-event-perl.c
@@ -267,7 +267,7 @@ int common_lock_depth(struct scripting_context *context)
}

static void perl_process_event(int cpu, void *data,
- int size __attribute((unused)),
+ int size __unused,
unsigned long long nsecs, char *comm)
{
struct format_field *field;
@@ -359,28 +359,42 @@ static void run_start_sub(void)
/*
* Start trace script
*/
-static int perl_start_script(const char *script)
+static int perl_start_script(const char *script, int argc, const char **argv)
{
- const char *command_line[2] = { "", NULL };
+ const char **command_line;
+ int i, err = 0;

+ command_line = malloc((argc + 2) * sizeof(const char *));
+ command_line[0] = "";
command_line[1] = script;
+ for (i = 2; i < argc + 2; i++)
+ command_line[i] = argv[i - 2];

my_perl = perl_alloc();
perl_construct(my_perl);

- if (perl_parse(my_perl, xs_init, 2, (char **)command_line,
- (char **)NULL))
- return -1;
+ if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line,
+ (char **)NULL)) {
+ err = -1;
+ goto error;
+ }

perl_run(my_perl);
- if (SvTRUE(ERRSV))
- return -1;
+ if (SvTRUE(ERRSV)) {
+ err = -1;
+ goto error;
+ }

run_start_sub();

+ free(command_line);
fprintf(stderr, "perf trace started with Perl script %s\n\n", script);
-
return 0;
+error:
+ perl_free(my_perl);
+ free(command_line);
+
+ return err;
}

/*
@@ -579,7 +593,9 @@ static void print_unsupported_msg(void)
"\n etc.\n");
}

-static int perl_start_script_unsupported(const char *script __unused)
+static int perl_start_script_unsupported(const char *script __unused,
+ int argc __unused,
+ const char **argv __unused)
{
print_unsupported_msg();

diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 81698d5..6ad4056 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -270,7 +270,7 @@ enum trace_flag_type {

struct scripting_ops {
const char *name;
- int (*start_script) (const char *);
+ int (*start_script) (const char *script, int argc, const char **argv);
int (*stop_script) (void);
void (*process_event) (int cpu, void *data, int size,
unsigned long long nsecs, char *comm);

2009-12-15 09:40:36

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: Don't install unneeded files

Commit-ID: a3a7cb7bb1d7bd989982314cf6f90ec392890006
Gitweb: http://git.kernel.org/tip/a3a7cb7bb1d7bd989982314cf6f90ec392890006
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:36 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:31 +0100

perf trace/scripting: Don't install unneeded files

README and Makefile.PL don't need to be installed for Perl
run-time support.

Signed-off-by: Tom Zanussi <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/Makefile | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 87a424e..7814dbb 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -996,8 +996,6 @@ install: all
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
$(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
- $(INSTALL) scripts/perl/Perf-Trace-Util/Makefile.PL -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
- $(INSTALL) scripts/perl/Perf-Trace-Util/README -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
ifdef BUILT_INS
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'

2009-12-15 09:40:34

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: Check return val of perl_run()

Commit-ID: 8f11d85a0e7e9025acea7493e6864089c8b52f42
Gitweb: http://git.kernel.org/tip/8f11d85a0e7e9025acea7493e6864089c8b52f42
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:37 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:32 +0100

perf trace/scripting: Check return val of perl_run()

The return value from perl_run() is currently ignored, but it
should be checked and used to exit perf if there are problems
loading the script.

Signed-off-by: Tom Zanussi <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/util/trace-event-perl.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c
index 6f10e76..6d6d76b 100644
--- a/tools/perf/util/trace-event-perl.c
+++ b/tools/perf/util/trace-event-perl.c
@@ -379,7 +379,11 @@ static int perl_start_script(const char *script, int argc, const char **argv)
goto error;
}

- perl_run(my_perl);
+ if (perl_run(my_perl)) {
+ err = -1;
+ goto error;
+ }
+
if (SvTRUE(ERRSV)) {
err = -1;
goto error;

2009-12-15 09:40:57

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: List available scripts

Commit-ID: 4b9c0c596ea826ef784eb83f663c5351ed01ba6d
Gitweb: http://git.kernel.org/tip/4b9c0c596ea826ef784eb83f663c5351ed01ba6d
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:38 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:32 +0100

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 <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/builtin-trace.c | 199 +++++++++++++++++++-
.../perf/scripts/perl/bin/check-perf-trace-report | 1 +
tools/perf/scripts/perl/bin/rw-by-file-report | 2 +
tools/perf/scripts/perl/bin/rw-by-pid-report | 1 +
tools/perf/scripts/perl/bin/wakeup-latency-report | 1 +
tools/perf/scripts/perl/bin/workqueue-stats-report | 1 +
6 files changed, 204 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 88b0353..7674153 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,
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),
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-report b/tools/perf/scripts/perl/bin/check-perf-trace-report
index 89948b0..7fc4a03 100644
--- a/tools/perf/scripts/perl/bin/check-perf-trace-report
+++ b/tools/perf/scripts/perl/bin/check-perf-trace-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: useless but exhaustive test script
perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl


diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report
index 1c05675..eddb9cc 100644
--- a/tools/perf/scripts/perl/bin/rw-by-file-report
+++ b/tools/perf/scripts/perl/bin/rw-by-file-report
@@ -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


diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report
index cea16f7..7f44c25 100644
--- a/tools/perf/scripts/perl/bin/rw-by-pid-report
+++ b/tools/perf/scripts/perl/bin/rw-by-pid-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: system-wide r/w activity
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl


diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report
index 85769dc..fce3adc 100644
--- a/tools/perf/scripts/perl/bin/wakeup-latency-report
+++ b/tools/perf/scripts/perl/bin/wakeup-latency-report
@@ -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


diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report
index aa68435..71cfbd1 100644
--- a/tools/perf/scripts/perl/bin/workqueue-stats-report
+++ b/tools/perf/scripts/perl/bin/workqueue-stats-report
@@ -1,4 +1,5 @@
#!/bin/bash
+# description: workqueue stats (ins/exe/create/destroy)
perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl

2009-12-15 09:41:22

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: Add 'record' and 'report' options

Commit-ID: 3875294f5c0d7b9ef96ffc373d8a956ebd7c0c7f
Gitweb: http://git.kernel.org/tip/3875294f5c0d7b9ef96ffc373d8a956ebd7c0c7f
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:39 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:33 +0100

perf trace/scripting: Add 'record' and 'report' options

Allow scripts to be recorded/executed by simply specifying the
script root name (the script name minus extension) along with
'record' or 'report' to 'perf trace'.

The script names shown by 'perf trace -l' can be directly used
to run the command-line contained within the corresponding
'-record' and '-report' versions of scripts in the scripts/*/bin
directories.

For example, to record the trace data needed to run the
wakeup-latency.pl script, the user can easily find the name of
the corresponding script from the script list and invoke it
using 'perf trace record', without having to remember the
details of how to do the same thing using the lower-level perf
trace command-line options:

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

root@tropicana:~# perf trace record wakeup-latency
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.296 MB perf.data (~12931
samples) ]

To run the wakeup-latency.pl script using the captured data,
change 'record' to 'report' in the command-line:

root@tropicana:~# perf trace report wakeup-latency

wakeup_latency stats:

total_wakeups: 65
avg_wakeup_latency (ns): 22417
min_wakeup_latency (ns): 3470
max_wakeup_latency (ns): 223311

perf trace Perl script stopped

If the script takes options, thay can be simply added to the end
of the 'report' invocation:

root@tropicana:~# perf trace record rw-by-file
^C[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.782 MB perf.data (~34171
samples) ]

root@tropicana:~# perf trace report rw-by-file perf

file read counts for perf:

fd # reads bytes_requested
------ ---------- -----------
122 1934 1980416
120 1 32

file write counts for perf:

fd # writes bytes_written
------ ---------- -----------
3 4006 280568

perf trace Perl script stopped

Signed-off-by: Tom Zanussi <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/builtin-trace.c | 84 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 83 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 7674153..7e744f7 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -469,6 +469,49 @@ static int list_available_scripts(const struct option *opt __used,
exit(0);
}

+static char *get_script_path(const char *script_root, const char *suffix)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ char script_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char lang_path[MAXPATHLEN];
+ char *str, *__script_root;
+ char *path = NULL;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return NULL;
+
+ 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, suffix);
+ if (str) {
+ *str = '\0';
+ if (strcmp(__script_root, script_root))
+ continue;
+ snprintf(script_path, MAXPATHLEN, "%s/%s",
+ lang_path, script_dirent.d_name);
+ path = strdup(script_path);
+ free(__script_root);
+ break;
+ }
+ free(__script_root);
+ }
+ }
+
+ return path;
+}
+
static const char * const annotate_usage[] = {
"perf trace [<options>] <command>",
NULL
@@ -494,8 +537,47 @@ static const struct option options[] = {

int cmd_trace(int argc, const char **argv, const char *prefix __used)
{
- int err;
struct perf_session *session;
+ const char *suffix = NULL;
+ const char **__argv;
+ char *script_path;
+ int i, err;
+
+ if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a record script\n");
+ return -1;
+ }
+ suffix = RECORD_SUFFIX;
+ }
+
+ if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) {
+ if (argc < 3) {
+ fprintf(stderr,
+ "Please specify a report script\n");
+ return -1;
+ }
+ suffix = REPORT_SUFFIX;
+ }
+
+ if (suffix) {
+ script_path = get_script_path(argv[2], suffix);
+ if (!script_path) {
+ fprintf(stderr, "script not found\n");
+ return -1;
+ }
+
+ __argv = malloc((argc + 1) * sizeof(const char *));
+ __argv[0] = "/bin/sh";
+ __argv[1] = script_path;
+ for (i = 3; i < argc; i++)
+ __argv[i - 1] = argv[i];
+ __argv[argc - 1] = NULL;
+
+ execvp("/bin/sh", (char **)__argv);
+ exit(-1);
+ }

symbol__init(0);

2009-12-15 09:41:27

by Tom Zanussi

[permalink] [raw]
Subject: [tip:perf/urgent] perf trace/scripting: Update Documentation

Commit-ID: a6005123ce22770dbd91bc3cb637ce0807ab959b
Gitweb: http://git.kernel.org/tip/a6005123ce22770dbd91bc3cb637ce0807ab959b
Author: Tom Zanussi <[email protected]>
AuthorDate: Tue, 15 Dec 2009 02:53:40 -0600
Committer: Ingo Molnar <[email protected]>
CommitDate: Tue, 15 Dec 2009 10:31:33 +0100

perf trace/scripting: Update Documentation

Update the perf-trace page with new and missing options and
remove some unused ones.

Signed-off-by: Tom Zanussi <[email protected]>
Cc: [email protected]
Cc: [email protected]
LKML-Reference: <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
---
tools/perf/Documentation/perf-trace.txt | 27 ++++++++++++++++++++++++++-
1 files changed, 26 insertions(+), 1 deletions(-)

diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
index 07065ef..60e5900 100644
--- a/tools/perf/Documentation/perf-trace.txt
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -8,18 +8,43 @@ perf-trace - Read perf.data (created by perf record) and display trace output
SYNOPSIS
--------
[verse]
-'perf trace' [-i <file> | --input=file] symbol_name
+'perf trace' {record <script> | report <script> [args] }

DESCRIPTION
-----------
This command reads the input file and displays the trace recorded.

+There are several variants of perf trace:
+
+ 'perf trace' to see a detailed trace of the workload that was
+ recorded.
+
+ 'perf trace record <script>' to record the events required for 'perf
+ trace report'. <script> is the name displayed in the output of
+ 'perf trace --list' i.e. the actual script name minus any language
+ extension.
+
+ 'perf trace report <script>' to run and display the results of
+ <script>. <script> is the name displayed in the output of 'perf
+ trace --list' i.e. the actual script name minus any language
+ extension. The perf.data output from a previous run of 'perf trace
+ record <script>' is used and should be present for this command to
+ succeed.
+
OPTIONS
-------
-D::
--dump-raw-trace=::
Display verbose dump of the trace data.

+-L::
+--Latency=::
+ Show latency attributes (irqs/preemption disabled, etc).
+
+-l::
+--list=::
+ Display a list of available trace scripts.
+
-s::
--script=::
Process trace data with the given script ([lang]:script[.ext]).