diff options
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 259 |
1 files changed, 254 insertions, 5 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index a7750256c40..abb914aa7be 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -5,6 +5,50 @@ | |||
5 | #include "util/symbol.h" | 5 | #include "util/symbol.h" |
6 | #include "util/thread.h" | 6 | #include "util/thread.h" |
7 | #include "util/header.h" | 7 | #include "util/header.h" |
8 | #include "util/exec_cmd.h" | ||
9 | #include "util/trace-event.h" | ||
10 | |||
11 | static char const *script_name; | ||
12 | static char const *generate_script_lang; | ||
13 | |||
14 | static int default_start_script(const char *script __attribute((unused))) | ||
15 | { | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | static int default_stop_script(void) | ||
20 | { | ||
21 | return 0; | ||
22 | } | ||
23 | |||
24 | static int default_generate_script(const char *outfile __attribute ((unused))) | ||
25 | { | ||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | static struct scripting_ops default_scripting_ops = { | ||
30 | .start_script = default_start_script, | ||
31 | .stop_script = default_stop_script, | ||
32 | .process_event = print_event, | ||
33 | .generate_script = default_generate_script, | ||
34 | }; | ||
35 | |||
36 | static struct scripting_ops *scripting_ops; | ||
37 | |||
38 | static void setup_scripting(void) | ||
39 | { | ||
40 | /* make sure PERF_EXEC_PATH is set for scripts */ | ||
41 | perf_set_argv_exec_path(perf_exec_path()); | ||
42 | |||
43 | setup_perl_scripting(); | ||
44 | |||
45 | scripting_ops = &default_scripting_ops; | ||
46 | } | ||
47 | |||
48 | static int cleanup_scripting(void) | ||
49 | { | ||
50 | return scripting_ops->stop_script(); | ||
51 | } | ||
8 | 52 | ||
9 | #include "util/parse-options.h" | 53 | #include "util/parse-options.h" |
10 | 54 | ||
@@ -13,11 +57,12 @@ | |||
13 | 57 | ||
14 | #include "util/trace-event.h" | 58 | #include "util/trace-event.h" |
15 | #include "util/data_map.h" | 59 | #include "util/data_map.h" |
60 | #include "util/exec_cmd.h" | ||
16 | 61 | ||
17 | static char const *input_name = "perf.data"; | 62 | static char const *input_name = "perf.data"; |
18 | 63 | ||
19 | static struct perf_header *header; | 64 | static struct perf_header *header; |
20 | static u64 sample_type; | 65 | static u64 sample_type; |
21 | 66 | ||
22 | static int process_sample_event(event_t *event) | 67 | static int process_sample_event(event_t *event) |
23 | { | 68 | { |
@@ -69,7 +114,8 @@ static int process_sample_event(event_t *event) | |||
69 | * field, although it should be the same than this perf | 114 | * field, although it should be the same than this perf |
70 | * event pid | 115 | * event pid |
71 | */ | 116 | */ |
72 | print_event(cpu, raw->data, raw->size, timestamp, thread->comm); | 117 | scripting_ops->process_event(cpu, raw->data, raw->size, |
118 | timestamp, thread->comm); | ||
73 | } | 119 | } |
74 | event__stats.total += period; | 120 | event__stats.total += period; |
75 | 121 | ||
@@ -105,6 +151,154 @@ static int __cmd_trace(void) | |||
105 | 0, 0, &event__cwdlen, &event__cwd); | 151 | 0, 0, &event__cwdlen, &event__cwd); |
106 | } | 152 | } |
107 | 153 | ||
154 | struct script_spec { | ||
155 | struct list_head node; | ||
156 | struct scripting_ops *ops; | ||
157 | char spec[0]; | ||
158 | }; | ||
159 | |||
160 | LIST_HEAD(script_specs); | ||
161 | |||
162 | static struct script_spec *script_spec__new(const char *spec, | ||
163 | struct scripting_ops *ops) | ||
164 | { | ||
165 | struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1); | ||
166 | |||
167 | if (s != NULL) { | ||
168 | strcpy(s->spec, spec); | ||
169 | s->ops = ops; | ||
170 | } | ||
171 | |||
172 | return s; | ||
173 | } | ||
174 | |||
175 | static void script_spec__delete(struct script_spec *s) | ||
176 | { | ||
177 | free(s->spec); | ||
178 | free(s); | ||
179 | } | ||
180 | |||
181 | static void script_spec__add(struct script_spec *s) | ||
182 | { | ||
183 | list_add_tail(&s->node, &script_specs); | ||
184 | } | ||
185 | |||
186 | static struct script_spec *script_spec__find(const char *spec) | ||
187 | { | ||
188 | struct script_spec *s; | ||
189 | |||
190 | list_for_each_entry(s, &script_specs, node) | ||
191 | if (strcasecmp(s->spec, spec) == 0) | ||
192 | return s; | ||
193 | return NULL; | ||
194 | } | ||
195 | |||
196 | static struct script_spec *script_spec__findnew(const char *spec, | ||
197 | struct scripting_ops *ops) | ||
198 | { | ||
199 | struct script_spec *s = script_spec__find(spec); | ||
200 | |||
201 | if (s) | ||
202 | return s; | ||
203 | |||
204 | s = script_spec__new(spec, ops); | ||
205 | if (!s) | ||
206 | goto out_delete_spec; | ||
207 | |||
208 | script_spec__add(s); | ||
209 | |||
210 | return s; | ||
211 | |||
212 | out_delete_spec: | ||
213 | script_spec__delete(s); | ||
214 | |||
215 | return NULL; | ||
216 | } | ||
217 | |||
218 | int script_spec_register(const char *spec, struct scripting_ops *ops) | ||
219 | { | ||
220 | struct script_spec *s; | ||
221 | |||
222 | s = script_spec__find(spec); | ||
223 | if (s) | ||
224 | return -1; | ||
225 | |||
226 | s = script_spec__findnew(spec, ops); | ||
227 | if (!s) | ||
228 | return -1; | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static struct scripting_ops *script_spec__lookup(const char *spec) | ||
234 | { | ||
235 | struct script_spec *s = script_spec__find(spec); | ||
236 | if (!s) | ||
237 | return NULL; | ||
238 | |||
239 | return s->ops; | ||
240 | } | ||
241 | |||
242 | static void list_available_languages(void) | ||
243 | { | ||
244 | struct script_spec *s; | ||
245 | |||
246 | fprintf(stderr, "\n"); | ||
247 | fprintf(stderr, "Scripting language extensions (used in " | ||
248 | "perf trace -s [spec:]script.[spec]):\n\n"); | ||
249 | |||
250 | list_for_each_entry(s, &script_specs, node) | ||
251 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); | ||
252 | |||
253 | fprintf(stderr, "\n"); | ||
254 | } | ||
255 | |||
256 | static int parse_scriptname(const struct option *opt __used, | ||
257 | const char *str, int unset __used) | ||
258 | { | ||
259 | char spec[PATH_MAX]; | ||
260 | const char *script, *ext; | ||
261 | int len; | ||
262 | |||
263 | if (strcmp(str, "list") == 0) { | ||
264 | list_available_languages(); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | script = strchr(str, ':'); | ||
269 | if (script) { | ||
270 | len = script - str; | ||
271 | if (len >= PATH_MAX) { | ||
272 | fprintf(stderr, "invalid language specifier"); | ||
273 | return -1; | ||
274 | } | ||
275 | strncpy(spec, str, len); | ||
276 | spec[len] = '\0'; | ||
277 | scripting_ops = script_spec__lookup(spec); | ||
278 | if (!scripting_ops) { | ||
279 | fprintf(stderr, "invalid language specifier"); | ||
280 | return -1; | ||
281 | } | ||
282 | script++; | ||
283 | } else { | ||
284 | script = str; | ||
285 | ext = strchr(script, '.'); | ||
286 | if (!ext) { | ||
287 | fprintf(stderr, "invalid script extension"); | ||
288 | return -1; | ||
289 | } | ||
290 | scripting_ops = script_spec__lookup(++ext); | ||
291 | if (!scripting_ops) { | ||
292 | fprintf(stderr, "invalid script extension"); | ||
293 | return -1; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | script_name = strdup(script); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
108 | static const char * const annotate_usage[] = { | 302 | static const char * const annotate_usage[] = { |
109 | "perf trace [<options>] <command>", | 303 | "perf trace [<options>] <command>", |
110 | NULL | 304 | NULL |
@@ -117,13 +311,23 @@ static const struct option options[] = { | |||
117 | "be more verbose (show symbol address, etc)"), | 311 | "be more verbose (show symbol address, etc)"), |
118 | OPT_BOOLEAN('l', "latency", &latency_format, | 312 | OPT_BOOLEAN('l', "latency", &latency_format, |
119 | "show latency attributes (irqs/preemption disabled, etc)"), | 313 | "show latency attributes (irqs/preemption disabled, etc)"), |
314 | OPT_CALLBACK('s', "script", NULL, "name", | ||
315 | "script file name (lang:script name, script name, or *)", | ||
316 | parse_scriptname), | ||
317 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", | ||
318 | "generate perf-trace.xx script in specified language"), | ||
319 | |||
120 | OPT_END() | 320 | OPT_END() |
121 | }; | 321 | }; |
122 | 322 | ||
123 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 323 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
124 | { | 324 | { |
325 | int err; | ||
326 | |||
125 | symbol__init(0); | 327 | symbol__init(0); |
126 | 328 | ||
329 | setup_scripting(); | ||
330 | |||
127 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 331 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
128 | if (argc) { | 332 | if (argc) { |
129 | /* | 333 | /* |
@@ -136,5 +340,50 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
136 | 340 | ||
137 | setup_pager(); | 341 | setup_pager(); |
138 | 342 | ||
139 | return __cmd_trace(); | 343 | if (generate_script_lang) { |
344 | struct stat perf_stat; | ||
345 | |||
346 | int input = open(input_name, O_RDONLY); | ||
347 | if (input < 0) { | ||
348 | perror("failed to open file"); | ||
349 | exit(-1); | ||
350 | } | ||
351 | |||
352 | err = fstat(input, &perf_stat); | ||
353 | if (err < 0) { | ||
354 | perror("failed to stat file"); | ||
355 | exit(-1); | ||
356 | } | ||
357 | |||
358 | if (!perf_stat.st_size) { | ||
359 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
360 | exit(0); | ||
361 | } | ||
362 | |||
363 | scripting_ops = script_spec__lookup(generate_script_lang); | ||
364 | if (!scripting_ops) { | ||
365 | fprintf(stderr, "invalid language specifier"); | ||
366 | return -1; | ||
367 | } | ||
368 | |||
369 | header = perf_header__new(); | ||
370 | if (header == NULL) | ||
371 | return -1; | ||
372 | |||
373 | perf_header__read(header, input); | ||
374 | err = scripting_ops->generate_script("perf-trace"); | ||
375 | goto out; | ||
376 | } | ||
377 | |||
378 | if (script_name) { | ||
379 | err = scripting_ops->start_script(script_name); | ||
380 | if (err) | ||
381 | goto out; | ||
382 | } | ||
383 | |||
384 | err = __cmd_trace(); | ||
385 | |||
386 | cleanup_scripting(); | ||
387 | out: | ||
388 | return err; | ||
140 | } | 389 | } |