Root/
1 | #include "builtin.h" |
2 | |
3 | #include "util/util.h" |
4 | #include "util/cache.h" |
5 | #include "util/symbol.h" |
6 | #include "util/thread.h" |
7 | #include "util/header.h" |
8 | #include "util/exec_cmd.h" |
9 | #include "util/trace-event.h" |
10 | #include "util/session.h" |
11 | |
12 | static char const *script_name; |
13 | static char const *generate_script_lang; |
14 | |
15 | static int default_start_script(const char *script __unused, |
16 | int argc __unused, |
17 | const char **argv __unused) |
18 | { |
19 | return 0; |
20 | } |
21 | |
22 | static int default_stop_script(void) |
23 | { |
24 | return 0; |
25 | } |
26 | |
27 | static int default_generate_script(const char *outfile __unused) |
28 | { |
29 | return 0; |
30 | } |
31 | |
32 | static struct scripting_ops default_scripting_ops = { |
33 | .start_script = default_start_script, |
34 | .stop_script = default_stop_script, |
35 | .process_event = print_event, |
36 | .generate_script = default_generate_script, |
37 | }; |
38 | |
39 | static struct scripting_ops *scripting_ops; |
40 | |
41 | static void setup_scripting(void) |
42 | { |
43 | /* make sure PERF_EXEC_PATH is set for scripts */ |
44 | perf_set_argv_exec_path(perf_exec_path()); |
45 | |
46 | setup_perl_scripting(); |
47 | setup_python_scripting(); |
48 | |
49 | scripting_ops = &default_scripting_ops; |
50 | } |
51 | |
52 | static int cleanup_scripting(void) |
53 | { |
54 | return scripting_ops->stop_script(); |
55 | } |
56 | |
57 | #include "util/parse-options.h" |
58 | |
59 | #include "perf.h" |
60 | #include "util/debug.h" |
61 | |
62 | #include "util/trace-event.h" |
63 | #include "util/exec_cmd.h" |
64 | |
65 | static char const *input_name = "perf.data"; |
66 | |
67 | static int process_sample_event(event_t *event, struct perf_session *session) |
68 | { |
69 | struct sample_data data; |
70 | struct thread *thread; |
71 | |
72 | memset(&data, 0, sizeof(data)); |
73 | data.time = -1; |
74 | data.cpu = -1; |
75 | data.period = 1; |
76 | |
77 | event__parse_sample(event, session->sample_type, &data); |
78 | |
79 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
80 | data.pid, data.tid, data.ip, data.period); |
81 | |
82 | thread = perf_session__findnew(session, event->ip.pid); |
83 | if (thread == NULL) { |
84 | pr_debug("problem processing %d event, skipping it.\n", |
85 | event->header.type); |
86 | return -1; |
87 | } |
88 | |
89 | if (session->sample_type & PERF_SAMPLE_RAW) { |
90 | /* |
91 | * FIXME: better resolve from pid from the struct trace_entry |
92 | * field, although it should be the same than this perf |
93 | * event pid |
94 | */ |
95 | scripting_ops->process_event(data.cpu, data.raw_data, |
96 | data.raw_size, |
97 | data.time, thread->comm); |
98 | } |
99 | |
100 | session->events_stats.total += data.period; |
101 | return 0; |
102 | } |
103 | |
104 | static struct perf_event_ops event_ops = { |
105 | .sample = process_sample_event, |
106 | .comm = event__process_comm, |
107 | }; |
108 | |
109 | static int __cmd_trace(struct perf_session *session) |
110 | { |
111 | return perf_session__process_events(session, &event_ops); |
112 | } |
113 | |
114 | struct script_spec { |
115 | struct list_head node; |
116 | struct scripting_ops *ops; |
117 | char spec[0]; |
118 | }; |
119 | |
120 | LIST_HEAD(script_specs); |
121 | |
122 | static struct script_spec *script_spec__new(const char *spec, |
123 | struct scripting_ops *ops) |
124 | { |
125 | struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1); |
126 | |
127 | if (s != NULL) { |
128 | strcpy(s->spec, spec); |
129 | s->ops = ops; |
130 | } |
131 | |
132 | return s; |
133 | } |
134 | |
135 | static void script_spec__delete(struct script_spec *s) |
136 | { |
137 | free(s->spec); |
138 | free(s); |
139 | } |
140 | |
141 | static void script_spec__add(struct script_spec *s) |
142 | { |
143 | list_add_tail(&s->node, &script_specs); |
144 | } |
145 | |
146 | static struct script_spec *script_spec__find(const char *spec) |
147 | { |
148 | struct script_spec *s; |
149 | |
150 | list_for_each_entry(s, &script_specs, node) |
151 | if (strcasecmp(s->spec, spec) == 0) |
152 | return s; |
153 | return NULL; |
154 | } |
155 | |
156 | static struct script_spec *script_spec__findnew(const char *spec, |
157 | struct scripting_ops *ops) |
158 | { |
159 | struct script_spec *s = script_spec__find(spec); |
160 | |
161 | if (s) |
162 | return s; |
163 | |
164 | s = script_spec__new(spec, ops); |
165 | if (!s) |
166 | goto out_delete_spec; |
167 | |
168 | script_spec__add(s); |
169 | |
170 | return s; |
171 | |
172 | out_delete_spec: |
173 | script_spec__delete(s); |
174 | |
175 | return NULL; |
176 | } |
177 | |
178 | int script_spec_register(const char *spec, struct scripting_ops *ops) |
179 | { |
180 | struct script_spec *s; |
181 | |
182 | s = script_spec__find(spec); |
183 | if (s) |
184 | return -1; |
185 | |
186 | s = script_spec__findnew(spec, ops); |
187 | if (!s) |
188 | return -1; |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | static struct scripting_ops *script_spec__lookup(const char *spec) |
194 | { |
195 | struct script_spec *s = script_spec__find(spec); |
196 | if (!s) |
197 | return NULL; |
198 | |
199 | return s->ops; |
200 | } |
201 | |
202 | static void list_available_languages(void) |
203 | { |
204 | struct script_spec *s; |
205 | |
206 | fprintf(stderr, "\n"); |
207 | fprintf(stderr, "Scripting language extensions (used in " |
208 | "perf trace -s [spec:]script.[spec]):\n\n"); |
209 | |
210 | list_for_each_entry(s, &script_specs, node) |
211 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); |
212 | |
213 | fprintf(stderr, "\n"); |
214 | } |
215 | |
216 | static int parse_scriptname(const struct option *opt __used, |
217 | const char *str, int unset __used) |
218 | { |
219 | char spec[PATH_MAX]; |
220 | const char *script, *ext; |
221 | int len; |
222 | |
223 | if (strcmp(str, "lang") == 0) { |
224 | list_available_languages(); |
225 | exit(0); |
226 | } |
227 | |
228 | script = strchr(str, ':'); |
229 | if (script) { |
230 | len = script - str; |
231 | if (len >= PATH_MAX) { |
232 | fprintf(stderr, "invalid language specifier"); |
233 | return -1; |
234 | } |
235 | strncpy(spec, str, len); |
236 | spec[len] = '\0'; |
237 | scripting_ops = script_spec__lookup(spec); |
238 | if (!scripting_ops) { |
239 | fprintf(stderr, "invalid language specifier"); |
240 | return -1; |
241 | } |
242 | script++; |
243 | } else { |
244 | script = str; |
245 | ext = strchr(script, '.'); |
246 | if (!ext) { |
247 | fprintf(stderr, "invalid script extension"); |
248 | return -1; |
249 | } |
250 | scripting_ops = script_spec__lookup(++ext); |
251 | if (!scripting_ops) { |
252 | fprintf(stderr, "invalid script extension"); |
253 | return -1; |
254 | } |
255 | } |
256 | |
257 | script_name = strdup(script); |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ |
263 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ |
264 | lang_next) \ |
265 | if (lang_dirent.d_type == DT_DIR && \ |
266 | (strcmp(lang_dirent.d_name, ".")) && \ |
267 | (strcmp(lang_dirent.d_name, ".."))) |
268 | |
269 | #define for_each_script(lang_dir, script_dirent, script_next) \ |
270 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ |
271 | script_next) \ |
272 | if (script_dirent.d_type != DT_DIR) |
273 | |
274 | |
275 | #define RECORD_SUFFIX "-record" |
276 | #define REPORT_SUFFIX "-report" |
277 | |
278 | struct script_desc { |
279 | struct list_head node; |
280 | char *name; |
281 | char *half_liner; |
282 | char *args; |
283 | }; |
284 | |
285 | LIST_HEAD(script_descs); |
286 | |
287 | static struct script_desc *script_desc__new(const char *name) |
288 | { |
289 | struct script_desc *s = zalloc(sizeof(*s)); |
290 | |
291 | if (s != NULL) |
292 | s->name = strdup(name); |
293 | |
294 | return s; |
295 | } |
296 | |
297 | static void script_desc__delete(struct script_desc *s) |
298 | { |
299 | free(s->name); |
300 | free(s); |
301 | } |
302 | |
303 | static void script_desc__add(struct script_desc *s) |
304 | { |
305 | list_add_tail(&s->node, &script_descs); |
306 | } |
307 | |
308 | static struct script_desc *script_desc__find(const char *name) |
309 | { |
310 | struct script_desc *s; |
311 | |
312 | list_for_each_entry(s, &script_descs, node) |
313 | if (strcasecmp(s->name, name) == 0) |
314 | return s; |
315 | return NULL; |
316 | } |
317 | |
318 | static struct script_desc *script_desc__findnew(const char *name) |
319 | { |
320 | struct script_desc *s = script_desc__find(name); |
321 | |
322 | if (s) |
323 | return s; |
324 | |
325 | s = script_desc__new(name); |
326 | if (!s) |
327 | goto out_delete_desc; |
328 | |
329 | script_desc__add(s); |
330 | |
331 | return s; |
332 | |
333 | out_delete_desc: |
334 | script_desc__delete(s); |
335 | |
336 | return NULL; |
337 | } |
338 | |
339 | static char *ends_with(char *str, const char *suffix) |
340 | { |
341 | size_t suffix_len = strlen(suffix); |
342 | char *p = str; |
343 | |
344 | if (strlen(str) > suffix_len) { |
345 | p = str + strlen(str) - suffix_len; |
346 | if (!strncmp(p, suffix, suffix_len)) |
347 | return p; |
348 | } |
349 | |
350 | return NULL; |
351 | } |
352 | |
353 | static char *ltrim(char *str) |
354 | { |
355 | int len = strlen(str); |
356 | |
357 | while (len && isspace(*str)) { |
358 | len--; |
359 | str++; |
360 | } |
361 | |
362 | return str; |
363 | } |
364 | |
365 | static int read_script_info(struct script_desc *desc, const char *filename) |
366 | { |
367 | char line[BUFSIZ], *p; |
368 | FILE *fp; |
369 | |
370 | fp = fopen(filename, "r"); |
371 | if (!fp) |
372 | return -1; |
373 | |
374 | while (fgets(line, sizeof(line), fp)) { |
375 | p = ltrim(line); |
376 | if (strlen(p) == 0) |
377 | continue; |
378 | if (*p != '#') |
379 | continue; |
380 | p++; |
381 | if (strlen(p) && *p == '!') |
382 | continue; |
383 | |
384 | p = ltrim(p); |
385 | if (strlen(p) && p[strlen(p) - 1] == '\n') |
386 | p[strlen(p) - 1] = '\0'; |
387 | |
388 | if (!strncmp(p, "description:", strlen("description:"))) { |
389 | p += strlen("description:"); |
390 | desc->half_liner = strdup(ltrim(p)); |
391 | continue; |
392 | } |
393 | |
394 | if (!strncmp(p, "args:", strlen("args:"))) { |
395 | p += strlen("args:"); |
396 | desc->args = strdup(ltrim(p)); |
397 | continue; |
398 | } |
399 | } |
400 | |
401 | fclose(fp); |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static int list_available_scripts(const struct option *opt __used, |
407 | const char *s __used, int unset __used) |
408 | { |
409 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; |
410 | char scripts_path[MAXPATHLEN]; |
411 | DIR *scripts_dir, *lang_dir; |
412 | char script_path[MAXPATHLEN]; |
413 | char lang_path[MAXPATHLEN]; |
414 | struct script_desc *desc; |
415 | char first_half[BUFSIZ]; |
416 | char *script_root; |
417 | char *str; |
418 | |
419 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); |
420 | |
421 | scripts_dir = opendir(scripts_path); |
422 | if (!scripts_dir) |
423 | return -1; |
424 | |
425 | for_each_lang(scripts_dir, lang_dirent, lang_next) { |
426 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, |
427 | lang_dirent.d_name); |
428 | lang_dir = opendir(lang_path); |
429 | if (!lang_dir) |
430 | continue; |
431 | |
432 | for_each_script(lang_dir, script_dirent, script_next) { |
433 | script_root = strdup(script_dirent.d_name); |
434 | str = ends_with(script_root, REPORT_SUFFIX); |
435 | if (str) { |
436 | *str = '\0'; |
437 | desc = script_desc__findnew(script_root); |
438 | snprintf(script_path, MAXPATHLEN, "%s/%s", |
439 | lang_path, script_dirent.d_name); |
440 | read_script_info(desc, script_path); |
441 | } |
442 | free(script_root); |
443 | } |
444 | } |
445 | |
446 | fprintf(stdout, "List of available trace scripts:\n"); |
447 | list_for_each_entry(desc, &script_descs, node) { |
448 | sprintf(first_half, "%s %s", desc->name, |
449 | desc->args ? desc->args : ""); |
450 | fprintf(stdout, " %-36s %s\n", first_half, |
451 | desc->half_liner ? desc->half_liner : ""); |
452 | } |
453 | |
454 | exit(0); |
455 | } |
456 | |
457 | static char *get_script_path(const char *script_root, const char *suffix) |
458 | { |
459 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; |
460 | char scripts_path[MAXPATHLEN]; |
461 | char script_path[MAXPATHLEN]; |
462 | DIR *scripts_dir, *lang_dir; |
463 | char lang_path[MAXPATHLEN]; |
464 | char *str, *__script_root; |
465 | char *path = NULL; |
466 | |
467 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); |
468 | |
469 | scripts_dir = opendir(scripts_path); |
470 | if (!scripts_dir) |
471 | return NULL; |
472 | |
473 | for_each_lang(scripts_dir, lang_dirent, lang_next) { |
474 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, |
475 | lang_dirent.d_name); |
476 | lang_dir = opendir(lang_path); |
477 | if (!lang_dir) |
478 | continue; |
479 | |
480 | for_each_script(lang_dir, script_dirent, script_next) { |
481 | __script_root = strdup(script_dirent.d_name); |
482 | str = ends_with(__script_root, suffix); |
483 | if (str) { |
484 | *str = '\0'; |
485 | if (strcmp(__script_root, script_root)) |
486 | continue; |
487 | snprintf(script_path, MAXPATHLEN, "%s/%s", |
488 | lang_path, script_dirent.d_name); |
489 | path = strdup(script_path); |
490 | free(__script_root); |
491 | break; |
492 | } |
493 | free(__script_root); |
494 | } |
495 | } |
496 | |
497 | return path; |
498 | } |
499 | |
500 | static const char * const trace_usage[] = { |
501 | "perf trace [<options>] <command>", |
502 | NULL |
503 | }; |
504 | |
505 | static const struct option options[] = { |
506 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
507 | "dump raw trace in ASCII"), |
508 | OPT_BOOLEAN('v', "verbose", &verbose, |
509 | "be more verbose (show symbol address, etc)"), |
510 | OPT_BOOLEAN('L', "Latency", &latency_format, |
511 | "show latency attributes (irqs/preemption disabled, etc)"), |
512 | OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", |
513 | list_available_scripts), |
514 | OPT_CALLBACK('s', "script", NULL, "name", |
515 | "script file name (lang:script name, script name, or *)", |
516 | parse_scriptname), |
517 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", |
518 | "generate perf-trace.xx script in specified language"), |
519 | OPT_STRING('i', "input", &input_name, "file", |
520 | "input file name"), |
521 | |
522 | OPT_END() |
523 | }; |
524 | |
525 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
526 | { |
527 | struct perf_session *session; |
528 | const char *suffix = NULL; |
529 | const char **__argv; |
530 | char *script_path; |
531 | int i, err; |
532 | |
533 | if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) { |
534 | if (argc < 3) { |
535 | fprintf(stderr, |
536 | "Please specify a record script\n"); |
537 | return -1; |
538 | } |
539 | suffix = RECORD_SUFFIX; |
540 | } |
541 | |
542 | if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) { |
543 | if (argc < 3) { |
544 | fprintf(stderr, |
545 | "Please specify a report script\n"); |
546 | return -1; |
547 | } |
548 | suffix = REPORT_SUFFIX; |
549 | } |
550 | |
551 | if (suffix) { |
552 | script_path = get_script_path(argv[2], suffix); |
553 | if (!script_path) { |
554 | fprintf(stderr, "script not found\n"); |
555 | return -1; |
556 | } |
557 | |
558 | __argv = malloc((argc + 1) * sizeof(const char *)); |
559 | __argv[0] = "/bin/sh"; |
560 | __argv[1] = script_path; |
561 | for (i = 3; i < argc; i++) |
562 | __argv[i - 1] = argv[i]; |
563 | __argv[argc - 1] = NULL; |
564 | |
565 | execvp("/bin/sh", (char **)__argv); |
566 | exit(-1); |
567 | } |
568 | |
569 | setup_scripting(); |
570 | |
571 | argc = parse_options(argc, argv, options, trace_usage, |
572 | PARSE_OPT_STOP_AT_NON_OPTION); |
573 | |
574 | if (symbol__init() < 0) |
575 | return -1; |
576 | if (!script_name) |
577 | setup_pager(); |
578 | |
579 | session = perf_session__new(input_name, O_RDONLY, 0); |
580 | if (session == NULL) |
581 | return -ENOMEM; |
582 | |
583 | if (!perf_session__has_traces(session, "record -R")) |
584 | return -EINVAL; |
585 | |
586 | if (generate_script_lang) { |
587 | struct stat perf_stat; |
588 | |
589 | int input = open(input_name, O_RDONLY); |
590 | if (input < 0) { |
591 | perror("failed to open file"); |
592 | exit(-1); |
593 | } |
594 | |
595 | err = fstat(input, &perf_stat); |
596 | if (err < 0) { |
597 | perror("failed to stat file"); |
598 | exit(-1); |
599 | } |
600 | |
601 | if (!perf_stat.st_size) { |
602 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
603 | exit(0); |
604 | } |
605 | |
606 | scripting_ops = script_spec__lookup(generate_script_lang); |
607 | if (!scripting_ops) { |
608 | fprintf(stderr, "invalid language specifier"); |
609 | return -1; |
610 | } |
611 | |
612 | err = scripting_ops->generate_script("perf-trace"); |
613 | goto out; |
614 | } |
615 | |
616 | if (script_name) { |
617 | err = scripting_ops->start_script(script_name, argc, argv); |
618 | if (err) |
619 | goto out; |
620 | } |
621 | |
622 | err = __cmd_trace(session); |
623 | |
624 | perf_session__delete(session); |
625 | cleanup_scripting(); |
626 | out: |
627 | return err; |
628 | } |
629 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9