diff options
author | David S. Miller <davem@davemloft.net> | 2010-01-23 01:45:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-23 01:45:46 -0500 |
commit | 6be325719b3e54624397e413efd4b33a997e55a3 (patch) | |
tree | 57f321a56794cab2222e179b16731e0d76a4a68a /tools/perf/builtin-trace.c | |
parent | 26d92f9276a56d55511a427fb70bd70886af647a (diff) | |
parent | 92dcffb916d309aa01778bf8963a6932e4014d07 (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r-- | tools/perf/builtin-trace.c | 393 |
1 files changed, 321 insertions, 72 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index abb914aa7be6..574a215e800b 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -7,11 +7,14 @@ | |||
7 | #include "util/header.h" | 7 | #include "util/header.h" |
8 | #include "util/exec_cmd.h" | 8 | #include "util/exec_cmd.h" |
9 | #include "util/trace-event.h" | 9 | #include "util/trace-event.h" |
10 | #include "util/session.h" | ||
10 | 11 | ||
11 | static char const *script_name; | 12 | static char const *script_name; |
12 | static char const *generate_script_lang; | 13 | static char const *generate_script_lang; |
13 | 14 | ||
14 | static int default_start_script(const char *script __attribute((unused))) | 15 | static int default_start_script(const char *script __unused, |
16 | int argc __unused, | ||
17 | const char **argv __unused) | ||
15 | { | 18 | { |
16 | return 0; | 19 | return 0; |
17 | } | 20 | } |
@@ -21,7 +24,7 @@ static int default_stop_script(void) | |||
21 | return 0; | 24 | return 0; |
22 | } | 25 | } |
23 | 26 | ||
24 | static int default_generate_script(const char *outfile __attribute ((unused))) | 27 | static int default_generate_script(const char *outfile __unused) |
25 | { | 28 | { |
26 | return 0; | 29 | return 0; |
27 | } | 30 | } |
@@ -56,77 +59,53 @@ static int cleanup_scripting(void) | |||
56 | #include "util/debug.h" | 59 | #include "util/debug.h" |
57 | 60 | ||
58 | #include "util/trace-event.h" | 61 | #include "util/trace-event.h" |
59 | #include "util/data_map.h" | ||
60 | #include "util/exec_cmd.h" | 62 | #include "util/exec_cmd.h" |
61 | 63 | ||
62 | static char const *input_name = "perf.data"; | 64 | static char const *input_name = "perf.data"; |
63 | 65 | ||
64 | static struct perf_header *header; | 66 | static int process_sample_event(event_t *event, struct perf_session *session) |
65 | static u64 sample_type; | ||
66 | |||
67 | static int process_sample_event(event_t *event) | ||
68 | { | 67 | { |
69 | u64 ip = event->ip.ip; | 68 | struct sample_data data; |
70 | u64 timestamp = -1; | 69 | struct thread *thread; |
71 | u32 cpu = -1; | ||
72 | u64 period = 1; | ||
73 | void *more_data = event->ip.__more_data; | ||
74 | struct thread *thread = threads__findnew(event->ip.pid); | ||
75 | |||
76 | if (sample_type & PERF_SAMPLE_TIME) { | ||
77 | timestamp = *(u64 *)more_data; | ||
78 | more_data += sizeof(u64); | ||
79 | } | ||
80 | 70 | ||
81 | if (sample_type & PERF_SAMPLE_CPU) { | 71 | memset(&data, 0, sizeof(data)); |
82 | cpu = *(u32 *)more_data; | 72 | data.time = -1; |
83 | more_data += sizeof(u32); | 73 | data.cpu = -1; |
84 | more_data += sizeof(u32); /* reserved */ | 74 | data.period = 1; |
85 | } | ||
86 | 75 | ||
87 | if (sample_type & PERF_SAMPLE_PERIOD) { | 76 | event__parse_sample(event, session->sample_type, &data); |
88 | period = *(u64 *)more_data; | ||
89 | more_data += sizeof(u64); | ||
90 | } | ||
91 | 77 | ||
92 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 78 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
93 | event->header.misc, | 79 | event->header.misc, |
94 | event->ip.pid, event->ip.tid, | 80 | data.pid, data.tid, |
95 | (void *)(long)ip, | 81 | (void *)(long)data.ip, |
96 | (long long)period); | 82 | (long long)data.period); |
97 | 83 | ||
84 | thread = perf_session__findnew(session, event->ip.pid); | ||
98 | if (thread == NULL) { | 85 | if (thread == NULL) { |
99 | pr_debug("problem processing %d event, skipping it.\n", | 86 | pr_debug("problem processing %d event, skipping it.\n", |
100 | event->header.type); | 87 | event->header.type); |
101 | return -1; | 88 | return -1; |
102 | } | 89 | } |
103 | 90 | ||
104 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 91 | if (session->sample_type & PERF_SAMPLE_RAW) { |
105 | |||
106 | if (sample_type & PERF_SAMPLE_RAW) { | ||
107 | struct { | ||
108 | u32 size; | ||
109 | char data[0]; | ||
110 | } *raw = more_data; | ||
111 | |||
112 | /* | 92 | /* |
113 | * FIXME: better resolve from pid from the struct trace_entry | 93 | * FIXME: better resolve from pid from the struct trace_entry |
114 | * field, although it should be the same than this perf | 94 | * field, although it should be the same than this perf |
115 | * event pid | 95 | * event pid |
116 | */ | 96 | */ |
117 | scripting_ops->process_event(cpu, raw->data, raw->size, | 97 | scripting_ops->process_event(data.cpu, data.raw_data, |
118 | timestamp, thread->comm); | 98 | data.raw_size, |
99 | data.time, thread->comm); | ||
119 | } | 100 | } |
120 | event__stats.total += period; | ||
121 | 101 | ||
102 | session->events_stats.total += data.period; | ||
122 | return 0; | 103 | return 0; |
123 | } | 104 | } |
124 | 105 | ||
125 | static int sample_type_check(u64 type) | 106 | static int sample_type_check(struct perf_session *session) |
126 | { | 107 | { |
127 | sample_type = type; | 108 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { |
128 | |||
129 | if (!(sample_type & PERF_SAMPLE_RAW)) { | ||
130 | fprintf(stderr, | 109 | fprintf(stderr, |
131 | "No trace sample to read. Did you call perf record " | 110 | "No trace sample to read. Did you call perf record " |
132 | "without -R?"); | 111 | "without -R?"); |
@@ -136,19 +115,15 @@ static int sample_type_check(u64 type) | |||
136 | return 0; | 115 | return 0; |
137 | } | 116 | } |
138 | 117 | ||
139 | static struct perf_file_handler file_handler = { | 118 | static struct perf_event_ops event_ops = { |
140 | .process_sample_event = process_sample_event, | 119 | .process_sample_event = process_sample_event, |
141 | .process_comm_event = event__process_comm, | 120 | .process_comm_event = event__process_comm, |
142 | .sample_type_check = sample_type_check, | 121 | .sample_type_check = sample_type_check, |
143 | }; | 122 | }; |
144 | 123 | ||
145 | static int __cmd_trace(void) | 124 | static int __cmd_trace(struct perf_session *session) |
146 | { | 125 | { |
147 | register_idle_thread(); | 126 | return perf_session__process_events(session, &event_ops); |
148 | register_perf_file_handler(&file_handler); | ||
149 | |||
150 | return mmap_dispatch_perf_file(&header, input_name, | ||
151 | 0, 0, &event__cwdlen, &event__cwd); | ||
152 | } | 127 | } |
153 | 128 | ||
154 | struct script_spec { | 129 | struct script_spec { |
@@ -299,7 +274,245 @@ static int parse_scriptname(const struct option *opt __used, | |||
299 | return 0; | 274 | return 0; |
300 | } | 275 | } |
301 | 276 | ||
302 | static const char * const annotate_usage[] = { | 277 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ |
278 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ | ||
279 | lang_next) \ | ||
280 | if (lang_dirent.d_type == DT_DIR && \ | ||
281 | (strcmp(lang_dirent.d_name, ".")) && \ | ||
282 | (strcmp(lang_dirent.d_name, ".."))) | ||
283 | |||
284 | #define for_each_script(lang_dir, script_dirent, script_next) \ | ||
285 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ | ||
286 | script_next) \ | ||
287 | if (script_dirent.d_type != DT_DIR) | ||
288 | |||
289 | |||
290 | #define RECORD_SUFFIX "-record" | ||
291 | #define REPORT_SUFFIX "-report" | ||
292 | |||
293 | struct script_desc { | ||
294 | struct list_head node; | ||
295 | char *name; | ||
296 | char *half_liner; | ||
297 | char *args; | ||
298 | }; | ||
299 | |||
300 | LIST_HEAD(script_descs); | ||
301 | |||
302 | static struct script_desc *script_desc__new(const char *name) | ||
303 | { | ||
304 | struct script_desc *s = zalloc(sizeof(*s)); | ||
305 | |||
306 | if (s != NULL) | ||
307 | s->name = strdup(name); | ||
308 | |||
309 | return s; | ||
310 | } | ||
311 | |||
312 | static void script_desc__delete(struct script_desc *s) | ||
313 | { | ||
314 | free(s->name); | ||
315 | free(s); | ||
316 | } | ||
317 | |||
318 | static void script_desc__add(struct script_desc *s) | ||
319 | { | ||
320 | list_add_tail(&s->node, &script_descs); | ||
321 | } | ||
322 | |||
323 | static struct script_desc *script_desc__find(const char *name) | ||
324 | { | ||
325 | struct script_desc *s; | ||
326 | |||
327 | list_for_each_entry(s, &script_descs, node) | ||
328 | if (strcasecmp(s->name, name) == 0) | ||
329 | return s; | ||
330 | return NULL; | ||
331 | } | ||
332 | |||
333 | static struct script_desc *script_desc__findnew(const char *name) | ||
334 | { | ||
335 | struct script_desc *s = script_desc__find(name); | ||
336 | |||
337 | if (s) | ||
338 | return s; | ||
339 | |||
340 | s = script_desc__new(name); | ||
341 | if (!s) | ||
342 | goto out_delete_desc; | ||
343 | |||
344 | script_desc__add(s); | ||
345 | |||
346 | return s; | ||
347 | |||
348 | out_delete_desc: | ||
349 | script_desc__delete(s); | ||
350 | |||
351 | return NULL; | ||
352 | } | ||
353 | |||
354 | static char *ends_with(char *str, const char *suffix) | ||
355 | { | ||
356 | size_t suffix_len = strlen(suffix); | ||
357 | char *p = str; | ||
358 | |||
359 | if (strlen(str) > suffix_len) { | ||
360 | p = str + strlen(str) - suffix_len; | ||
361 | if (!strncmp(p, suffix, suffix_len)) | ||
362 | return p; | ||
363 | } | ||
364 | |||
365 | return NULL; | ||
366 | } | ||
367 | |||
368 | static char *ltrim(char *str) | ||
369 | { | ||
370 | int len = strlen(str); | ||
371 | |||
372 | while (len && isspace(*str)) { | ||
373 | len--; | ||
374 | str++; | ||
375 | } | ||
376 | |||
377 | return str; | ||
378 | } | ||
379 | |||
380 | static int read_script_info(struct script_desc *desc, const char *filename) | ||
381 | { | ||
382 | char line[BUFSIZ], *p; | ||
383 | FILE *fp; | ||
384 | |||
385 | fp = fopen(filename, "r"); | ||
386 | if (!fp) | ||
387 | return -1; | ||
388 | |||
389 | while (fgets(line, sizeof(line), fp)) { | ||
390 | p = ltrim(line); | ||
391 | if (strlen(p) == 0) | ||
392 | continue; | ||
393 | if (*p != '#') | ||
394 | continue; | ||
395 | p++; | ||
396 | if (strlen(p) && *p == '!') | ||
397 | continue; | ||
398 | |||
399 | p = ltrim(p); | ||
400 | if (strlen(p) && p[strlen(p) - 1] == '\n') | ||
401 | p[strlen(p) - 1] = '\0'; | ||
402 | |||
403 | if (!strncmp(p, "description:", strlen("description:"))) { | ||
404 | p += strlen("description:"); | ||
405 | desc->half_liner = strdup(ltrim(p)); | ||
406 | continue; | ||
407 | } | ||
408 | |||
409 | if (!strncmp(p, "args:", strlen("args:"))) { | ||
410 | p += strlen("args:"); | ||
411 | desc->args = strdup(ltrim(p)); | ||
412 | continue; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | fclose(fp); | ||
417 | |||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | static int list_available_scripts(const struct option *opt __used, | ||
422 | const char *s __used, int unset __used) | ||
423 | { | ||
424 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
425 | char scripts_path[MAXPATHLEN]; | ||
426 | DIR *scripts_dir, *lang_dir; | ||
427 | char script_path[MAXPATHLEN]; | ||
428 | char lang_path[MAXPATHLEN]; | ||
429 | struct script_desc *desc; | ||
430 | char first_half[BUFSIZ]; | ||
431 | char *script_root; | ||
432 | char *str; | ||
433 | |||
434 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
435 | |||
436 | scripts_dir = opendir(scripts_path); | ||
437 | if (!scripts_dir) | ||
438 | return -1; | ||
439 | |||
440 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
441 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
442 | lang_dirent.d_name); | ||
443 | lang_dir = opendir(lang_path); | ||
444 | if (!lang_dir) | ||
445 | continue; | ||
446 | |||
447 | for_each_script(lang_dir, script_dirent, script_next) { | ||
448 | script_root = strdup(script_dirent.d_name); | ||
449 | str = ends_with(script_root, REPORT_SUFFIX); | ||
450 | if (str) { | ||
451 | *str = '\0'; | ||
452 | desc = script_desc__findnew(script_root); | ||
453 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
454 | lang_path, script_dirent.d_name); | ||
455 | read_script_info(desc, script_path); | ||
456 | } | ||
457 | free(script_root); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | fprintf(stdout, "List of available trace scripts:\n"); | ||
462 | list_for_each_entry(desc, &script_descs, node) { | ||
463 | sprintf(first_half, "%s %s", desc->name, | ||
464 | desc->args ? desc->args : ""); | ||
465 | fprintf(stdout, " %-36s %s\n", first_half, | ||
466 | desc->half_liner ? desc->half_liner : ""); | ||
467 | } | ||
468 | |||
469 | exit(0); | ||
470 | } | ||
471 | |||
472 | static char *get_script_path(const char *script_root, const char *suffix) | ||
473 | { | ||
474 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
475 | char scripts_path[MAXPATHLEN]; | ||
476 | char script_path[MAXPATHLEN]; | ||
477 | DIR *scripts_dir, *lang_dir; | ||
478 | char lang_path[MAXPATHLEN]; | ||
479 | char *str, *__script_root; | ||
480 | char *path = NULL; | ||
481 | |||
482 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
483 | |||
484 | scripts_dir = opendir(scripts_path); | ||
485 | if (!scripts_dir) | ||
486 | return NULL; | ||
487 | |||
488 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
489 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
490 | lang_dirent.d_name); | ||
491 | lang_dir = opendir(lang_path); | ||
492 | if (!lang_dir) | ||
493 | continue; | ||
494 | |||
495 | for_each_script(lang_dir, script_dirent, script_next) { | ||
496 | __script_root = strdup(script_dirent.d_name); | ||
497 | str = ends_with(__script_root, suffix); | ||
498 | if (str) { | ||
499 | *str = '\0'; | ||
500 | if (strcmp(__script_root, script_root)) | ||
501 | continue; | ||
502 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
503 | lang_path, script_dirent.d_name); | ||
504 | path = strdup(script_path); | ||
505 | free(__script_root); | ||
506 | break; | ||
507 | } | ||
508 | free(__script_root); | ||
509 | } | ||
510 | } | ||
511 | |||
512 | return path; | ||
513 | } | ||
514 | |||
515 | static const char * const trace_usage[] = { | ||
303 | "perf trace [<options>] <command>", | 516 | "perf trace [<options>] <command>", |
304 | NULL | 517 | NULL |
305 | }; | 518 | }; |
@@ -309,8 +522,10 @@ static const struct option options[] = { | |||
309 | "dump raw trace in ASCII"), | 522 | "dump raw trace in ASCII"), |
310 | OPT_BOOLEAN('v', "verbose", &verbose, | 523 | OPT_BOOLEAN('v', "verbose", &verbose, |
311 | "be more verbose (show symbol address, etc)"), | 524 | "be more verbose (show symbol address, etc)"), |
312 | OPT_BOOLEAN('l', "latency", &latency_format, | 525 | OPT_BOOLEAN('L', "Latency", &latency_format, |
313 | "show latency attributes (irqs/preemption disabled, etc)"), | 526 | "show latency attributes (irqs/preemption disabled, etc)"), |
527 | OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", | ||
528 | list_available_scripts), | ||
314 | OPT_CALLBACK('s', "script", NULL, "name", | 529 | OPT_CALLBACK('s', "script", NULL, "name", |
315 | "script file name (lang:script name, script name, or *)", | 530 | "script file name (lang:script name, script name, or *)", |
316 | parse_scriptname), | 531 | parse_scriptname), |
@@ -322,24 +537,61 @@ static const struct option options[] = { | |||
322 | 537 | ||
323 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 538 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
324 | { | 539 | { |
325 | int err; | 540 | struct perf_session *session; |
541 | const char *suffix = NULL; | ||
542 | const char **__argv; | ||
543 | char *script_path; | ||
544 | int i, err; | ||
545 | |||
546 | if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) { | ||
547 | if (argc < 3) { | ||
548 | fprintf(stderr, | ||
549 | "Please specify a record script\n"); | ||
550 | return -1; | ||
551 | } | ||
552 | suffix = RECORD_SUFFIX; | ||
553 | } | ||
554 | |||
555 | if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) { | ||
556 | if (argc < 3) { | ||
557 | fprintf(stderr, | ||
558 | "Please specify a report script\n"); | ||
559 | return -1; | ||
560 | } | ||
561 | suffix = REPORT_SUFFIX; | ||
562 | } | ||
326 | 563 | ||
327 | symbol__init(0); | 564 | if (suffix) { |
565 | script_path = get_script_path(argv[2], suffix); | ||
566 | if (!script_path) { | ||
567 | fprintf(stderr, "script not found\n"); | ||
568 | return -1; | ||
569 | } | ||
328 | 570 | ||
329 | setup_scripting(); | 571 | __argv = malloc((argc + 1) * sizeof(const char *)); |
572 | __argv[0] = "/bin/sh"; | ||
573 | __argv[1] = script_path; | ||
574 | for (i = 3; i < argc; i++) | ||
575 | __argv[i - 1] = argv[i]; | ||
576 | __argv[argc - 1] = NULL; | ||
330 | 577 | ||
331 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 578 | execvp("/bin/sh", (char **)__argv); |
332 | if (argc) { | 579 | exit(-1); |
333 | /* | ||
334 | * Special case: if there's an argument left then assume tha | ||
335 | * it's a symbol filter: | ||
336 | */ | ||
337 | if (argc > 1) | ||
338 | usage_with_options(annotate_usage, options); | ||
339 | } | 580 | } |
340 | 581 | ||
582 | setup_scripting(); | ||
583 | |||
584 | argc = parse_options(argc, argv, options, trace_usage, | ||
585 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
586 | |||
587 | if (symbol__init() < 0) | ||
588 | return -1; | ||
341 | setup_pager(); | 589 | setup_pager(); |
342 | 590 | ||
591 | session = perf_session__new(input_name, O_RDONLY, 0); | ||
592 | if (session == NULL) | ||
593 | return -ENOMEM; | ||
594 | |||
343 | if (generate_script_lang) { | 595 | if (generate_script_lang) { |
344 | struct stat perf_stat; | 596 | struct stat perf_stat; |
345 | 597 | ||
@@ -366,23 +618,20 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
366 | return -1; | 618 | return -1; |
367 | } | 619 | } |
368 | 620 | ||
369 | header = perf_header__new(); | 621 | perf_header__read(&session->header, input); |
370 | if (header == NULL) | ||
371 | return -1; | ||
372 | |||
373 | perf_header__read(header, input); | ||
374 | err = scripting_ops->generate_script("perf-trace"); | 622 | err = scripting_ops->generate_script("perf-trace"); |
375 | goto out; | 623 | goto out; |
376 | } | 624 | } |
377 | 625 | ||
378 | if (script_name) { | 626 | if (script_name) { |
379 | err = scripting_ops->start_script(script_name); | 627 | err = scripting_ops->start_script(script_name, argc, argv); |
380 | if (err) | 628 | if (err) |
381 | goto out; | 629 | goto out; |
382 | } | 630 | } |
383 | 631 | ||
384 | err = __cmd_trace(); | 632 | err = __cmd_trace(session); |
385 | 633 | ||
634 | perf_session__delete(session); | ||
386 | cleanup_scripting(); | 635 | cleanup_scripting(); |
387 | out: | 636 | out: |
388 | return err; | 637 | return err; |