diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-20 18:54:37 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-20 18:54:37 -0400 |
commit | 467f9957d9283be40101d7255d06fae7e211ff4c (patch) | |
tree | 71d155ab52b3a78bc88d0c8088b09b3c37f9357a /tools/perf/util/parse-events.c | |
parent | 78f28b7c555359c67c2a0d23f7436e915329421e (diff) | |
parent | cdf8073d6b2c6c5a3cd6ce0e6c1297157f7f99ba (diff) |
Merge branch 'perfcounters-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perfcounters-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (58 commits)
perf_counter: Fix perf_copy_attr() pointer arithmetic
perf utils: Use a define for the maximum length of a trace event
perf: Add timechart help text and add timechart to "perf help"
tracing, x86, cpuidle: Move the end point of a C state in the power tracer
perf utils: Be consistent about minimum text size in the svghelper
perf timechart: Add "perf timechart record"
perf: Add the timechart tool
perf: Add a SVG helper library file
tracing, perf: Convert the power tracer into an event tracer
perf: Add a sample_event type to the event_union
perf: Allow perf utilities to have "callback" options without arguments
perf: Store trace event name/id pairs in perf.data
perf: Add a timestamp to fork events
sched_clock: Make it NMI safe
perf_counter: Fix up swcounter throttling
x86, perf_counter, bts: Optimize BTS overflow handling
perf sched: Add --input=file option to builtin-sched.c
perf trace: Sample timestamp and cpu when using record flag
perf tools: Increase MAX_EVENT_LENGTH
perf tools: Fix memory leak in read_ftrace_printk()
...
Diffstat (limited to 'tools/perf/util/parse-events.c')
-rw-r--r-- | tools/perf/util/parse-events.c | 237 |
1 files changed, 185 insertions, 52 deletions
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index a587d41ae3c9..89172fd0038b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "exec_cmd.h" | 6 | #include "exec_cmd.h" |
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "cache.h" | 8 | #include "cache.h" |
9 | #include "header.h" | ||
9 | 10 | ||
10 | int nr_counters; | 11 | int nr_counters; |
11 | 12 | ||
@@ -18,6 +19,12 @@ struct event_symbol { | |||
18 | const char *alias; | 19 | const char *alias; |
19 | }; | 20 | }; |
20 | 21 | ||
22 | enum event_result { | ||
23 | EVT_FAILED, | ||
24 | EVT_HANDLED, | ||
25 | EVT_HANDLED_ALL | ||
26 | }; | ||
27 | |||
21 | char debugfs_path[MAXPATHLEN]; | 28 | char debugfs_path[MAXPATHLEN]; |
22 | 29 | ||
23 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 30 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
@@ -139,7 +146,7 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
139 | (strcmp(evt_dirent.d_name, "..")) && \ | 146 | (strcmp(evt_dirent.d_name, "..")) && \ |
140 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) | 147 | (!tp_event_has_id(&sys_dirent, &evt_dirent))) |
141 | 148 | ||
142 | #define MAX_EVENT_LENGTH 30 | 149 | #define MAX_EVENT_LENGTH 512 |
143 | 150 | ||
144 | int valid_debugfs_mount(const char *debugfs) | 151 | int valid_debugfs_mount(const char *debugfs) |
145 | { | 152 | { |
@@ -344,7 +351,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int | |||
344 | return -1; | 351 | return -1; |
345 | } | 352 | } |
346 | 353 | ||
347 | static int | 354 | static enum event_result |
348 | parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | 355 | parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) |
349 | { | 356 | { |
350 | const char *s = *str; | 357 | const char *s = *str; |
@@ -356,7 +363,7 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
356 | * then bail out: | 363 | * then bail out: |
357 | */ | 364 | */ |
358 | if (cache_type == -1) | 365 | if (cache_type == -1) |
359 | return 0; | 366 | return EVT_FAILED; |
360 | 367 | ||
361 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | 368 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { |
362 | ++s; | 369 | ++s; |
@@ -402,27 +409,115 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
402 | attr->type = PERF_TYPE_HW_CACHE; | 409 | attr->type = PERF_TYPE_HW_CACHE; |
403 | 410 | ||
404 | *str = s; | 411 | *str = s; |
405 | return 1; | 412 | return EVT_HANDLED; |
413 | } | ||
414 | |||
415 | static enum event_result | ||
416 | parse_single_tracepoint_event(char *sys_name, | ||
417 | const char *evt_name, | ||
418 | unsigned int evt_length, | ||
419 | char *flags, | ||
420 | struct perf_counter_attr *attr, | ||
421 | const char **strp) | ||
422 | { | ||
423 | char evt_path[MAXPATHLEN]; | ||
424 | char id_buf[4]; | ||
425 | u64 id; | ||
426 | int fd; | ||
427 | |||
428 | if (flags) { | ||
429 | if (!strncmp(flags, "record", strlen(flags))) { | ||
430 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
431 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
432 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
437 | sys_name, evt_name); | ||
438 | |||
439 | fd = open(evt_path, O_RDONLY); | ||
440 | if (fd < 0) | ||
441 | return EVT_FAILED; | ||
442 | |||
443 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
444 | close(fd); | ||
445 | return EVT_FAILED; | ||
446 | } | ||
447 | |||
448 | close(fd); | ||
449 | id = atoll(id_buf); | ||
450 | attr->config = id; | ||
451 | attr->type = PERF_TYPE_TRACEPOINT; | ||
452 | *strp = evt_name + evt_length; | ||
453 | |||
454 | return EVT_HANDLED; | ||
455 | } | ||
456 | |||
457 | /* sys + ':' + event + ':' + flags*/ | ||
458 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | ||
459 | static enum event_result | ||
460 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | ||
461 | { | ||
462 | char evt_path[MAXPATHLEN]; | ||
463 | struct dirent *evt_ent; | ||
464 | DIR *evt_dir; | ||
465 | |||
466 | snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name); | ||
467 | evt_dir = opendir(evt_path); | ||
468 | |||
469 | if (!evt_dir) { | ||
470 | perror("Can't open event dir"); | ||
471 | return EVT_FAILED; | ||
472 | } | ||
473 | |||
474 | while ((evt_ent = readdir(evt_dir))) { | ||
475 | char event_opt[MAX_EVOPT_LEN + 1]; | ||
476 | int len; | ||
477 | unsigned int rem = MAX_EVOPT_LEN; | ||
478 | |||
479 | if (!strcmp(evt_ent->d_name, ".") | ||
480 | || !strcmp(evt_ent->d_name, "..") | ||
481 | || !strcmp(evt_ent->d_name, "enable") | ||
482 | || !strcmp(evt_ent->d_name, "filter")) | ||
483 | continue; | ||
484 | |||
485 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | ||
486 | evt_ent->d_name); | ||
487 | if (len < 0) | ||
488 | return EVT_FAILED; | ||
489 | |||
490 | rem -= len; | ||
491 | if (flags) { | ||
492 | if (rem < strlen(flags) + 1) | ||
493 | return EVT_FAILED; | ||
494 | |||
495 | strcat(event_opt, ":"); | ||
496 | strcat(event_opt, flags); | ||
497 | } | ||
498 | |||
499 | if (parse_events(NULL, event_opt, 0)) | ||
500 | return EVT_FAILED; | ||
501 | } | ||
502 | |||
503 | return EVT_HANDLED_ALL; | ||
406 | } | 504 | } |
407 | 505 | ||
408 | static int parse_tracepoint_event(const char **strp, | 506 | |
507 | static enum event_result parse_tracepoint_event(const char **strp, | ||
409 | struct perf_counter_attr *attr) | 508 | struct perf_counter_attr *attr) |
410 | { | 509 | { |
411 | const char *evt_name; | 510 | const char *evt_name; |
412 | char *flags; | 511 | char *flags; |
413 | char sys_name[MAX_EVENT_LENGTH]; | 512 | char sys_name[MAX_EVENT_LENGTH]; |
414 | char id_buf[4]; | ||
415 | int fd; | ||
416 | unsigned int sys_length, evt_length; | 513 | unsigned int sys_length, evt_length; |
417 | u64 id; | ||
418 | char evt_path[MAXPATHLEN]; | ||
419 | 514 | ||
420 | if (valid_debugfs_mount(debugfs_path)) | 515 | if (valid_debugfs_mount(debugfs_path)) |
421 | return 0; | 516 | return 0; |
422 | 517 | ||
423 | evt_name = strchr(*strp, ':'); | 518 | evt_name = strchr(*strp, ':'); |
424 | if (!evt_name) | 519 | if (!evt_name) |
425 | return 0; | 520 | return EVT_FAILED; |
426 | 521 | ||
427 | sys_length = evt_name - *strp; | 522 | sys_length = evt_name - *strp; |
428 | if (sys_length >= MAX_EVENT_LENGTH) | 523 | if (sys_length >= MAX_EVENT_LENGTH) |
@@ -434,32 +529,22 @@ static int parse_tracepoint_event(const char **strp, | |||
434 | 529 | ||
435 | flags = strchr(evt_name, ':'); | 530 | flags = strchr(evt_name, ':'); |
436 | if (flags) { | 531 | if (flags) { |
437 | *flags = '\0'; | 532 | /* split it out: */ |
533 | evt_name = strndup(evt_name, flags - evt_name); | ||
438 | flags++; | 534 | flags++; |
439 | if (!strncmp(flags, "record", strlen(flags))) | ||
440 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
441 | } | 535 | } |
442 | 536 | ||
443 | evt_length = strlen(evt_name); | 537 | evt_length = strlen(evt_name); |
444 | if (evt_length >= MAX_EVENT_LENGTH) | 538 | if (evt_length >= MAX_EVENT_LENGTH) |
445 | return 0; | 539 | return EVT_FAILED; |
446 | |||
447 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
448 | sys_name, evt_name); | ||
449 | fd = open(evt_path, O_RDONLY); | ||
450 | if (fd < 0) | ||
451 | return 0; | ||
452 | 540 | ||
453 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 541 | if (!strcmp(evt_name, "*")) { |
454 | close(fd); | 542 | *strp = evt_name + evt_length; |
455 | return 0; | 543 | return parse_subsystem_tracepoint_event(sys_name, flags); |
456 | } | 544 | } else |
457 | close(fd); | 545 | return parse_single_tracepoint_event(sys_name, evt_name, |
458 | id = atoll(id_buf); | 546 | evt_length, flags, |
459 | attr->config = id; | 547 | attr, strp); |
460 | attr->type = PERF_TYPE_TRACEPOINT; | ||
461 | *strp = evt_name + evt_length; | ||
462 | return 1; | ||
463 | } | 548 | } |
464 | 549 | ||
465 | static int check_events(const char *str, unsigned int i) | 550 | static int check_events(const char *str, unsigned int i) |
@@ -477,7 +562,7 @@ static int check_events(const char *str, unsigned int i) | |||
477 | return 0; | 562 | return 0; |
478 | } | 563 | } |
479 | 564 | ||
480 | static int | 565 | static enum event_result |
481 | parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) | 566 | parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) |
482 | { | 567 | { |
483 | const char *str = *strp; | 568 | const char *str = *strp; |
@@ -490,31 +575,32 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) | |||
490 | attr->type = event_symbols[i].type; | 575 | attr->type = event_symbols[i].type; |
491 | attr->config = event_symbols[i].config; | 576 | attr->config = event_symbols[i].config; |
492 | *strp = str + n; | 577 | *strp = str + n; |
493 | return 1; | 578 | return EVT_HANDLED; |
494 | } | 579 | } |
495 | } | 580 | } |
496 | return 0; | 581 | return EVT_FAILED; |
497 | } | 582 | } |
498 | 583 | ||
499 | static int parse_raw_event(const char **strp, struct perf_counter_attr *attr) | 584 | static enum event_result |
585 | parse_raw_event(const char **strp, struct perf_counter_attr *attr) | ||
500 | { | 586 | { |
501 | const char *str = *strp; | 587 | const char *str = *strp; |
502 | u64 config; | 588 | u64 config; |
503 | int n; | 589 | int n; |
504 | 590 | ||
505 | if (*str != 'r') | 591 | if (*str != 'r') |
506 | return 0; | 592 | return EVT_FAILED; |
507 | n = hex2u64(str + 1, &config); | 593 | n = hex2u64(str + 1, &config); |
508 | if (n > 0) { | 594 | if (n > 0) { |
509 | *strp = str + n + 1; | 595 | *strp = str + n + 1; |
510 | attr->type = PERF_TYPE_RAW; | 596 | attr->type = PERF_TYPE_RAW; |
511 | attr->config = config; | 597 | attr->config = config; |
512 | return 1; | 598 | return EVT_HANDLED; |
513 | } | 599 | } |
514 | return 0; | 600 | return EVT_FAILED; |
515 | } | 601 | } |
516 | 602 | ||
517 | static int | 603 | static enum event_result |
518 | parse_numeric_event(const char **strp, struct perf_counter_attr *attr) | 604 | parse_numeric_event(const char **strp, struct perf_counter_attr *attr) |
519 | { | 605 | { |
520 | const char *str = *strp; | 606 | const char *str = *strp; |
@@ -530,13 +616,13 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr) | |||
530 | attr->type = type; | 616 | attr->type = type; |
531 | attr->config = config; | 617 | attr->config = config; |
532 | *strp = endp; | 618 | *strp = endp; |
533 | return 1; | 619 | return EVT_HANDLED; |
534 | } | 620 | } |
535 | } | 621 | } |
536 | return 0; | 622 | return EVT_FAILED; |
537 | } | 623 | } |
538 | 624 | ||
539 | static int | 625 | static enum event_result |
540 | parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | 626 | parse_event_modifier(const char **strp, struct perf_counter_attr *attr) |
541 | { | 627 | { |
542 | const char *str = *strp; | 628 | const char *str = *strp; |
@@ -569,37 +655,84 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
569 | * Each event can have multiple symbolic names. | 655 | * Each event can have multiple symbolic names. |
570 | * Symbolic names are (almost) exactly matched. | 656 | * Symbolic names are (almost) exactly matched. |
571 | */ | 657 | */ |
572 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 658 | static enum event_result |
659 | parse_event_symbols(const char **str, struct perf_counter_attr *attr) | ||
573 | { | 660 | { |
574 | if (!(parse_tracepoint_event(str, attr) || | 661 | enum event_result ret; |
575 | parse_raw_event(str, attr) || | 662 | |
576 | parse_numeric_event(str, attr) || | 663 | ret = parse_tracepoint_event(str, attr); |
577 | parse_symbolic_event(str, attr) || | 664 | if (ret != EVT_FAILED) |
578 | parse_generic_hw_event(str, attr))) | 665 | goto modifier; |
579 | return 0; | 666 | |
667 | ret = parse_raw_event(str, attr); | ||
668 | if (ret != EVT_FAILED) | ||
669 | goto modifier; | ||
670 | |||
671 | ret = parse_numeric_event(str, attr); | ||
672 | if (ret != EVT_FAILED) | ||
673 | goto modifier; | ||
674 | |||
675 | ret = parse_symbolic_event(str, attr); | ||
676 | if (ret != EVT_FAILED) | ||
677 | goto modifier; | ||
580 | 678 | ||
679 | ret = parse_generic_hw_event(str, attr); | ||
680 | if (ret != EVT_FAILED) | ||
681 | goto modifier; | ||
682 | |||
683 | return EVT_FAILED; | ||
684 | |||
685 | modifier: | ||
581 | parse_event_modifier(str, attr); | 686 | parse_event_modifier(str, attr); |
582 | 687 | ||
583 | return 1; | 688 | return ret; |
584 | } | 689 | } |
585 | 690 | ||
691 | static void store_event_type(const char *orgname) | ||
692 | { | ||
693 | char filename[PATH_MAX], *c; | ||
694 | FILE *file; | ||
695 | int id; | ||
696 | |||
697 | sprintf(filename, "/sys/kernel/debug/tracing/events/%s/id", orgname); | ||
698 | c = strchr(filename, ':'); | ||
699 | if (c) | ||
700 | *c = '/'; | ||
701 | |||
702 | file = fopen(filename, "r"); | ||
703 | if (!file) | ||
704 | return; | ||
705 | if (fscanf(file, "%i", &id) < 1) | ||
706 | die("cannot store event ID"); | ||
707 | fclose(file); | ||
708 | perf_header__push_event(id, orgname); | ||
709 | } | ||
710 | |||
711 | |||
586 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 712 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
587 | { | 713 | { |
588 | struct perf_counter_attr attr; | 714 | struct perf_counter_attr attr; |
715 | enum event_result ret; | ||
716 | |||
717 | if (strchr(str, ':')) | ||
718 | store_event_type(str); | ||
589 | 719 | ||
590 | for (;;) { | 720 | for (;;) { |
591 | if (nr_counters == MAX_COUNTERS) | 721 | if (nr_counters == MAX_COUNTERS) |
592 | return -1; | 722 | return -1; |
593 | 723 | ||
594 | memset(&attr, 0, sizeof(attr)); | 724 | memset(&attr, 0, sizeof(attr)); |
595 | if (!parse_event_symbols(&str, &attr)) | 725 | ret = parse_event_symbols(&str, &attr); |
726 | if (ret == EVT_FAILED) | ||
596 | return -1; | 727 | return -1; |
597 | 728 | ||
598 | if (!(*str == 0 || *str == ',' || isspace(*str))) | 729 | if (!(*str == 0 || *str == ',' || isspace(*str))) |
599 | return -1; | 730 | return -1; |
600 | 731 | ||
601 | attrs[nr_counters] = attr; | 732 | if (ret != EVT_HANDLED_ALL) { |
602 | nr_counters++; | 733 | attrs[nr_counters] = attr; |
734 | nr_counters++; | ||
735 | } | ||
603 | 736 | ||
604 | if (*str == 0) | 737 | if (*str == 0) |
605 | break; | 738 | break; |