diff options
author | Ingo Molnar <mingo@kernel.org> | 2012-08-24 06:01:02 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2012-08-24 06:01:02 -0400 |
commit | 734e9a26d612f64e1c9cfd92256969b773954ae2 (patch) | |
tree | d6fd23e9cf7ea37d36060e877e856001d83e6c6c | |
parent | d57c5d51a30152f3175d2344cb6395f08bf8ee0c (diff) | |
parent | f63fe79fa0b1362e8ea1c1e4996cf70d14917b02 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
* Improve warning message when libunwind devel packages not present, from Jiri Olsa
* Remove perf_event_attr needless version inflation, from Jiri Olsa
* Introduce libtraceevent strerror like error reporting facility, from Namhyung Kim
* Add pmu mappings to perf.data header and use event names from cmd line, from Robert Richter
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | include/linux/perf_event.h | 4 | ||||
-rw-r--r-- | tools/lib/traceevent/event-parse.c | 104 | ||||
-rw-r--r-- | tools/lib/traceevent/event-parse.h | 34 | ||||
-rw-r--r-- | tools/lib/traceevent/event-utils.h | 6 | ||||
-rw-r--r-- | tools/perf/Makefile | 2 | ||||
-rw-r--r-- | tools/perf/util/header.c | 265 | ||||
-rw-r--r-- | tools/perf/util/header.h | 1 | ||||
-rw-r--r-- | tools/perf/util/parse-events-test.c | 37 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 12 | ||||
-rw-r--r-- | tools/perf/util/parse-events.h | 1 | ||||
-rw-r--r-- | tools/perf/util/parse-events.l | 50 | ||||
-rw-r--r-- | tools/perf/util/parse-events.y | 20 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 50 | ||||
-rw-r--r-- | tools/perf/util/pmu.h | 2 |
14 files changed, 487 insertions, 101 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 297ca3db6b4a..28f9cee3fbc3 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -205,8 +205,8 @@ enum perf_event_read_format { | |||
205 | #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ | 205 | #define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ |
206 | #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ | 206 | #define PERF_ATTR_SIZE_VER1 72 /* add: config2 */ |
207 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ | 207 | #define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */ |
208 | #define PERF_ATTR_SIZE_VER3 88 /* add: sample_regs_user */ | 208 | #define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */ |
209 | #define PERF_ATTR_SIZE_VER4 96 /* add: sample_stack_user */ | 209 | /* add: sample_stack_user */ |
210 | 210 | ||
211 | /* | 211 | /* |
212 | * Hardware event_id to monitor via a performance monitoring event: | 212 | * Hardware event_id to monitor via a performance monitoring event: |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index b7c2c491f61e..b5b4d806ffa2 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -4686,9 +4686,8 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event) | |||
4686 | * | 4686 | * |
4687 | * /sys/kernel/debug/tracing/events/.../.../format | 4687 | * /sys/kernel/debug/tracing/events/.../.../format |
4688 | */ | 4688 | */ |
4689 | int pevent_parse_event(struct pevent *pevent, | 4689 | enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, |
4690 | const char *buf, unsigned long size, | 4690 | unsigned long size, const char *sys) |
4691 | const char *sys) | ||
4692 | { | 4691 | { |
4693 | struct event_format *event; | 4692 | struct event_format *event; |
4694 | int ret; | 4693 | int ret; |
@@ -4697,17 +4696,16 @@ int pevent_parse_event(struct pevent *pevent, | |||
4697 | 4696 | ||
4698 | event = alloc_event(); | 4697 | event = alloc_event(); |
4699 | if (!event) | 4698 | if (!event) |
4700 | return -ENOMEM; | 4699 | return PEVENT_ERRNO__MEM_ALLOC_FAILED; |
4701 | 4700 | ||
4702 | event->name = event_read_name(); | 4701 | event->name = event_read_name(); |
4703 | if (!event->name) { | 4702 | if (!event->name) { |
4704 | /* Bad event? */ | 4703 | /* Bad event? */ |
4705 | free(event); | 4704 | ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; |
4706 | return -1; | 4705 | goto event_alloc_failed; |
4707 | } | 4706 | } |
4708 | 4707 | ||
4709 | if (strcmp(sys, "ftrace") == 0) { | 4708 | if (strcmp(sys, "ftrace") == 0) { |
4710 | |||
4711 | event->flags |= EVENT_FL_ISFTRACE; | 4709 | event->flags |= EVENT_FL_ISFTRACE; |
4712 | 4710 | ||
4713 | if (strcmp(event->name, "bprint") == 0) | 4711 | if (strcmp(event->name, "bprint") == 0) |
@@ -4715,20 +4713,28 @@ int pevent_parse_event(struct pevent *pevent, | |||
4715 | } | 4713 | } |
4716 | 4714 | ||
4717 | event->id = event_read_id(); | 4715 | event->id = event_read_id(); |
4718 | if (event->id < 0) | 4716 | if (event->id < 0) { |
4719 | die("failed to read event id"); | 4717 | ret = PEVENT_ERRNO__READ_ID_FAILED; |
4718 | /* | ||
4719 | * This isn't an allocation error actually. | ||
4720 | * But as the ID is critical, just bail out. | ||
4721 | */ | ||
4722 | goto event_alloc_failed; | ||
4723 | } | ||
4720 | 4724 | ||
4721 | event->system = strdup(sys); | 4725 | event->system = strdup(sys); |
4722 | if (!event->system) | 4726 | if (!event->system) { |
4723 | die("failed to allocate system"); | 4727 | ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; |
4728 | goto event_alloc_failed; | ||
4729 | } | ||
4724 | 4730 | ||
4725 | /* Add pevent to event so that it can be referenced */ | 4731 | /* Add pevent to event so that it can be referenced */ |
4726 | event->pevent = pevent; | 4732 | event->pevent = pevent; |
4727 | 4733 | ||
4728 | ret = event_read_format(event); | 4734 | ret = event_read_format(event); |
4729 | if (ret < 0) { | 4735 | if (ret < 0) { |
4730 | do_warning("failed to read event format for %s", event->name); | 4736 | ret = PEVENT_ERRNO__READ_FORMAT_FAILED; |
4731 | goto event_failed; | 4737 | goto event_parse_failed; |
4732 | } | 4738 | } |
4733 | 4739 | ||
4734 | /* | 4740 | /* |
@@ -4740,10 +4746,9 @@ int pevent_parse_event(struct pevent *pevent, | |||
4740 | 4746 | ||
4741 | ret = event_read_print(event); | 4747 | ret = event_read_print(event); |
4742 | if (ret < 0) { | 4748 | if (ret < 0) { |
4743 | do_warning("failed to read event print fmt for %s", | ||
4744 | event->name); | ||
4745 | show_warning = 1; | 4749 | show_warning = 1; |
4746 | goto event_failed; | 4750 | ret = PEVENT_ERRNO__READ_PRINT_FAILED; |
4751 | goto event_parse_failed; | ||
4747 | } | 4752 | } |
4748 | show_warning = 1; | 4753 | show_warning = 1; |
4749 | 4754 | ||
@@ -4754,20 +4759,19 @@ int pevent_parse_event(struct pevent *pevent, | |||
4754 | struct print_arg *arg, **list; | 4759 | struct print_arg *arg, **list; |
4755 | 4760 | ||
4756 | /* old ftrace had no args */ | 4761 | /* old ftrace had no args */ |
4757 | |||
4758 | list = &event->print_fmt.args; | 4762 | list = &event->print_fmt.args; |
4759 | for (field = event->format.fields; field; field = field->next) { | 4763 | for (field = event->format.fields; field; field = field->next) { |
4760 | arg = alloc_arg(); | 4764 | arg = alloc_arg(); |
4761 | *list = arg; | ||
4762 | list = &arg->next; | ||
4763 | arg->type = PRINT_FIELD; | 4765 | arg->type = PRINT_FIELD; |
4764 | arg->field.name = strdup(field->name); | 4766 | arg->field.name = strdup(field->name); |
4765 | if (!arg->field.name) { | 4767 | if (!arg->field.name) { |
4766 | do_warning("failed to allocate field name"); | ||
4767 | event->flags |= EVENT_FL_FAILED; | 4768 | event->flags |= EVENT_FL_FAILED; |
4768 | return -1; | 4769 | free_arg(arg); |
4770 | return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; | ||
4769 | } | 4771 | } |
4770 | arg->field.field = field; | 4772 | arg->field.field = field; |
4773 | *list = arg; | ||
4774 | list = &arg->next; | ||
4771 | } | 4775 | } |
4772 | return 0; | 4776 | return 0; |
4773 | } | 4777 | } |
@@ -4778,11 +4782,65 @@ int pevent_parse_event(struct pevent *pevent, | |||
4778 | 4782 | ||
4779 | return 0; | 4783 | return 0; |
4780 | 4784 | ||
4781 | event_failed: | 4785 | event_parse_failed: |
4782 | event->flags |= EVENT_FL_FAILED; | 4786 | event->flags |= EVENT_FL_FAILED; |
4783 | /* still add it even if it failed */ | 4787 | /* still add it even if it failed */ |
4784 | add_event(pevent, event); | 4788 | add_event(pevent, event); |
4785 | return -1; | 4789 | return ret; |
4790 | |||
4791 | event_alloc_failed: | ||
4792 | free(event->system); | ||
4793 | free(event->name); | ||
4794 | free(event); | ||
4795 | return ret; | ||
4796 | } | ||
4797 | |||
4798 | #undef _PE | ||
4799 | #define _PE(code, str) str | ||
4800 | static const char * const pevent_error_str[] = { | ||
4801 | PEVENT_ERRORS | ||
4802 | }; | ||
4803 | #undef _PE | ||
4804 | |||
4805 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | ||
4806 | char *buf, size_t buflen) | ||
4807 | { | ||
4808 | int idx; | ||
4809 | const char *msg; | ||
4810 | |||
4811 | if (errnum >= 0) { | ||
4812 | msg = strerror_r(errnum, buf, buflen); | ||
4813 | if (msg != buf) { | ||
4814 | size_t len = strlen(msg); | ||
4815 | char *c = mempcpy(buf, msg, min(buflen-1, len)); | ||
4816 | *c = '\0'; | ||
4817 | } | ||
4818 | return 0; | ||
4819 | } | ||
4820 | |||
4821 | if (errnum <= __PEVENT_ERRNO__START || | ||
4822 | errnum >= __PEVENT_ERRNO__END) | ||
4823 | return -1; | ||
4824 | |||
4825 | idx = errnum - __PEVENT_ERRNO__START - 1; | ||
4826 | msg = pevent_error_str[idx]; | ||
4827 | |||
4828 | switch (errnum) { | ||
4829 | case PEVENT_ERRNO__MEM_ALLOC_FAILED: | ||
4830 | case PEVENT_ERRNO__PARSE_EVENT_FAILED: | ||
4831 | case PEVENT_ERRNO__READ_ID_FAILED: | ||
4832 | case PEVENT_ERRNO__READ_FORMAT_FAILED: | ||
4833 | case PEVENT_ERRNO__READ_PRINT_FAILED: | ||
4834 | case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: | ||
4835 | snprintf(buf, buflen, "%s", msg); | ||
4836 | break; | ||
4837 | |||
4838 | default: | ||
4839 | /* cannot reach here */ | ||
4840 | break; | ||
4841 | } | ||
4842 | |||
4843 | return 0; | ||
4786 | } | 4844 | } |
4787 | 4845 | ||
4788 | int get_field_val(struct trace_seq *s, struct format_field *field, | 4846 | int get_field_val(struct trace_seq *s, struct format_field *field, |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 5772ad8cb386..527df038a25f 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
@@ -345,6 +345,34 @@ enum pevent_flag { | |||
345 | PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ | 345 | PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ |
346 | }; | 346 | }; |
347 | 347 | ||
348 | #define PEVENT_ERRORS \ | ||
349 | _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \ | ||
350 | _PE(PARSE_EVENT_FAILED, "failed to parse event"), \ | ||
351 | _PE(READ_ID_FAILED, "failed to read event id"), \ | ||
352 | _PE(READ_FORMAT_FAILED, "failed to read event format"), \ | ||
353 | _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ | ||
354 | _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace") | ||
355 | |||
356 | #undef _PE | ||
357 | #define _PE(__code, __str) PEVENT_ERRNO__ ## __code | ||
358 | enum pevent_errno { | ||
359 | PEVENT_ERRNO__SUCCESS = 0, | ||
360 | |||
361 | /* | ||
362 | * Choose an arbitrary negative big number not to clash with standard | ||
363 | * errno since SUS requires the errno has distinct positive values. | ||
364 | * See 'Issue 6' in the link below. | ||
365 | * | ||
366 | * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html | ||
367 | */ | ||
368 | __PEVENT_ERRNO__START = -100000, | ||
369 | |||
370 | PEVENT_ERRORS, | ||
371 | |||
372 | __PEVENT_ERRNO__END, | ||
373 | }; | ||
374 | #undef _PE | ||
375 | |||
348 | struct cmdline; | 376 | struct cmdline; |
349 | struct cmdline_list; | 377 | struct cmdline_list; |
350 | struct func_map; | 378 | struct func_map; |
@@ -509,8 +537,8 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, | |||
509 | int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, | 537 | int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, |
510 | int long_size); | 538 | int long_size); |
511 | 539 | ||
512 | int pevent_parse_event(struct pevent *pevent, const char *buf, | 540 | enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, |
513 | unsigned long size, const char *sys); | 541 | unsigned long size, const char *sys); |
514 | 542 | ||
515 | void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, | 543 | void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, |
516 | const char *name, struct pevent_record *record, | 544 | const char *name, struct pevent_record *record, |
@@ -561,6 +589,8 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); | |||
561 | const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); | 589 | const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); |
562 | void pevent_event_info(struct trace_seq *s, struct event_format *event, | 590 | void pevent_event_info(struct trace_seq *s, struct event_format *event, |
563 | struct pevent_record *record); | 591 | struct pevent_record *record); |
592 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | ||
593 | char *buf, size_t buflen); | ||
564 | 594 | ||
565 | struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); | 595 | struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); |
566 | struct format_field **pevent_event_common_fields(struct event_format *event); | 596 | struct format_field **pevent_event_common_fields(struct event_format *event); |
diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h index 08296383d1e6..bc075006966e 100644 --- a/tools/lib/traceevent/event-utils.h +++ b/tools/lib/traceevent/event-utils.h | |||
@@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...); | |||
39 | void __vwarning(const char *fmt, ...); | 39 | void __vwarning(const char *fmt, ...); |
40 | void __vpr_stat(const char *fmt, ...); | 40 | void __vpr_stat(const char *fmt, ...); |
41 | 41 | ||
42 | #define min(x, y) ({ \ | ||
43 | typeof(x) _min1 = (x); \ | ||
44 | typeof(y) _min2 = (y); \ | ||
45 | (void) (&_min1 == &_min2); \ | ||
46 | _min1 < _min2 ? _min1 : _min2; }) | ||
47 | |||
42 | static inline char *strim(char *string) | 48 | static inline char *strim(char *string) |
43 | { | 49 | { |
44 | char *ret; | 50 | char *ret; |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6bd888d04b6e..722ddee61f9f 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -493,7 +493,7 @@ endif | |||
493 | 493 | ||
494 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) | 494 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) |
495 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y) | 495 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y) |
496 | msg := $(warning No libunwind found. Please install libunwind >= 0.99); | 496 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); |
497 | NO_LIBUNWIND := 1 | 497 | NO_LIBUNWIND := 1 |
498 | endif # Libunwind support | 498 | endif # Libunwind support |
499 | endif # NO_LIBUNWIND | 499 | endif # NO_LIBUNWIND |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1e5b6aa60523..9696e64c9dbd 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "symbol.h" | 20 | #include "symbol.h" |
21 | #include "debug.h" | 21 | #include "debug.h" |
22 | #include "cpumap.h" | 22 | #include "cpumap.h" |
23 | #include "pmu.h" | ||
23 | 24 | ||
24 | static bool no_buildid_cache = false; | 25 | static bool no_buildid_cache = false; |
25 | 26 | ||
@@ -1004,6 +1005,45 @@ done: | |||
1004 | } | 1005 | } |
1005 | 1006 | ||
1006 | /* | 1007 | /* |
1008 | * File format: | ||
1009 | * | ||
1010 | * struct pmu_mappings { | ||
1011 | * u32 pmu_num; | ||
1012 | * struct pmu_map { | ||
1013 | * u32 type; | ||
1014 | * char name[]; | ||
1015 | * }[pmu_num]; | ||
1016 | * }; | ||
1017 | */ | ||
1018 | |||
1019 | static int write_pmu_mappings(int fd, struct perf_header *h __used, | ||
1020 | struct perf_evlist *evlist __used) | ||
1021 | { | ||
1022 | struct perf_pmu *pmu = NULL; | ||
1023 | off_t offset = lseek(fd, 0, SEEK_CUR); | ||
1024 | __u32 pmu_num = 0; | ||
1025 | |||
1026 | /* write real pmu_num later */ | ||
1027 | do_write(fd, &pmu_num, sizeof(pmu_num)); | ||
1028 | |||
1029 | while ((pmu = perf_pmu__scan(pmu))) { | ||
1030 | if (!pmu->name) | ||
1031 | continue; | ||
1032 | pmu_num++; | ||
1033 | do_write(fd, &pmu->type, sizeof(pmu->type)); | ||
1034 | do_write_string(fd, pmu->name); | ||
1035 | } | ||
1036 | |||
1037 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { | ||
1038 | /* discard all */ | ||
1039 | lseek(fd, offset, SEEK_SET); | ||
1040 | return -1; | ||
1041 | } | ||
1042 | |||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | /* | ||
1007 | * default get_cpuid(): nothing gets recorded | 1047 | * default get_cpuid(): nothing gets recorded |
1008 | * actual implementation must be in arch/$(ARCH)/util/header.c | 1048 | * actual implementation must be in arch/$(ARCH)/util/header.c |
1009 | */ | 1049 | */ |
@@ -1148,12 +1188,29 @@ static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | |||
1148 | } | 1188 | } |
1149 | } | 1189 | } |
1150 | 1190 | ||
1151 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | 1191 | static void free_event_desc(struct perf_evsel *events) |
1192 | { | ||
1193 | struct perf_evsel *evsel; | ||
1194 | |||
1195 | if (!events) | ||
1196 | return; | ||
1197 | |||
1198 | for (evsel = events; evsel->attr.size; evsel++) { | ||
1199 | if (evsel->name) | ||
1200 | free(evsel->name); | ||
1201 | if (evsel->id) | ||
1202 | free(evsel->id); | ||
1203 | } | ||
1204 | |||
1205 | free(events); | ||
1206 | } | ||
1207 | |||
1208 | static struct perf_evsel * | ||
1209 | read_event_desc(struct perf_header *ph, int fd) | ||
1152 | { | 1210 | { |
1153 | struct perf_event_attr attr; | 1211 | struct perf_evsel *evsel, *events = NULL; |
1154 | uint64_t id; | 1212 | u64 *id; |
1155 | void *buf = NULL; | 1213 | void *buf = NULL; |
1156 | char *str; | ||
1157 | u32 nre, sz, nr, i, j; | 1214 | u32 nre, sz, nr, i, j; |
1158 | ssize_t ret; | 1215 | ssize_t ret; |
1159 | size_t msz; | 1216 | size_t msz; |
@@ -1173,18 +1230,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1173 | if (ph->needs_swap) | 1230 | if (ph->needs_swap) |
1174 | sz = bswap_32(sz); | 1231 | sz = bswap_32(sz); |
1175 | 1232 | ||
1176 | memset(&attr, 0, sizeof(attr)); | ||
1177 | |||
1178 | /* buffer to hold on file attr struct */ | 1233 | /* buffer to hold on file attr struct */ |
1179 | buf = malloc(sz); | 1234 | buf = malloc(sz); |
1180 | if (!buf) | 1235 | if (!buf) |
1181 | goto error; | 1236 | goto error; |
1182 | 1237 | ||
1183 | msz = sizeof(attr); | 1238 | /* the last event terminates with evsel->attr.size == 0: */ |
1239 | events = calloc(nre + 1, sizeof(*events)); | ||
1240 | if (!events) | ||
1241 | goto error; | ||
1242 | |||
1243 | msz = sizeof(evsel->attr); | ||
1184 | if (sz < msz) | 1244 | if (sz < msz) |
1185 | msz = sz; | 1245 | msz = sz; |
1186 | 1246 | ||
1187 | for (i = 0 ; i < nre; i++) { | 1247 | for (i = 0, evsel = events; i < nre; evsel++, i++) { |
1248 | evsel->idx = i; | ||
1188 | 1249 | ||
1189 | /* | 1250 | /* |
1190 | * must read entire on-file attr struct to | 1251 | * must read entire on-file attr struct to |
@@ -1197,7 +1258,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1197 | if (ph->needs_swap) | 1258 | if (ph->needs_swap) |
1198 | perf_event__attr_swap(buf); | 1259 | perf_event__attr_swap(buf); |
1199 | 1260 | ||
1200 | memcpy(&attr, buf, msz); | 1261 | memcpy(&evsel->attr, buf, msz); |
1201 | 1262 | ||
1202 | ret = read(fd, &nr, sizeof(nr)); | 1263 | ret = read(fd, &nr, sizeof(nr)); |
1203 | if (ret != (ssize_t)sizeof(nr)) | 1264 | if (ret != (ssize_t)sizeof(nr)) |
@@ -1206,51 +1267,82 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1206 | if (ph->needs_swap) | 1267 | if (ph->needs_swap) |
1207 | nr = bswap_32(nr); | 1268 | nr = bswap_32(nr); |
1208 | 1269 | ||
1209 | str = do_read_string(fd, ph); | 1270 | evsel->name = do_read_string(fd, ph); |
1210 | fprintf(fp, "# event : name = %s, ", str); | 1271 | |
1211 | free(str); | 1272 | if (!nr) |
1273 | continue; | ||
1274 | |||
1275 | id = calloc(nr, sizeof(*id)); | ||
1276 | if (!id) | ||
1277 | goto error; | ||
1278 | evsel->ids = nr; | ||
1279 | evsel->id = id; | ||
1280 | |||
1281 | for (j = 0 ; j < nr; j++) { | ||
1282 | ret = read(fd, id, sizeof(*id)); | ||
1283 | if (ret != (ssize_t)sizeof(*id)) | ||
1284 | goto error; | ||
1285 | if (ph->needs_swap) | ||
1286 | *id = bswap_64(*id); | ||
1287 | id++; | ||
1288 | } | ||
1289 | } | ||
1290 | out: | ||
1291 | if (buf) | ||
1292 | free(buf); | ||
1293 | return events; | ||
1294 | error: | ||
1295 | if (events) | ||
1296 | free_event_desc(events); | ||
1297 | events = NULL; | ||
1298 | goto out; | ||
1299 | } | ||
1300 | |||
1301 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
1302 | { | ||
1303 | struct perf_evsel *evsel, *events = read_event_desc(ph, fd); | ||
1304 | u32 j; | ||
1305 | u64 *id; | ||
1306 | |||
1307 | if (!events) { | ||
1308 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
1309 | return; | ||
1310 | } | ||
1311 | |||
1312 | for (evsel = events; evsel->attr.size; evsel++) { | ||
1313 | fprintf(fp, "# event : name = %s, ", evsel->name); | ||
1212 | 1314 | ||
1213 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | 1315 | fprintf(fp, "type = %d, config = 0x%"PRIx64 |
1214 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | 1316 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, |
1215 | attr.type, | 1317 | evsel->attr.type, |
1216 | (u64)attr.config, | 1318 | (u64)evsel->attr.config, |
1217 | (u64)attr.config1, | 1319 | (u64)evsel->attr.config1, |
1218 | (u64)attr.config2); | 1320 | (u64)evsel->attr.config2); |
1219 | 1321 | ||
1220 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | 1322 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", |
1221 | attr.exclude_user, | 1323 | evsel->attr.exclude_user, |
1222 | attr.exclude_kernel); | 1324 | evsel->attr.exclude_kernel); |
1223 | 1325 | ||
1224 | fprintf(fp, ", excl_host = %d, excl_guest = %d", | 1326 | fprintf(fp, ", excl_host = %d, excl_guest = %d", |
1225 | attr.exclude_host, | 1327 | evsel->attr.exclude_host, |
1226 | attr.exclude_guest); | 1328 | evsel->attr.exclude_guest); |
1227 | 1329 | ||
1228 | fprintf(fp, ", precise_ip = %d", attr.precise_ip); | 1330 | fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip); |
1229 | 1331 | ||
1230 | if (nr) | 1332 | if (evsel->ids) { |
1231 | fprintf(fp, ", id = {"); | 1333 | fprintf(fp, ", id = {"); |
1232 | 1334 | for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) { | |
1233 | for (j = 0 ; j < nr; j++) { | 1335 | if (j) |
1234 | ret = read(fd, &id, sizeof(id)); | 1336 | fputc(',', fp); |
1235 | if (ret != (ssize_t)sizeof(id)) | 1337 | fprintf(fp, " %"PRIu64, *id); |
1236 | goto error; | 1338 | } |
1237 | |||
1238 | if (ph->needs_swap) | ||
1239 | id = bswap_64(id); | ||
1240 | |||
1241 | if (j) | ||
1242 | fputc(',', fp); | ||
1243 | |||
1244 | fprintf(fp, " %"PRIu64, id); | ||
1245 | } | ||
1246 | if (nr && j == nr) | ||
1247 | fprintf(fp, " }"); | 1339 | fprintf(fp, " }"); |
1340 | } | ||
1341 | |||
1248 | fputc('\n', fp); | 1342 | fputc('\n', fp); |
1249 | } | 1343 | } |
1250 | free(buf); | 1344 | |
1251 | return; | 1345 | free_event_desc(events); |
1252 | error: | ||
1253 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
1254 | } | 1346 | } |
1255 | 1347 | ||
1256 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | 1348 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) |
@@ -1337,6 +1429,43 @@ static void print_branch_stack(struct perf_header *ph __used, int fd __used, | |||
1337 | fprintf(fp, "# contains samples with branch stack\n"); | 1429 | fprintf(fp, "# contains samples with branch stack\n"); |
1338 | } | 1430 | } |
1339 | 1431 | ||
1432 | static void print_pmu_mappings(struct perf_header *ph, int fd, FILE *fp) | ||
1433 | { | ||
1434 | const char *delimiter = "# pmu mappings: "; | ||
1435 | char *name; | ||
1436 | int ret; | ||
1437 | u32 pmu_num; | ||
1438 | u32 type; | ||
1439 | |||
1440 | ret = read(fd, &pmu_num, sizeof(pmu_num)); | ||
1441 | if (ret != sizeof(pmu_num)) | ||
1442 | goto error; | ||
1443 | |||
1444 | if (!pmu_num) { | ||
1445 | fprintf(fp, "# pmu mappings: not available\n"); | ||
1446 | return; | ||
1447 | } | ||
1448 | |||
1449 | while (pmu_num) { | ||
1450 | if (read(fd, &type, sizeof(type)) != sizeof(type)) | ||
1451 | break; | ||
1452 | name = do_read_string(fd, ph); | ||
1453 | if (!name) | ||
1454 | break; | ||
1455 | pmu_num--; | ||
1456 | fprintf(fp, "%s%s = %" PRIu32, delimiter, name, type); | ||
1457 | free(name); | ||
1458 | delimiter = ", "; | ||
1459 | } | ||
1460 | |||
1461 | fprintf(fp, "\n"); | ||
1462 | |||
1463 | if (!pmu_num) | ||
1464 | return; | ||
1465 | error: | ||
1466 | fprintf(fp, "# pmu mappings: unable to read\n"); | ||
1467 | } | ||
1468 | |||
1340 | static int __event_process_build_id(struct build_id_event *bev, | 1469 | static int __event_process_build_id(struct build_id_event *bev, |
1341 | char *filename, | 1470 | char *filename, |
1342 | struct perf_session *session) | 1471 | struct perf_session *session) |
@@ -1504,6 +1633,56 @@ static int process_build_id(struct perf_file_section *section, | |||
1504 | return 0; | 1633 | return 0; |
1505 | } | 1634 | } |
1506 | 1635 | ||
1636 | static struct perf_evsel * | ||
1637 | perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) | ||
1638 | { | ||
1639 | struct perf_evsel *evsel; | ||
1640 | |||
1641 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1642 | if (evsel->idx == idx) | ||
1643 | return evsel; | ||
1644 | } | ||
1645 | |||
1646 | return NULL; | ||
1647 | } | ||
1648 | |||
1649 | static void | ||
1650 | perf_evlist__set_event_name(struct perf_evlist *evlist, struct perf_evsel *event) | ||
1651 | { | ||
1652 | struct perf_evsel *evsel; | ||
1653 | |||
1654 | if (!event->name) | ||
1655 | return; | ||
1656 | |||
1657 | evsel = perf_evlist__find_by_index(evlist, event->idx); | ||
1658 | if (!evsel) | ||
1659 | return; | ||
1660 | |||
1661 | if (evsel->name) | ||
1662 | return; | ||
1663 | |||
1664 | evsel->name = strdup(event->name); | ||
1665 | } | ||
1666 | |||
1667 | static int | ||
1668 | process_event_desc(struct perf_file_section *section __unused, | ||
1669 | struct perf_header *header, int feat __unused, int fd, | ||
1670 | void *data __used) | ||
1671 | { | ||
1672 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1673 | struct perf_evsel *evsel, *events = read_event_desc(header, fd); | ||
1674 | |||
1675 | if (!events) | ||
1676 | return 0; | ||
1677 | |||
1678 | for (evsel = events; evsel->attr.size; evsel++) | ||
1679 | perf_evlist__set_event_name(session->evlist, evsel); | ||
1680 | |||
1681 | free_event_desc(events); | ||
1682 | |||
1683 | return 0; | ||
1684 | } | ||
1685 | |||
1507 | struct feature_ops { | 1686 | struct feature_ops { |
1508 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 1687 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1509 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 1688 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1537,11 +1716,12 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1537 | FEAT_OPA(HEADER_CPUDESC, cpudesc), | 1716 | FEAT_OPA(HEADER_CPUDESC, cpudesc), |
1538 | FEAT_OPA(HEADER_CPUID, cpuid), | 1717 | FEAT_OPA(HEADER_CPUID, cpuid), |
1539 | FEAT_OPA(HEADER_TOTAL_MEM, total_mem), | 1718 | FEAT_OPA(HEADER_TOTAL_MEM, total_mem), |
1540 | FEAT_OPA(HEADER_EVENT_DESC, event_desc), | 1719 | FEAT_OPP(HEADER_EVENT_DESC, event_desc), |
1541 | FEAT_OPA(HEADER_CMDLINE, cmdline), | 1720 | FEAT_OPA(HEADER_CMDLINE, cmdline), |
1542 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), | 1721 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), |
1543 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 1722 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1544 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 1723 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
1724 | FEAT_OPA(HEADER_PMU_MAPPINGS, pmu_mappings), | ||
1545 | }; | 1725 | }; |
1546 | 1726 | ||
1547 | struct header_print_data { | 1727 | struct header_print_data { |
@@ -1831,7 +2011,6 @@ static const int attr_file_abi_sizes[] = { | |||
1831 | [1] = PERF_ATTR_SIZE_VER1, | 2011 | [1] = PERF_ATTR_SIZE_VER1, |
1832 | [2] = PERF_ATTR_SIZE_VER2, | 2012 | [2] = PERF_ATTR_SIZE_VER2, |
1833 | [3] = PERF_ATTR_SIZE_VER3, | 2013 | [3] = PERF_ATTR_SIZE_VER3, |
1834 | [4] = PERF_ATTR_SIZE_VER4, | ||
1835 | 0, | 2014 | 0, |
1836 | }; | 2015 | }; |
1837 | 2016 | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 24962e707e5b..9d5eedceda72 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -28,6 +28,7 @@ enum { | |||
28 | HEADER_CPU_TOPOLOGY, | 28 | HEADER_CPU_TOPOLOGY, |
29 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | ||
31 | HEADER_LAST_FEATURE, | 32 | HEADER_LAST_FEATURE, |
32 | HEADER_FEAT_BITS = 256, | 33 | HEADER_FEAT_BITS = 256, |
33 | }; | 34 | }; |
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c index bf055ce1916e..bc8b65130ae0 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/util/parse-events-test.c | |||
@@ -301,12 +301,13 @@ static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) | |||
301 | { | 301 | { |
302 | struct perf_evsel *evsel = perf_evlist__first(evlist); | 302 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
303 | 303 | ||
304 | |||
304 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 305 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
305 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 306 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
306 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 307 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
307 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 308 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
308 | TEST_ASSERT_VAL("wrong name", | 309 | TEST_ASSERT_VAL("wrong name", |
309 | !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u")); | 310 | !strcmp(perf_evsel__name(evsel), "mem:0:u")); |
310 | 311 | ||
311 | return test__checkevent_breakpoint(evlist); | 312 | return test__checkevent_breakpoint(evlist); |
312 | } | 313 | } |
@@ -320,7 +321,7 @@ static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) | |||
320 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 321 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
321 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 322 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
322 | TEST_ASSERT_VAL("wrong name", | 323 | TEST_ASSERT_VAL("wrong name", |
323 | !strcmp(perf_evsel__name(evsel), "mem:0x0:x:k")); | 324 | !strcmp(perf_evsel__name(evsel), "mem:0:x:k")); |
324 | 325 | ||
325 | return test__checkevent_breakpoint_x(evlist); | 326 | return test__checkevent_breakpoint_x(evlist); |
326 | } | 327 | } |
@@ -334,7 +335,7 @@ static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) | |||
334 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | 335 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); |
335 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 336 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
336 | TEST_ASSERT_VAL("wrong name", | 337 | TEST_ASSERT_VAL("wrong name", |
337 | !strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp")); | 338 | !strcmp(perf_evsel__name(evsel), "mem:0:r:hp")); |
338 | 339 | ||
339 | return test__checkevent_breakpoint_r(evlist); | 340 | return test__checkevent_breakpoint_r(evlist); |
340 | } | 341 | } |
@@ -348,7 +349,7 @@ static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) | |||
348 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 349 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
349 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 350 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
350 | TEST_ASSERT_VAL("wrong name", | 351 | TEST_ASSERT_VAL("wrong name", |
351 | !strcmp(perf_evsel__name(evsel), "mem:0x0:w:up")); | 352 | !strcmp(perf_evsel__name(evsel), "mem:0:w:up")); |
352 | 353 | ||
353 | return test__checkevent_breakpoint_w(evlist); | 354 | return test__checkevent_breakpoint_w(evlist); |
354 | } | 355 | } |
@@ -362,7 +363,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist) | |||
362 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 363 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
363 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | 364 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); |
364 | TEST_ASSERT_VAL("wrong name", | 365 | TEST_ASSERT_VAL("wrong name", |
365 | !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp")); | 366 | !strcmp(perf_evsel__name(evsel), "mem:0:rw:kp")); |
366 | 367 | ||
367 | return test__checkevent_breakpoint_rw(evlist); | 368 | return test__checkevent_breakpoint_rw(evlist); |
368 | } | 369 | } |
@@ -437,7 +438,7 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) | |||
437 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 438 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
438 | TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); | 439 | TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); |
439 | TEST_ASSERT_VAL("wrong name", | 440 | TEST_ASSERT_VAL("wrong name", |
440 | !strcmp(perf_evsel__name(evsel), "raw 0x2:u")); | 441 | !strcmp(perf_evsel__name(evsel), "cpu/config=2/u")); |
441 | 442 | ||
442 | return 0; | 443 | return 0; |
443 | } | 444 | } |
@@ -948,19 +949,19 @@ static int test_event(struct test__event_st *e) | |||
948 | 949 | ||
949 | static int test_events(struct test__event_st *events, unsigned cnt) | 950 | static int test_events(struct test__event_st *events, unsigned cnt) |
950 | { | 951 | { |
951 | int ret = 0; | 952 | int ret1, ret2 = 0; |
952 | unsigned i; | 953 | unsigned i; |
953 | 954 | ||
954 | for (i = 0; i < cnt; i++) { | 955 | for (i = 0; i < cnt; i++) { |
955 | struct test__event_st *e = &events[i]; | 956 | struct test__event_st *e = &events[i]; |
956 | 957 | ||
957 | pr_debug("running test %d '%s'\n", i, e->name); | 958 | pr_debug("running test %d '%s'\n", i, e->name); |
958 | ret = test_event(e); | 959 | ret1 = test_event(e); |
959 | if (ret) | 960 | if (ret1) |
960 | break; | 961 | ret2 = ret1; |
961 | } | 962 | } |
962 | 963 | ||
963 | return ret; | 964 | return ret2; |
964 | } | 965 | } |
965 | 966 | ||
966 | static int test_term(struct test__term *t) | 967 | static int test_term(struct test__term *t) |
@@ -1021,13 +1022,13 @@ static int test_pmu(void) | |||
1021 | 1022 | ||
1022 | int parse_events__test(void) | 1023 | int parse_events__test(void) |
1023 | { | 1024 | { |
1024 | int ret; | 1025 | int ret1, ret2 = 0; |
1025 | 1026 | ||
1026 | #define TEST_EVENTS(tests) \ | 1027 | #define TEST_EVENTS(tests) \ |
1027 | do { \ | 1028 | do { \ |
1028 | ret = test_events(tests, ARRAY_SIZE(tests)); \ | 1029 | ret1 = test_events(tests, ARRAY_SIZE(tests)); \ |
1029 | if (ret) \ | 1030 | if (!ret2) \ |
1030 | return ret; \ | 1031 | ret2 = ret1; \ |
1031 | } while (0) | 1032 | } while (0) |
1032 | 1033 | ||
1033 | TEST_EVENTS(test__events); | 1034 | TEST_EVENTS(test__events); |
@@ -1035,5 +1036,9 @@ do { \ | |||
1035 | if (test_pmu()) | 1036 | if (test_pmu()) |
1036 | TEST_EVENTS(test__events_pmu); | 1037 | TEST_EVENTS(test__events_pmu); |
1037 | 1038 | ||
1038 | return test_terms(test__terms, ARRAY_SIZE(test__terms)); | 1039 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); |
1040 | if (!ret2) | ||
1041 | ret2 = ret1; | ||
1042 | |||
1043 | return ret2; | ||
1039 | } | 1044 | } |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 925784a930a8..b24630398b92 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -751,6 +751,18 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) | |||
751 | return 0; | 751 | return 0; |
752 | } | 752 | } |
753 | 753 | ||
754 | int parse_events_name(struct list_head *list, char *name) | ||
755 | { | ||
756 | struct perf_evsel *evsel; | ||
757 | |||
758 | list_for_each_entry(evsel, list, node) { | ||
759 | if (!evsel->name) | ||
760 | evsel->name = strdup(name); | ||
761 | } | ||
762 | |||
763 | return 0; | ||
764 | } | ||
765 | |||
754 | static int parse_events__scanner(const char *str, void *data, int start_token) | 766 | static int parse_events__scanner(const char *str, void *data, int start_token) |
755 | { | 767 | { |
756 | YY_BUFFER_STATE buffer; | 768 | YY_BUFFER_STATE buffer; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 0b9782dc3da2..c356e443448d 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -81,6 +81,7 @@ int parse_events__term_clone(struct parse_events__term **new, | |||
81 | void parse_events__free_terms(struct list_head *terms); | 81 | void parse_events__free_terms(struct list_head *terms); |
82 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); | 82 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
83 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | 83 | int parse_events__modifier_group(struct list_head *list, char *event_mod); |
84 | int parse_events_name(struct list_head *list, char *name); | ||
84 | int parse_events_add_tracepoint(struct list_head **list, int *idx, | 85 | int parse_events_add_tracepoint(struct list_head **list, int *idx, |
85 | char *sys, char *event); | 86 | char *sys, char *event); |
86 | int parse_events_add_numeric(struct list_head **list, int *idx, | 87 | int parse_events_add_numeric(struct list_head **list, int *idx, |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 2c0d006d43db..f5e28dc68270 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type) | |||
70 | %} | 70 | %} |
71 | 71 | ||
72 | %x mem | 72 | %x mem |
73 | %s config | ||
74 | %x event | ||
75 | |||
76 | group [^,{}/]*[{][^}]*[}][^,{}/]* | ||
77 | event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* | ||
78 | event [^,{}/]+ | ||
73 | 79 | ||
74 | num_dec [0-9]+ | 80 | num_dec [0-9]+ |
75 | num_hex 0x[a-fA-F0-9]+ | 81 | num_hex 0x[a-fA-F0-9]+ |
@@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3} | |||
84 | { | 90 | { |
85 | int start_token; | 91 | int start_token; |
86 | 92 | ||
87 | start_token = (int) parse_events_get_extra(yyscanner); | 93 | start_token = parse_events_get_extra(yyscanner); |
94 | |||
95 | if (start_token == PE_START_TERMS) | ||
96 | BEGIN(config); | ||
97 | else if (start_token == PE_START_EVENTS) | ||
98 | BEGIN(event); | ||
99 | |||
88 | if (start_token) { | 100 | if (start_token) { |
89 | parse_events_set_extra(NULL, yyscanner); | 101 | parse_events_set_extra(NULL, yyscanner); |
90 | return start_token; | 102 | return start_token; |
@@ -92,6 +104,26 @@ modifier_bp [rwx]{1,3} | |||
92 | } | 104 | } |
93 | %} | 105 | %} |
94 | 106 | ||
107 | <event>{ | ||
108 | |||
109 | {group} { | ||
110 | BEGIN(INITIAL); yyless(0); | ||
111 | } | ||
112 | |||
113 | {event_pmu} | | ||
114 | {event} { | ||
115 | str(yyscanner, PE_EVENT_NAME); | ||
116 | BEGIN(INITIAL); yyless(0); | ||
117 | return PE_EVENT_NAME; | ||
118 | } | ||
119 | |||
120 | . | | ||
121 | <<EOF>> { | ||
122 | BEGIN(INITIAL); yyless(0); | ||
123 | } | ||
124 | |||
125 | } | ||
126 | |||
95 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | 127 | cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } |
96 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | 128 | stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } |
97 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | 129 | stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } |
@@ -127,18 +159,16 @@ speculative-read|speculative-load | | |||
127 | refs|Reference|ops|access | | 159 | refs|Reference|ops|access | |
128 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } | 160 | misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); } |
129 | 161 | ||
130 | /* | 162 | <config>{ |
131 | * These are event config hardcoded term names to be specified | ||
132 | * within xxx/.../ syntax. So far we dont clash with other names, | ||
133 | * so we can put them here directly. In case the we have a conflict | ||
134 | * in future, this needs to go into '//' condition block. | ||
135 | */ | ||
136 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } | 163 | config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); } |
137 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } | 164 | config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); } |
138 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } | 165 | config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); } |
139 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } | 166 | name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); } |
140 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | 167 | period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } |
141 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | 168 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } |
169 | , { return ','; } | ||
170 | "/" { BEGIN(INITIAL); return '/'; } | ||
171 | } | ||
142 | 172 | ||
143 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } | 173 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } |
144 | r{num_raw_hex} { return raw(yyscanner); } | 174 | r{num_raw_hex} { return raw(yyscanner); } |
@@ -147,11 +177,11 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
147 | 177 | ||
148 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } | 178 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } |
149 | {name} { return str(yyscanner, PE_NAME); } | 179 | {name} { return str(yyscanner, PE_NAME); } |
150 | "/" { return '/'; } | 180 | "/" { BEGIN(config); return '/'; } |
151 | - { return '-'; } | 181 | - { return '-'; } |
152 | , { return ','; } | 182 | , { BEGIN(event); return ','; } |
153 | : { return ':'; } | 183 | : { return ':'; } |
154 | "{" { return '{'; } | 184 | "{" { BEGIN(event); return '{'; } |
155 | "}" { return '}'; } | 185 | "}" { return '}'; } |
156 | = { return '='; } | 186 | = { return '='; } |
157 | \n { } | 187 | \n { } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 66850f820df9..42d9a17b83b1 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -27,6 +27,7 @@ do { \ | |||
27 | 27 | ||
28 | %token PE_START_EVENTS PE_START_TERMS | 28 | %token PE_START_EVENTS PE_START_TERMS |
29 | %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM | 29 | %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM |
30 | %token PE_EVENT_NAME | ||
30 | %token PE_NAME | 31 | %token PE_NAME |
31 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP | 32 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP |
32 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | 33 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT |
@@ -42,6 +43,7 @@ do { \ | |||
42 | %type <str> PE_NAME_CACHE_OP_RESULT | 43 | %type <str> PE_NAME_CACHE_OP_RESULT |
43 | %type <str> PE_MODIFIER_EVENT | 44 | %type <str> PE_MODIFIER_EVENT |
44 | %type <str> PE_MODIFIER_BP | 45 | %type <str> PE_MODIFIER_BP |
46 | %type <str> PE_EVENT_NAME | ||
45 | %type <num> value_sym | 47 | %type <num> value_sym |
46 | %type <head> event_config | 48 | %type <head> event_config |
47 | %type <term> event_term | 49 | %type <term> event_term |
@@ -53,6 +55,8 @@ do { \ | |||
53 | %type <head> event_legacy_numeric | 55 | %type <head> event_legacy_numeric |
54 | %type <head> event_legacy_raw | 56 | %type <head> event_legacy_raw |
55 | %type <head> event_def | 57 | %type <head> event_def |
58 | %type <head> event_mod | ||
59 | %type <head> event_name | ||
56 | %type <head> event | 60 | %type <head> event |
57 | %type <head> events | 61 | %type <head> events |
58 | %type <head> group_def | 62 | %type <head> group_def |
@@ -143,8 +147,10 @@ events ',' event | |||
143 | | | 147 | | |
144 | event | 148 | event |
145 | 149 | ||
146 | event: | 150 | event: event_mod |
147 | event_def PE_MODIFIER_EVENT | 151 | |
152 | event_mod: | ||
153 | event_name PE_MODIFIER_EVENT | ||
148 | { | 154 | { |
149 | struct list_head *list = $1; | 155 | struct list_head *list = $1; |
150 | 156 | ||
@@ -157,6 +163,16 @@ event_def PE_MODIFIER_EVENT | |||
157 | $$ = list; | 163 | $$ = list; |
158 | } | 164 | } |
159 | | | 165 | | |
166 | event_name | ||
167 | |||
168 | event_name: | ||
169 | PE_EVENT_NAME event_def | ||
170 | { | ||
171 | ABORT_ON(parse_events_name($2, $1)); | ||
172 | free($1); | ||
173 | $$ = $2; | ||
174 | } | ||
175 | | | ||
160 | event_def | 176 | event_def |
161 | 177 | ||
162 | event_def: event_pmu | | 178 | event_def: event_pmu | |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 67715a42cd6d..6631d828db3d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -10,6 +10,8 @@ | |||
10 | #include "pmu.h" | 10 | #include "pmu.h" |
11 | #include "parse-events.h" | 11 | #include "parse-events.h" |
12 | 12 | ||
13 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" | ||
14 | |||
13 | int perf_pmu_parse(struct list_head *list, char *name); | 15 | int perf_pmu_parse(struct list_head *list, char *name); |
14 | extern FILE *perf_pmu_in; | 16 | extern FILE *perf_pmu_in; |
15 | 17 | ||
@@ -69,7 +71,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
69 | return -1; | 71 | return -1; |
70 | 72 | ||
71 | snprintf(path, PATH_MAX, | 73 | snprintf(path, PATH_MAX, |
72 | "%s/bus/event_source/devices/%s/format", sysfs, name); | 74 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); |
73 | 75 | ||
74 | if (stat(path, &st) < 0) | 76 | if (stat(path, &st) < 0) |
75 | return 0; /* no error if format does not exist */ | 77 | return 0; /* no error if format does not exist */ |
@@ -206,7 +208,7 @@ static int pmu_type(char *name, __u32 *type) | |||
206 | return -1; | 208 | return -1; |
207 | 209 | ||
208 | snprintf(path, PATH_MAX, | 210 | snprintf(path, PATH_MAX, |
209 | "%s/bus/event_source/devices/%s/type", sysfs, name); | 211 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); |
210 | 212 | ||
211 | if (stat(path, &st) < 0) | 213 | if (stat(path, &st) < 0) |
212 | return -1; | 214 | return -1; |
@@ -222,6 +224,35 @@ static int pmu_type(char *name, __u32 *type) | |||
222 | return ret; | 224 | return ret; |
223 | } | 225 | } |
224 | 226 | ||
227 | /* Add all pmus in sysfs to pmu list: */ | ||
228 | static void pmu_read_sysfs(void) | ||
229 | { | ||
230 | char path[PATH_MAX]; | ||
231 | const char *sysfs; | ||
232 | DIR *dir; | ||
233 | struct dirent *dent; | ||
234 | |||
235 | sysfs = sysfs_find_mountpoint(); | ||
236 | if (!sysfs) | ||
237 | return; | ||
238 | |||
239 | snprintf(path, PATH_MAX, | ||
240 | "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); | ||
241 | |||
242 | dir = opendir(path); | ||
243 | if (!dir) | ||
244 | return; | ||
245 | |||
246 | while ((dent = readdir(dir))) { | ||
247 | if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) | ||
248 | continue; | ||
249 | /* add to static LIST_HEAD(pmus): */ | ||
250 | perf_pmu__find(dent->d_name); | ||
251 | } | ||
252 | |||
253 | closedir(dir); | ||
254 | } | ||
255 | |||
225 | static struct perf_pmu *pmu_lookup(char *name) | 256 | static struct perf_pmu *pmu_lookup(char *name) |
226 | { | 257 | { |
227 | struct perf_pmu *pmu; | 258 | struct perf_pmu *pmu; |
@@ -267,6 +298,21 @@ static struct perf_pmu *pmu_find(char *name) | |||
267 | return NULL; | 298 | return NULL; |
268 | } | 299 | } |
269 | 300 | ||
301 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) | ||
302 | { | ||
303 | /* | ||
304 | * pmu iterator: If pmu is NULL, we start at the begin, | ||
305 | * otherwise return the next pmu. Returns NULL on end. | ||
306 | */ | ||
307 | if (!pmu) { | ||
308 | pmu_read_sysfs(); | ||
309 | pmu = list_prepare_entry(pmu, &pmus, list); | ||
310 | } | ||
311 | list_for_each_entry_continue(pmu, &pmus, list) | ||
312 | return pmu; | ||
313 | return NULL; | ||
314 | } | ||
315 | |||
270 | struct perf_pmu *perf_pmu__find(char *name) | 316 | struct perf_pmu *perf_pmu__find(char *name) |
271 | { | 317 | { |
272 | struct perf_pmu *pmu; | 318 | struct perf_pmu *pmu; |
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 535f2c5258ab..47f68d3cc5d1 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -46,5 +46,7 @@ int perf_pmu__new_format(struct list_head *list, char *name, | |||
46 | int config, unsigned long *bits); | 46 | int config, unsigned long *bits); |
47 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | 47 | void perf_pmu__set_format(unsigned long *bits, long from, long to); |
48 | 48 | ||
49 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | ||
50 | |||
49 | int perf_pmu__test(void); | 51 | int perf_pmu__test(void); |
50 | #endif /* __PMU_H */ | 52 | #endif /* __PMU_H */ |