aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Zanussi <tzanussi@gmail.com>2009-11-25 02:15:46 -0500
committerIngo Molnar <mingo@elte.hu>2009-11-28 04:04:24 -0500
commit956ffd027bedc4106b901eb6a50f0a6c6de4113d (patch)
tree11634edc2ac27a81db9a5d2a15b3897992761272
parent1ed091c45ae33b2179d387573c3fe3f3b4adf60a (diff)
perf trace: Add scripting ops
Adds an interface, scripting_ops, that when implemented for a particular scripting language enables built-in support for trace stream processing using that language. The interface is designed to enable full-fledged language interpreters to be embedded inside the perf executable and thereby make the full capabilities of the supported languages available for trace processing. See below for details on the interface. This patch also adds a couple command-line options to 'perf trace': The -s option option is used to specify the script to be run. Script names that can be used with -s take the form: [language spec:]scriptname[.ext] Scripting languages register a set of 'language specs' that can be used to specify scripts for the registered languages. The specs can be used either as prefixes or extensions. If [language spec:] is used, the script is taken as a script of the matching language regardless of any extension it might have. If [language spec:] is not used, [.ext] is used to look up the language it corresponds to. Language specs are case insensitive. e.g. Perl scripts can be specified in the following ways: Perl:scriptname pl:scriptname.py # extension ignored PL:scriptname scriptname.pl scriptname.perl The -g [language spec] option gives users an easy starting point for writing scripts in the specified language. Scripting support for a particular language can implement a generate_script() scripting op that outputs an empty (or near-empty) set of handlers for all the events contained in a given perf.data trace file - this option gives users a direct way to access that. Adding support for a scripting language --------------------------------------- The main thing that needs to be done do add support for a new language is to implement the scripting_ops interface: It consists of the following four functions: start_script() stop_script() process_event() generate_script() start_script() is called before any events are processed, and is meant to give the scripting language support an opportunity to set things up to receive events e.g. create and initialize an instance of a language interpreter. stop_script() is called after all events are processed, and is meant to give the scripting language support an opportunity to clean up e.g. destroy the interpreter instance, etc. process_event() is called once for each event and takes as its main parameter a pointer to the binary trace event record to be processed. The implementation is responsible for picking out the binary fields from the event record and sending them to the script handler function associated with that event e.g. a function derived from the event name it's meant to handle e.g. 'sched::sched_switch()'. The 'format' information for trace events can be used to parse the binary data and map it into a form usable by a given scripting language; see the Perl implemention in subsequent patches for one possible way to leverage the existing trace format parsing code in perf and map that info into specific scripting language types. generate_script() should generate a ready-to-run script for the current set of events in the trace, preferably with bodies that print out every field for each event. Again, look at the Perl implementation for clues as to how that can be done. This is an optional, but very useful op. Support for a given language should also add a language-specific setup function and call it from setup_scripting(). The language-specific setup function associates the the scripting ops for that language with one or more 'language specifiers' (see below) using script_spec_register(). When a script name is specified on the command line, the scripting ops associated with the specified language are used to instantiate and use the appropriate interpreter to process the trace stream. In general, it should be relatively easy to add support for a new language, especially if the language implementation supports an interface allowing an interpreter to be 'embedded' inside another program (in this case the containing program will be 'perf trace'). If so, it should be relatively straightforward to translate trace events into invocations of user-defined script functions where e.g. the function name corresponds to the event type and the function parameters correspond to the event fields. The event and field type information exported by the event tracing infrastructure (via the event 'format' files) should be enough to parse and send any piece of trace data to the user script. The easiest way to see how this can be done would be to look at the Perl implementation contained in perf/util/trace-event-perl.c/.h. There are a couple of other things that aren't covered by the scripting_ops or setup interface and are technically optional, but should be implemented if possible. One of these is support for 'flag' and 'symbolic' fields e.g. being able to use more human-readable values such as 'GFP_KERNEL' or HI/BLOCK_IOPOLL/TASKLET in place of raw flag values. See the Perl implementation to see how this can be done. The other thing is support for 'calling back' into the perf executable to access e.g. uncommon fields not passed by default into handler functions, or any metadata the implementation might want to make available to users via the language interface. Again, see the Perl implementation for examples. Signed-off-by: Tom Zanussi <tzanussi@gmail.com> Cc: fweisbec@gmail.com Cc: rostedt@goodmis.org Cc: anton@samba.org Cc: hch@infradead.org LKML-Reference: <1259133352-23685-2-git-send-email-tzanussi@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/builtin-trace.c255
-rw-r--r--tools/perf/util/trace-event.h11
2 files changed, 261 insertions, 5 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index a7750256c408..e96bb534b948 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -6,6 +6,46 @@
6#include "util/thread.h" 6#include "util/thread.h"
7#include "util/header.h" 7#include "util/header.h"
8 8
9static char const *script_name;
10static char const *generate_script_lang;
11
12static int default_start_script(const char *script __attribute((unused)))
13{
14 return 0;
15}
16
17static int default_stop_script(void)
18{
19 return 0;
20}
21
22static int default_generate_script(const char *outfile __attribute ((unused)))
23{
24 return 0;
25}
26
27static struct scripting_ops default_scripting_ops = {
28 .start_script = default_start_script,
29 .stop_script = default_stop_script,
30 .process_event = print_event,
31 .generate_script = default_generate_script,
32};
33
34static struct scripting_ops *scripting_ops;
35
36static void setup_scripting(void)
37{
38 /* make sure PERF_EXEC_PATH is set for scripts */
39 perf_set_argv_exec_path(perf_exec_path());
40
41 scripting_ops = &default_scripting_ops;
42}
43
44static int cleanup_scripting(void)
45{
46 return scripting_ops->stop_script();
47}
48
9#include "util/parse-options.h" 49#include "util/parse-options.h"
10 50
11#include "perf.h" 51#include "perf.h"
@@ -13,11 +53,12 @@
13 53
14#include "util/trace-event.h" 54#include "util/trace-event.h"
15#include "util/data_map.h" 55#include "util/data_map.h"
56#include "util/exec_cmd.h"
16 57
17static char const *input_name = "perf.data"; 58static char const *input_name = "perf.data";
18 59
19static struct perf_header *header; 60static struct perf_header *header;
20static u64 sample_type; 61static u64 sample_type;
21 62
22static int process_sample_event(event_t *event) 63static int process_sample_event(event_t *event)
23{ 64{
@@ -69,7 +110,8 @@ static int process_sample_event(event_t *event)
69 * field, although it should be the same than this perf 110 * field, although it should be the same than this perf
70 * event pid 111 * event pid
71 */ 112 */
72 print_event(cpu, raw->data, raw->size, timestamp, thread->comm); 113 scripting_ops->process_event(cpu, raw->data, raw->size,
114 timestamp, thread->comm);
73 } 115 }
74 event__stats.total += period; 116 event__stats.total += period;
75 117
@@ -105,6 +147,154 @@ static int __cmd_trace(void)
105 0, 0, &event__cwdlen, &event__cwd); 147 0, 0, &event__cwdlen, &event__cwd);
106} 148}
107 149
150struct script_spec {
151 struct list_head node;
152 struct scripting_ops *ops;
153 char spec[0];
154};
155
156LIST_HEAD(script_specs);
157
158static struct script_spec *script_spec__new(const char *spec,
159 struct scripting_ops *ops)
160{
161 struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
162
163 if (s != NULL) {
164 strcpy(s->spec, spec);
165 s->ops = ops;
166 }
167
168 return s;
169}
170
171static void script_spec__delete(struct script_spec *s)
172{
173 free(s->spec);
174 free(s);
175}
176
177static void script_spec__add(struct script_spec *s)
178{
179 list_add_tail(&s->node, &script_specs);
180}
181
182static struct script_spec *script_spec__find(const char *spec)
183{
184 struct script_spec *s;
185
186 list_for_each_entry(s, &script_specs, node)
187 if (strcasecmp(s->spec, spec) == 0)
188 return s;
189 return NULL;
190}
191
192static struct script_spec *script_spec__findnew(const char *spec,
193 struct scripting_ops *ops)
194{
195 struct script_spec *s = script_spec__find(spec);
196
197 if (s)
198 return s;
199
200 s = script_spec__new(spec, ops);
201 if (!s)
202 goto out_delete_spec;
203
204 script_spec__add(s);
205
206 return s;
207
208out_delete_spec:
209 script_spec__delete(s);
210
211 return NULL;
212}
213
214int script_spec_register(const char *spec, struct scripting_ops *ops)
215{
216 struct script_spec *s;
217
218 s = script_spec__find(spec);
219 if (s)
220 return -1;
221
222 s = script_spec__findnew(spec, ops);
223 if (!s)
224 return -1;
225
226 return 0;
227}
228
229static struct scripting_ops *script_spec__lookup(const char *spec)
230{
231 struct script_spec *s = script_spec__find(spec);
232 if (!s)
233 return NULL;
234
235 return s->ops;
236}
237
238static void list_available_languages(void)
239{
240 struct script_spec *s;
241
242 fprintf(stderr, "\n");
243 fprintf(stderr, "Scripting language extensions (used in "
244 "perf trace -s [spec:]script.[spec]):\n\n");
245
246 list_for_each_entry(s, &script_specs, node)
247 fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
248
249 fprintf(stderr, "\n");
250}
251
252static int parse_scriptname(const struct option *opt __used,
253 const char *str, int unset __used)
254{
255 char spec[PATH_MAX];
256 const char *script, *ext;
257 int len;
258
259 if (strcmp(str, "list") == 0) {
260 list_available_languages();
261 return 0;
262 }
263
264 script = strchr(str, ':');
265 if (script) {
266 len = script - str;
267 if (len >= PATH_MAX) {
268 fprintf(stderr, "invalid language specifier");
269 return -1;
270 }
271 strncpy(spec, str, len);
272 spec[len] = '\0';
273 scripting_ops = script_spec__lookup(spec);
274 if (!scripting_ops) {
275 fprintf(stderr, "invalid language specifier");
276 return -1;
277 }
278 script++;
279 } else {
280 script = str;
281 ext = strchr(script, '.');
282 if (!ext) {
283 fprintf(stderr, "invalid script extension");
284 return -1;
285 }
286 scripting_ops = script_spec__lookup(++ext);
287 if (!scripting_ops) {
288 fprintf(stderr, "invalid script extension");
289 return -1;
290 }
291 }
292
293 script_name = strdup(script);
294
295 return 0;
296}
297
108static const char * const annotate_usage[] = { 298static const char * const annotate_usage[] = {
109 "perf trace [<options>] <command>", 299 "perf trace [<options>] <command>",
110 NULL 300 NULL
@@ -117,13 +307,23 @@ static const struct option options[] = {
117 "be more verbose (show symbol address, etc)"), 307 "be more verbose (show symbol address, etc)"),
118 OPT_BOOLEAN('l', "latency", &latency_format, 308 OPT_BOOLEAN('l', "latency", &latency_format,
119 "show latency attributes (irqs/preemption disabled, etc)"), 309 "show latency attributes (irqs/preemption disabled, etc)"),
310 OPT_CALLBACK('s', "script", NULL, "name",
311 "script file name (lang:script name, script name, or *)",
312 parse_scriptname),
313 OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
314 "generate perf-trace.xx script in specified language"),
315
120 OPT_END() 316 OPT_END()
121}; 317};
122 318
123int cmd_trace(int argc, const char **argv, const char *prefix __used) 319int cmd_trace(int argc, const char **argv, const char *prefix __used)
124{ 320{
321 int err;
322
125 symbol__init(0); 323 symbol__init(0);
126 324
325 setup_scripting();
326
127 argc = parse_options(argc, argv, options, annotate_usage, 0); 327 argc = parse_options(argc, argv, options, annotate_usage, 0);
128 if (argc) { 328 if (argc) {
129 /* 329 /*
@@ -136,5 +336,50 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
136 336
137 setup_pager(); 337 setup_pager();
138 338
139 return __cmd_trace(); 339 if (generate_script_lang) {
340 struct stat perf_stat;
341
342 int input = open(input_name, O_RDONLY);
343 if (input < 0) {
344 perror("failed to open file");
345 exit(-1);
346 }
347
348 err = fstat(input, &perf_stat);
349 if (err < 0) {
350 perror("failed to stat file");
351 exit(-1);
352 }
353
354 if (!perf_stat.st_size) {
355 fprintf(stderr, "zero-sized file, nothing to do!\n");
356 exit(0);
357 }
358
359 scripting_ops = script_spec__lookup(generate_script_lang);
360 if (!scripting_ops) {
361 fprintf(stderr, "invalid language specifier");
362 return -1;
363 }
364
365 header = perf_header__new();
366 if (header == NULL)
367 return -1;
368
369 perf_header__read(header, input);
370 err = scripting_ops->generate_script("perf-trace");
371 goto out;
372 }
373
374 if (script_name) {
375 err = scripting_ops->start_script(script_name);
376 if (err)
377 goto out;
378 }
379
380 err = __cmd_trace();
381
382 cleanup_scripting();
383out:
384 return err;
140} 385}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index dd51c6872a15..e7aaf002e667 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -259,4 +259,15 @@ enum trace_flag_type {
259 TRACE_FLAG_SOFTIRQ = 0x10, 259 TRACE_FLAG_SOFTIRQ = 0x10,
260}; 260};
261 261
262struct scripting_ops {
263 const char *name;
264 int (*start_script) (const char *);
265 int (*stop_script) (void);
266 void (*process_event) (int cpu, void *data, int size,
267 unsigned long long nsecs, char *comm);
268 int (*generate_script) (const char *outfile);
269};
270
271int script_spec_register(const char *spec, struct scripting_ops *ops);
272
262#endif /* __PERF_TRACE_EVENTS_H */ 273#endif /* __PERF_TRACE_EVENTS_H */