diff options
Diffstat (limited to 'tools/perf/util')
27 files changed, 1815 insertions, 416 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e5a462f1d07..199f69ec656 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -28,8 +28,8 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) | |||
28 | int symbol__alloc_hist(struct symbol *sym) | 28 | int symbol__alloc_hist(struct symbol *sym) |
29 | { | 29 | { |
30 | struct annotation *notes = symbol__annotation(sym); | 30 | struct annotation *notes = symbol__annotation(sym); |
31 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + | 31 | const size_t size = sym->end - sym->start + 1; |
32 | (sym->end - sym->start) * sizeof(u64)); | 32 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); |
33 | 33 | ||
34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); | 34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); |
35 | if (notes->src == NULL) | 35 | if (notes->src == NULL) |
@@ -64,7 +64,7 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
64 | 64 | ||
65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
66 | 66 | ||
67 | if (addr >= sym->end) | 67 | if (addr > sym->end) |
68 | return 0; | 68 | return 0; |
69 | 69 | ||
70 | offset = addr - sym->start; | 70 | offset = addr - sym->start; |
@@ -408,7 +408,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
408 | if (!notes->src->lines) | 408 | if (!notes->src->lines) |
409 | return -1; | 409 | return -1; |
410 | 410 | ||
411 | start = map->unmap_ip(map, sym->start); | 411 | start = map__rip_2objdump(map, sym->start); |
412 | 412 | ||
413 | for (i = 0; i < len; i++) { | 413 | for (i = 0; i < len; i++) { |
414 | char *path = NULL; | 414 | char *path = NULL; |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index fc5e5a09d5b..8dd224df3e5 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager); | |||
45 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | #ifdef NO_GTK2_SUPPORT | ||
49 | static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager) | ||
50 | { | ||
51 | if (fallback_to_pager) | ||
52 | setup_pager(); | ||
53 | } | ||
54 | static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {} | ||
55 | #else | ||
56 | void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager); | ||
57 | void perf_gtk_exit_browser(bool wait_for_ok); | ||
58 | #endif | ||
59 | |||
48 | char *alias_lookup(const char *alias); | 60 | char *alias_lookup(const char *alias); |
49 | int split_cmdline(char *cmdline, const char ***argv); | 61 | int split_cmdline(char *cmdline, const char ***argv); |
50 | 62 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 159263d17c2..1986d8051bd 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
52 | struct perf_record_opts *opts) | 52 | struct perf_record_opts *opts) |
53 | { | 53 | { |
54 | struct perf_evsel *evsel; | 54 | struct perf_evsel *evsel, *first; |
55 | 55 | ||
56 | if (evlist->cpus->map[0] < 0) | 56 | if (evlist->cpus->map[0] < 0) |
57 | opts->no_inherit = true; | 57 | opts->no_inherit = true; |
58 | 58 | ||
59 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
60 | |||
59 | list_for_each_entry(evsel, &evlist->entries, node) { | 61 | list_for_each_entry(evsel, &evlist->entries, node) { |
60 | perf_evsel__config(evsel, opts); | 62 | perf_evsel__config(evsel, opts, first); |
61 | 63 | ||
62 | if (evlist->nr_entries > 1) | 64 | if (evlist->nr_entries > 1) |
63 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 65 | evsel->attr.sample_type |= PERF_SAMPLE_ID; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f421f7cbc0d..8c13dbcb84b 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type) | |||
34 | return size; | 34 | return size; |
35 | } | 35 | } |
36 | 36 | ||
37 | static void hists__init(struct hists *hists) | 37 | void hists__init(struct hists *hists) |
38 | { | 38 | { |
39 | memset(hists, 0, sizeof(*hists)); | 39 | memset(hists, 0, sizeof(*hists)); |
40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | 40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; |
@@ -63,7 +63,8 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | |||
63 | return evsel; | 63 | return evsel; |
64 | } | 64 | } |
65 | 65 | ||
66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | 66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, |
67 | struct perf_evsel *first) | ||
67 | { | 68 | { |
68 | struct perf_event_attr *attr = &evsel->attr; | 69 | struct perf_event_attr *attr = &evsel->attr; |
69 | int track = !evsel->idx; /* only the first counter needs these */ | 70 | int track = !evsel->idx; /* only the first counter needs these */ |
@@ -134,7 +135,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
134 | attr->mmap = track; | 135 | attr->mmap = track; |
135 | attr->comm = track; | 136 | attr->comm = track; |
136 | 137 | ||
137 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide) { | 138 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide && |
139 | (!opts->group || evsel == first)) { | ||
138 | attr->disabled = 1; | 140 | attr->disabled = 1; |
139 | attr->enable_on_exec = 1; | 141 | attr->enable_on_exec = 1; |
140 | } | 142 | } |
@@ -578,6 +580,8 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
578 | return -EFAULT; | 580 | return -EFAULT; |
579 | 581 | ||
580 | data->raw_data = (void *) pdata; | 582 | data->raw_data = (void *) pdata; |
583 | |||
584 | array = (void *)array + data->raw_size + sizeof(u32); | ||
581 | } | 585 | } |
582 | 586 | ||
583 | if (type & PERF_SAMPLE_BRANCH_STACK) { | 587 | if (type & PERF_SAMPLE_BRANCH_STACK) { |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 326b8e4d503..3d6b3e4cb66 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel); | |||
80 | void perf_evsel__delete(struct perf_evsel *evsel); | 80 | void perf_evsel__delete(struct perf_evsel *evsel); |
81 | 81 | ||
82 | void perf_evsel__config(struct perf_evsel *evsel, | 82 | void perf_evsel__config(struct perf_evsel *evsel, |
83 | struct perf_record_opts *opts); | 83 | struct perf_record_opts *opts, |
84 | struct perf_evsel *first); | ||
84 | 85 | ||
85 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 86 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
86 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 87 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
@@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel) | |||
169 | return __perf_evsel__sample_size(evsel->attr.sample_type); | 170 | return __perf_evsel__sample_size(evsel->attr.sample_type); |
170 | } | 171 | } |
171 | 172 | ||
173 | void hists__init(struct hists *hists); | ||
174 | |||
172 | #endif /* __PERF_EVSEL_H */ | 175 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/gtk/browser.c b/tools/perf/util/gtk/browser.c new file mode 100644 index 00000000000..258352a2356 --- /dev/null +++ b/tools/perf/util/gtk/browser.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include "../evlist.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../evsel.h" | ||
4 | #include "../sort.h" | ||
5 | #include "../hist.h" | ||
6 | #include "gtk.h" | ||
7 | |||
8 | #include <signal.h> | ||
9 | |||
10 | #define MAX_COLUMNS 32 | ||
11 | |||
12 | void perf_gtk_setup_browser(int argc, const char *argv[], | ||
13 | bool fallback_to_pager __used) | ||
14 | { | ||
15 | gtk_init(&argc, (char ***)&argv); | ||
16 | } | ||
17 | |||
18 | void perf_gtk_exit_browser(bool wait_for_ok __used) | ||
19 | { | ||
20 | gtk_main_quit(); | ||
21 | } | ||
22 | |||
23 | static void perf_gtk_signal(int sig) | ||
24 | { | ||
25 | psignal(sig, "perf"); | ||
26 | gtk_main_quit(); | ||
27 | } | ||
28 | |||
29 | static void perf_gtk_resize_window(GtkWidget *window) | ||
30 | { | ||
31 | GdkRectangle rect; | ||
32 | GdkScreen *screen; | ||
33 | int monitor; | ||
34 | int height; | ||
35 | int width; | ||
36 | |||
37 | screen = gtk_widget_get_screen(window); | ||
38 | |||
39 | monitor = gdk_screen_get_monitor_at_window(screen, window->window); | ||
40 | |||
41 | gdk_screen_get_monitor_geometry(screen, monitor, &rect); | ||
42 | |||
43 | width = rect.width * 3 / 4; | ||
44 | height = rect.height * 3 / 4; | ||
45 | |||
46 | gtk_window_resize(GTK_WINDOW(window), width, height); | ||
47 | } | ||
48 | |||
49 | static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists) | ||
50 | { | ||
51 | GType col_types[MAX_COLUMNS]; | ||
52 | GtkCellRenderer *renderer; | ||
53 | struct sort_entry *se; | ||
54 | GtkListStore *store; | ||
55 | struct rb_node *nd; | ||
56 | u64 total_period; | ||
57 | GtkWidget *view; | ||
58 | int col_idx; | ||
59 | int nr_cols; | ||
60 | |||
61 | nr_cols = 0; | ||
62 | |||
63 | /* The percentage column */ | ||
64 | col_types[nr_cols++] = G_TYPE_STRING; | ||
65 | |||
66 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
67 | if (se->elide) | ||
68 | continue; | ||
69 | |||
70 | col_types[nr_cols++] = G_TYPE_STRING; | ||
71 | } | ||
72 | |||
73 | store = gtk_list_store_newv(nr_cols, col_types); | ||
74 | |||
75 | view = gtk_tree_view_new(); | ||
76 | |||
77 | renderer = gtk_cell_renderer_text_new(); | ||
78 | |||
79 | col_idx = 0; | ||
80 | |||
81 | /* The percentage column */ | ||
82 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
83 | -1, "Overhead (%)", | ||
84 | renderer, "text", | ||
85 | col_idx++, NULL); | ||
86 | |||
87 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
88 | if (se->elide) | ||
89 | continue; | ||
90 | |||
91 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
92 | -1, se->se_header, | ||
93 | renderer, "text", | ||
94 | col_idx++, NULL); | ||
95 | } | ||
96 | |||
97 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
98 | |||
99 | g_object_unref(GTK_TREE_MODEL(store)); | ||
100 | |||
101 | total_period = hists->stats.total_period; | ||
102 | |||
103 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
104 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
105 | GtkTreeIter iter; | ||
106 | double percent; | ||
107 | char s[512]; | ||
108 | |||
109 | if (h->filtered) | ||
110 | continue; | ||
111 | |||
112 | gtk_list_store_append(store, &iter); | ||
113 | |||
114 | col_idx = 0; | ||
115 | |||
116 | percent = (h->period * 100.0) / total_period; | ||
117 | |||
118 | snprintf(s, ARRAY_SIZE(s), "%.2f", percent); | ||
119 | |||
120 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
121 | |||
122 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
123 | if (se->elide) | ||
124 | continue; | ||
125 | |||
126 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
127 | hists__col_len(hists, se->se_width_idx)); | ||
128 | |||
129 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | gtk_container_add(GTK_CONTAINER(window), view); | ||
134 | } | ||
135 | |||
136 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
137 | const char *help __used, | ||
138 | void (*timer) (void *arg)__used, | ||
139 | void *arg __used, int delay_secs __used) | ||
140 | { | ||
141 | struct perf_evsel *pos; | ||
142 | GtkWidget *notebook; | ||
143 | GtkWidget *window; | ||
144 | |||
145 | signal(SIGSEGV, perf_gtk_signal); | ||
146 | signal(SIGFPE, perf_gtk_signal); | ||
147 | signal(SIGINT, perf_gtk_signal); | ||
148 | signal(SIGQUIT, perf_gtk_signal); | ||
149 | signal(SIGTERM, perf_gtk_signal); | ||
150 | |||
151 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
152 | |||
153 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
154 | |||
155 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
156 | |||
157 | notebook = gtk_notebook_new(); | ||
158 | |||
159 | list_for_each_entry(pos, &evlist->entries, node) { | ||
160 | struct hists *hists = &pos->hists; | ||
161 | const char *evname = event_name(pos); | ||
162 | GtkWidget *scrolled_window; | ||
163 | GtkWidget *tab_label; | ||
164 | |||
165 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
166 | |||
167 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
168 | GTK_POLICY_AUTOMATIC, | ||
169 | GTK_POLICY_AUTOMATIC); | ||
170 | |||
171 | perf_gtk_show_hists(scrolled_window, hists); | ||
172 | |||
173 | tab_label = gtk_label_new(evname); | ||
174 | |||
175 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
176 | } | ||
177 | |||
178 | gtk_container_add(GTK_CONTAINER(window), notebook); | ||
179 | |||
180 | gtk_widget_show_all(window); | ||
181 | |||
182 | perf_gtk_resize_window(window); | ||
183 | |||
184 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
185 | |||
186 | gtk_main(); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
diff --git a/tools/perf/util/gtk/gtk.h b/tools/perf/util/gtk/gtk.h new file mode 100644 index 00000000000..75177ee0403 --- /dev/null +++ b/tools/perf/util/gtk/gtk.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_GTK_H_ | ||
2 | #define _PERF_GTK_H_ 1 | ||
3 | |||
4 | #pragma GCC diagnostic ignored "-Wstrict-prototypes" | ||
5 | #include <gtk/gtk.h> | ||
6 | #pragma GCC diagnostic error "-Wstrict-prototypes" | ||
7 | |||
8 | #endif /* _PERF_GTK_H_ */ | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index fcd9cf3ea63..4c7c2d73251 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1177,7 +1177,7 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1177 | goto error; | 1177 | goto error; |
1178 | 1178 | ||
1179 | msz = sizeof(attr); | 1179 | msz = sizeof(attr); |
1180 | if (sz < (ssize_t)msz) | 1180 | if (sz < msz) |
1181 | msz = sz; | 1181 | msz = sz; |
1182 | 1182 | ||
1183 | for (i = 0 ; i < nre; i++) { | 1183 | for (i = 0 ; i < nre; i++) { |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 3dc99a9b71f..2ec4b60aff6 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists, | |||
10 | struct hist_entry *he); | 10 | struct hist_entry *he); |
11 | static bool hists__filter_entry_by_thread(struct hists *hists, | 11 | static bool hists__filter_entry_by_thread(struct hists *hists, |
12 | struct hist_entry *he); | 12 | struct hist_entry *he); |
13 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
14 | struct hist_entry *he); | ||
13 | 15 | ||
14 | enum hist_filter { | 16 | enum hist_filter { |
15 | HIST_FILTER__DSO, | 17 | HIST_FILTER__DSO, |
16 | HIST_FILTER__THREAD, | 18 | HIST_FILTER__THREAD, |
17 | HIST_FILTER__PARENT, | 19 | HIST_FILTER__PARENT, |
20 | HIST_FILTER__SYMBOL, | ||
18 | }; | 21 | }; |
19 | 22 | ||
20 | struct callchain_param callchain_param = { | 23 | struct callchain_param callchain_param = { |
@@ -420,6 +423,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
420 | { | 423 | { |
421 | hists__filter_entry_by_dso(hists, he); | 424 | hists__filter_entry_by_dso(hists, he); |
422 | hists__filter_entry_by_thread(hists, he); | 425 | hists__filter_entry_by_thread(hists, he); |
426 | hists__filter_entry_by_symbol(hists, he); | ||
423 | } | 427 | } |
424 | 428 | ||
425 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | 429 | static void __hists__collapse_resort(struct hists *hists, bool threaded) |
@@ -603,7 +607,7 @@ static void init_rem_hits(void) | |||
603 | rem_hits.ms.sym = rem_sq_bracket; | 607 | rem_hits.ms.sym = rem_sq_bracket; |
604 | } | 608 | } |
605 | 609 | ||
606 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 610 | static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
607 | u64 total_samples, int depth, | 611 | u64 total_samples, int depth, |
608 | int depth_mask, int left_margin) | 612 | int depth_mask, int left_margin) |
609 | { | 613 | { |
@@ -611,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
611 | struct callchain_node *child; | 615 | struct callchain_node *child; |
612 | struct callchain_list *chain; | 616 | struct callchain_list *chain; |
613 | int new_depth_mask = depth_mask; | 617 | int new_depth_mask = depth_mask; |
614 | u64 new_total; | ||
615 | u64 remaining; | 618 | u64 remaining; |
616 | size_t ret = 0; | 619 | size_t ret = 0; |
617 | int i; | 620 | int i; |
618 | uint entries_printed = 0; | 621 | uint entries_printed = 0; |
619 | 622 | ||
620 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 623 | remaining = total_samples; |
621 | new_total = self->children_hit; | ||
622 | else | ||
623 | new_total = total_samples; | ||
624 | |||
625 | remaining = new_total; | ||
626 | 624 | ||
627 | node = rb_first(&self->rb_root); | 625 | node = rb_first(root); |
628 | while (node) { | 626 | while (node) { |
627 | u64 new_total; | ||
629 | u64 cumul; | 628 | u64 cumul; |
630 | 629 | ||
631 | child = rb_entry(node, struct callchain_node, rb_node); | 630 | child = rb_entry(node, struct callchain_node, rb_node); |
@@ -653,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
653 | list_for_each_entry(chain, &child->val, list) { | 652 | list_for_each_entry(chain, &child->val, list) { |
654 | ret += ipchain__fprintf_graph(fp, chain, depth, | 653 | ret += ipchain__fprintf_graph(fp, chain, depth, |
655 | new_depth_mask, i++, | 654 | new_depth_mask, i++, |
656 | new_total, | 655 | total_samples, |
657 | cumul, | 656 | cumul, |
658 | left_margin); | 657 | left_margin); |
659 | } | 658 | } |
660 | ret += __callchain__fprintf_graph(fp, child, new_total, | 659 | |
660 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
661 | new_total = child->children_hit; | ||
662 | else | ||
663 | new_total = total_samples; | ||
664 | |||
665 | ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, | ||
661 | depth + 1, | 666 | depth + 1, |
662 | new_depth_mask | (1 << depth), | 667 | new_depth_mask | (1 << depth), |
663 | left_margin); | 668 | left_margin); |
@@ -667,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
667 | } | 672 | } |
668 | 673 | ||
669 | if (callchain_param.mode == CHAIN_GRAPH_REL && | 674 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
670 | remaining && remaining != new_total) { | 675 | remaining && remaining != total_samples) { |
671 | 676 | ||
672 | if (!rem_sq_bracket) | 677 | if (!rem_sq_bracket) |
673 | return ret; | 678 | return ret; |
674 | 679 | ||
675 | new_depth_mask &= ~(1 << (depth - 1)); | 680 | new_depth_mask &= ~(1 << (depth - 1)); |
676 | |||
677 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | 681 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, |
678 | new_depth_mask, 0, new_total, | 682 | new_depth_mask, 0, total_samples, |
679 | remaining, left_margin); | 683 | remaining, left_margin); |
680 | } | 684 | } |
681 | 685 | ||
682 | return ret; | 686 | return ret; |
683 | } | 687 | } |
684 | 688 | ||
685 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 689 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
686 | u64 total_samples, int left_margin) | 690 | u64 total_samples, int left_margin) |
687 | { | 691 | { |
692 | struct callchain_node *cnode; | ||
688 | struct callchain_list *chain; | 693 | struct callchain_list *chain; |
694 | u32 entries_printed = 0; | ||
689 | bool printed = false; | 695 | bool printed = false; |
696 | struct rb_node *node; | ||
690 | int i = 0; | 697 | int i = 0; |
691 | int ret = 0; | 698 | int ret; |
692 | u32 entries_printed = 0; | ||
693 | |||
694 | list_for_each_entry(chain, &self->val, list) { | ||
695 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
696 | continue; | ||
697 | |||
698 | if (!printed) { | ||
699 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
700 | ret += fprintf(fp, "|\n"); | ||
701 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
702 | ret += fprintf(fp, "---"); | ||
703 | |||
704 | left_margin += 3; | ||
705 | printed = true; | ||
706 | } else | ||
707 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
708 | 699 | ||
709 | if (chain->ms.sym) | 700 | /* |
710 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 701 | * If have one single callchain root, don't bother printing |
711 | else | 702 | * its percentage (100 % in fractal mode and the same percentage |
712 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 703 | * than the hist in graph mode). This also avoid one level of column. |
704 | */ | ||
705 | node = rb_first(root); | ||
706 | if (node && !rb_next(node)) { | ||
707 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
708 | list_for_each_entry(chain, &cnode->val, list) { | ||
709 | /* | ||
710 | * If we sort by symbol, the first entry is the same than | ||
711 | * the symbol. No need to print it otherwise it appears as | ||
712 | * displayed twice. | ||
713 | */ | ||
714 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
715 | continue; | ||
716 | if (!printed) { | ||
717 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
718 | ret += fprintf(fp, "|\n"); | ||
719 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
720 | ret += fprintf(fp, "---"); | ||
721 | left_margin += 3; | ||
722 | printed = true; | ||
723 | } else | ||
724 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
725 | |||
726 | if (chain->ms.sym) | ||
727 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
728 | else | ||
729 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
713 | 730 | ||
714 | if (++entries_printed == callchain_param.print_limit) | 731 | if (++entries_printed == callchain_param.print_limit) |
715 | break; | 732 | break; |
733 | } | ||
734 | root = &cnode->rb_root; | ||
716 | } | 735 | } |
717 | 736 | ||
718 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | 737 | return __callchain__fprintf_graph(fp, root, total_samples, |
719 | 738 | 1, 1, left_margin); | |
720 | return ret; | ||
721 | } | 739 | } |
722 | 740 | ||
723 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | 741 | static size_t __callchain__fprintf_flat(FILE *fp, |
724 | u64 total_samples) | 742 | struct callchain_node *self, |
743 | u64 total_samples) | ||
725 | { | 744 | { |
726 | struct callchain_list *chain; | 745 | struct callchain_list *chain; |
727 | size_t ret = 0; | 746 | size_t ret = 0; |
@@ -729,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
729 | if (!self) | 748 | if (!self) |
730 | return 0; | 749 | return 0; |
731 | 750 | ||
732 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | 751 | ret += __callchain__fprintf_flat(fp, self->parent, total_samples); |
733 | 752 | ||
734 | 753 | ||
735 | list_for_each_entry(chain, &self->val, list) { | 754 | list_for_each_entry(chain, &self->val, list) { |
@@ -745,44 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
745 | return ret; | 764 | return ret; |
746 | } | 765 | } |
747 | 766 | ||
748 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | 767 | static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, |
749 | u64 total_samples, int left_margin, | 768 | u64 total_samples) |
750 | FILE *fp) | ||
751 | { | 769 | { |
752 | struct rb_node *rb_node; | ||
753 | struct callchain_node *chain; | ||
754 | size_t ret = 0; | 770 | size_t ret = 0; |
755 | u32 entries_printed = 0; | 771 | u32 entries_printed = 0; |
772 | struct rb_node *rb_node; | ||
773 | struct callchain_node *chain; | ||
756 | 774 | ||
757 | rb_node = rb_first(&he->sorted_chain); | 775 | rb_node = rb_first(self); |
758 | while (rb_node) { | 776 | while (rb_node) { |
759 | double percent; | 777 | double percent; |
760 | 778 | ||
761 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 779 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
762 | percent = chain->hit * 100.0 / total_samples; | 780 | percent = chain->hit * 100.0 / total_samples; |
763 | switch (callchain_param.mode) { | 781 | |
764 | case CHAIN_FLAT: | 782 | ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); |
765 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | 783 | ret += __callchain__fprintf_flat(fp, chain, total_samples); |
766 | percent); | ||
767 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
768 | break; | ||
769 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
770 | case CHAIN_GRAPH_REL: | ||
771 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
772 | left_margin); | ||
773 | case CHAIN_NONE: | ||
774 | default: | ||
775 | break; | ||
776 | } | ||
777 | ret += fprintf(fp, "\n"); | 784 | ret += fprintf(fp, "\n"); |
778 | if (++entries_printed == callchain_param.print_limit) | 785 | if (++entries_printed == callchain_param.print_limit) |
779 | break; | 786 | break; |
787 | |||
780 | rb_node = rb_next(rb_node); | 788 | rb_node = rb_next(rb_node); |
781 | } | 789 | } |
782 | 790 | ||
783 | return ret; | 791 | return ret; |
784 | } | 792 | } |
785 | 793 | ||
794 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | ||
795 | u64 total_samples, int left_margin, | ||
796 | FILE *fp) | ||
797 | { | ||
798 | switch (callchain_param.mode) { | ||
799 | case CHAIN_GRAPH_REL: | ||
800 | return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, | ||
801 | left_margin); | ||
802 | break; | ||
803 | case CHAIN_GRAPH_ABS: | ||
804 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | ||
805 | left_margin); | ||
806 | break; | ||
807 | case CHAIN_FLAT: | ||
808 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | ||
809 | break; | ||
810 | case CHAIN_NONE: | ||
811 | break; | ||
812 | default: | ||
813 | pr_err("Bad callchain mode\n"); | ||
814 | } | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | |||
786 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 819 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
787 | { | 820 | { |
788 | struct rb_node *next = rb_first(&hists->entries); | 821 | struct rb_node *next = rb_first(&hists->entries); |
@@ -887,9 +920,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, | |||
887 | diff = new_percent - old_percent; | 920 | diff = new_percent - old_percent; |
888 | 921 | ||
889 | if (fabs(diff) >= 0.01) | 922 | if (fabs(diff) >= 0.01) |
890 | ret += scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); | 923 | scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); |
891 | else | 924 | else |
892 | ret += scnprintf(bf, sizeof(bf), " "); | 925 | scnprintf(bf, sizeof(bf), " "); |
893 | 926 | ||
894 | if (sep) | 927 | if (sep) |
895 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | 928 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
@@ -898,9 +931,9 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, | |||
898 | 931 | ||
899 | if (show_displacement) { | 932 | if (show_displacement) { |
900 | if (displacement) | 933 | if (displacement) |
901 | ret += scnprintf(bf, sizeof(bf), "%+4ld", displacement); | 934 | scnprintf(bf, sizeof(bf), "%+4ld", displacement); |
902 | else | 935 | else |
903 | ret += scnprintf(bf, sizeof(bf), " "); | 936 | scnprintf(bf, sizeof(bf), " "); |
904 | 937 | ||
905 | if (sep) | 938 | if (sep) |
906 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); | 939 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
@@ -1247,6 +1280,37 @@ void hists__filter_by_thread(struct hists *hists) | |||
1247 | } | 1280 | } |
1248 | } | 1281 | } |
1249 | 1282 | ||
1283 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
1284 | struct hist_entry *he) | ||
1285 | { | ||
1286 | if (hists->symbol_filter_str != NULL && | ||
1287 | (!he->ms.sym || strstr(he->ms.sym->name, | ||
1288 | hists->symbol_filter_str) == NULL)) { | ||
1289 | he->filtered |= (1 << HIST_FILTER__SYMBOL); | ||
1290 | return true; | ||
1291 | } | ||
1292 | |||
1293 | return false; | ||
1294 | } | ||
1295 | |||
1296 | void hists__filter_by_symbol(struct hists *hists) | ||
1297 | { | ||
1298 | struct rb_node *nd; | ||
1299 | |||
1300 | hists->nr_entries = hists->stats.total_period = 0; | ||
1301 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | ||
1302 | hists__reset_col_len(hists); | ||
1303 | |||
1304 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1305 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1306 | |||
1307 | if (hists__filter_entry_by_symbol(hists, h)) | ||
1308 | continue; | ||
1309 | |||
1310 | hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL); | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1250 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | 1314 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
1251 | { | 1315 | { |
1252 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 1316 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 9413f3e31fe..2cae9df40e0 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -62,6 +62,7 @@ struct hists { | |||
62 | const struct thread *thread_filter; | 62 | const struct thread *thread_filter; |
63 | const struct dso *dso_filter; | 63 | const struct dso *dso_filter; |
64 | const char *uid_filter_str; | 64 | const char *uid_filter_str; |
65 | const char *symbol_filter_str; | ||
65 | pthread_mutex_t lock; | 66 | pthread_mutex_t lock; |
66 | struct events_stats stats; | 67 | struct events_stats stats; |
67 | u64 event_stream; | 68 | u64 event_stream; |
@@ -107,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize); | |||
107 | 108 | ||
108 | void hists__filter_by_dso(struct hists *hists); | 109 | void hists__filter_by_dso(struct hists *hists); |
109 | void hists__filter_by_thread(struct hists *hists); | 110 | void hists__filter_by_thread(struct hists *hists); |
111 | void hists__filter_by_symbol(struct hists *hists); | ||
110 | 112 | ||
111 | u16 hists__col_len(struct hists *self, enum hist_column col); | 113 | u16 hists__col_len(struct hists *self, enum hist_column col); |
112 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 114 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
@@ -145,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
145 | int refresh); | 147 | int refresh); |
146 | #endif | 148 | #endif |
147 | 149 | ||
150 | #ifdef NO_GTK2_SUPPORT | ||
151 | static inline | ||
152 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used, | ||
153 | const char *help __used, | ||
154 | void(*timer)(void *arg) __used, | ||
155 | void *arg __used, | ||
156 | int refresh __used) | ||
157 | { | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | #else | ||
162 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
163 | void(*timer)(void *arg), void *arg, | ||
164 | int refresh); | ||
165 | #endif | ||
166 | |||
148 | unsigned int hists__sort_list_width(struct hists *self); | 167 | unsigned int hists__sort_list_width(struct hists *self); |
149 | 168 | ||
150 | #endif /* __PERF_HIST_H */ | 169 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/export.h index b43e2dc21e0..b43e2dc21e0 100644 --- a/tools/perf/util/include/linux/module.h +++ b/tools/perf/util/include/linux/export.h | |||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c7a6f6faf91..5b3a0ef4e23 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -11,6 +11,10 @@ | |||
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include "debugfs.h" |
14 | #include "parse-events-flex.h" | ||
15 | #include "pmu.h" | ||
16 | |||
17 | #define MAX_NAME_LEN 100 | ||
14 | 18 | ||
15 | struct event_symbol { | 19 | struct event_symbol { |
16 | u8 type; | 20 | u8 type; |
@@ -19,11 +23,8 @@ struct event_symbol { | |||
19 | const char *alias; | 23 | const char *alias; |
20 | }; | 24 | }; |
21 | 25 | ||
22 | enum event_result { | 26 | int parse_events_parse(struct list_head *list, struct list_head *list_tmp, |
23 | EVT_FAILED, | 27 | int *idx); |
24 | EVT_HANDLED, | ||
25 | EVT_HANDLED_ALL | ||
26 | }; | ||
27 | 28 | ||
28 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 29 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
29 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 30 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
@@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config) | |||
354 | return "unknown"; | 355 | return "unknown"; |
355 | } | 356 | } |
356 | 357 | ||
357 | static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) | 358 | static int add_event(struct list_head *list, int *idx, |
359 | struct perf_event_attr *attr, char *name) | ||
360 | { | ||
361 | struct perf_evsel *evsel; | ||
362 | |||
363 | event_attr_init(attr); | ||
364 | |||
365 | evsel = perf_evsel__new(attr, (*idx)++); | ||
366 | if (!evsel) | ||
367 | return -ENOMEM; | ||
368 | |||
369 | list_add_tail(&evsel->node, list); | ||
370 | |||
371 | evsel->name = strdup(name); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) | ||
358 | { | 376 | { |
359 | int i, j; | 377 | int i, j; |
360 | int n, longest = -1; | 378 | int n, longest = -1; |
@@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int | |||
362 | for (i = 0; i < size; i++) { | 380 | for (i = 0; i < size; i++) { |
363 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | 381 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { |
364 | n = strlen(names[i][j]); | 382 | n = strlen(names[i][j]); |
365 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | 383 | if (n > longest && !strncasecmp(str, names[i][j], n)) |
366 | longest = n; | 384 | longest = n; |
367 | } | 385 | } |
368 | if (longest > 0) { | 386 | if (longest > 0) |
369 | *str += longest; | ||
370 | return i; | 387 | return i; |
371 | } | ||
372 | } | 388 | } |
373 | 389 | ||
374 | return -1; | 390 | return -1; |
375 | } | 391 | } |
376 | 392 | ||
377 | static enum event_result | 393 | int parse_events_add_cache(struct list_head *list, int *idx, |
378 | parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | 394 | char *type, char *op_result1, char *op_result2) |
379 | { | 395 | { |
380 | const char *s = *str; | 396 | struct perf_event_attr attr; |
397 | char name[MAX_NAME_LEN]; | ||
381 | int cache_type = -1, cache_op = -1, cache_result = -1; | 398 | int cache_type = -1, cache_op = -1, cache_result = -1; |
399 | char *op_result[2] = { op_result1, op_result2 }; | ||
400 | int i, n; | ||
382 | 401 | ||
383 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); | ||
384 | /* | 402 | /* |
385 | * No fallback - if we cannot get a clear cache type | 403 | * No fallback - if we cannot get a clear cache type |
386 | * then bail out: | 404 | * then bail out: |
387 | */ | 405 | */ |
406 | cache_type = parse_aliases(type, hw_cache, | ||
407 | PERF_COUNT_HW_CACHE_MAX); | ||
388 | if (cache_type == -1) | 408 | if (cache_type == -1) |
389 | return EVT_FAILED; | 409 | return -EINVAL; |
390 | 410 | ||
391 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | 411 | n = snprintf(name, MAX_NAME_LEN, "%s", type); |
392 | ++s; | 412 | |
413 | for (i = 0; (i < 2) && (op_result[i]); i++) { | ||
414 | char *str = op_result[i]; | ||
415 | |||
416 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); | ||
393 | 417 | ||
394 | if (cache_op == -1) { | 418 | if (cache_op == -1) { |
395 | cache_op = parse_aliases(&s, hw_cache_op, | 419 | cache_op = parse_aliases(str, hw_cache_op, |
396 | PERF_COUNT_HW_CACHE_OP_MAX); | 420 | PERF_COUNT_HW_CACHE_OP_MAX); |
397 | if (cache_op >= 0) { | 421 | if (cache_op >= 0) { |
398 | if (!is_cache_op_valid(cache_type, cache_op)) | 422 | if (!is_cache_op_valid(cache_type, cache_op)) |
399 | return EVT_FAILED; | 423 | return -EINVAL; |
400 | continue; | 424 | continue; |
401 | } | 425 | } |
402 | } | 426 | } |
403 | 427 | ||
404 | if (cache_result == -1) { | 428 | if (cache_result == -1) { |
405 | cache_result = parse_aliases(&s, hw_cache_result, | 429 | cache_result = parse_aliases(str, hw_cache_result, |
406 | PERF_COUNT_HW_CACHE_RESULT_MAX); | 430 | PERF_COUNT_HW_CACHE_RESULT_MAX); |
407 | if (cache_result >= 0) | 431 | if (cache_result >= 0) |
408 | continue; | 432 | continue; |
409 | } | 433 | } |
410 | |||
411 | /* | ||
412 | * Can't parse this as a cache op or result, so back up | ||
413 | * to the '-'. | ||
414 | */ | ||
415 | --s; | ||
416 | break; | ||
417 | } | 434 | } |
418 | 435 | ||
419 | /* | 436 | /* |
@@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
428 | if (cache_result == -1) | 445 | if (cache_result == -1) |
429 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | 446 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; |
430 | 447 | ||
431 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | 448 | memset(&attr, 0, sizeof(attr)); |
432 | attr->type = PERF_TYPE_HW_CACHE; | 449 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
433 | 450 | attr.type = PERF_TYPE_HW_CACHE; | |
434 | *str = s; | 451 | return add_event(list, idx, &attr, name); |
435 | return EVT_HANDLED; | ||
436 | } | 452 | } |
437 | 453 | ||
438 | static enum event_result | 454 | static int add_tracepoint(struct list_head *list, int *idx, |
439 | parse_single_tracepoint_event(char *sys_name, | 455 | char *sys_name, char *evt_name) |
440 | const char *evt_name, | ||
441 | unsigned int evt_length, | ||
442 | struct perf_event_attr *attr, | ||
443 | const char **strp) | ||
444 | { | 456 | { |
457 | struct perf_event_attr attr; | ||
458 | char name[MAX_NAME_LEN]; | ||
445 | char evt_path[MAXPATHLEN]; | 459 | char evt_path[MAXPATHLEN]; |
446 | char id_buf[4]; | 460 | char id_buf[4]; |
447 | u64 id; | 461 | u64 id; |
@@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name, | |||
452 | 466 | ||
453 | fd = open(evt_path, O_RDONLY); | 467 | fd = open(evt_path, O_RDONLY); |
454 | if (fd < 0) | 468 | if (fd < 0) |
455 | return EVT_FAILED; | 469 | return -1; |
456 | 470 | ||
457 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 471 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { |
458 | close(fd); | 472 | close(fd); |
459 | return EVT_FAILED; | 473 | return -1; |
460 | } | 474 | } |
461 | 475 | ||
462 | close(fd); | 476 | close(fd); |
463 | id = atoll(id_buf); | 477 | id = atoll(id_buf); |
464 | attr->config = id; | ||
465 | attr->type = PERF_TYPE_TRACEPOINT; | ||
466 | *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */ | ||
467 | |||
468 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
469 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
470 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
471 | |||
472 | attr->sample_period = 1; | ||
473 | 478 | ||
479 | memset(&attr, 0, sizeof(attr)); | ||
480 | attr.config = id; | ||
481 | attr.type = PERF_TYPE_TRACEPOINT; | ||
482 | attr.sample_type |= PERF_SAMPLE_RAW; | ||
483 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
484 | attr.sample_type |= PERF_SAMPLE_CPU; | ||
485 | attr.sample_period = 1; | ||
474 | 486 | ||
475 | return EVT_HANDLED; | 487 | snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name); |
488 | return add_event(list, idx, &attr, name); | ||
476 | } | 489 | } |
477 | 490 | ||
478 | /* sys + ':' + event + ':' + flags*/ | 491 | static int add_tracepoint_multi(struct list_head *list, int *idx, |
479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 492 | char *sys_name, char *evt_name) |
480 | static enum event_result | ||
481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, | ||
482 | const char *evt_exp, char *flags) | ||
483 | { | 493 | { |
484 | char evt_path[MAXPATHLEN]; | 494 | char evt_path[MAXPATHLEN]; |
485 | struct dirent *evt_ent; | 495 | struct dirent *evt_ent; |
486 | DIR *evt_dir; | 496 | DIR *evt_dir; |
497 | int ret = 0; | ||
487 | 498 | ||
488 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); | 499 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); |
489 | evt_dir = opendir(evt_path); | 500 | evt_dir = opendir(evt_path); |
490 | |||
491 | if (!evt_dir) { | 501 | if (!evt_dir) { |
492 | perror("Can't open event dir"); | 502 | perror("Can't open event dir"); |
493 | return EVT_FAILED; | 503 | return -1; |
494 | } | 504 | } |
495 | 505 | ||
496 | while ((evt_ent = readdir(evt_dir))) { | 506 | while (!ret && (evt_ent = readdir(evt_dir))) { |
497 | char event_opt[MAX_EVOPT_LEN + 1]; | ||
498 | int len; | ||
499 | |||
500 | if (!strcmp(evt_ent->d_name, ".") | 507 | if (!strcmp(evt_ent->d_name, ".") |
501 | || !strcmp(evt_ent->d_name, "..") | 508 | || !strcmp(evt_ent->d_name, "..") |
502 | || !strcmp(evt_ent->d_name, "enable") | 509 | || !strcmp(evt_ent->d_name, "enable") |
503 | || !strcmp(evt_ent->d_name, "filter")) | 510 | || !strcmp(evt_ent->d_name, "filter")) |
504 | continue; | 511 | continue; |
505 | 512 | ||
506 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | 513 | if (!strglobmatch(evt_ent->d_name, evt_name)) |
507 | continue; | 514 | continue; |
508 | 515 | ||
509 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 516 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
510 | evt_ent->d_name, flags ? ":" : "", | ||
511 | flags ?: ""); | ||
512 | if (len < 0) | ||
513 | return EVT_FAILED; | ||
514 | |||
515 | if (parse_events(evlist, event_opt, 0)) | ||
516 | return EVT_FAILED; | ||
517 | } | 517 | } |
518 | 518 | ||
519 | return EVT_HANDLED_ALL; | 519 | return ret; |
520 | } | 520 | } |
521 | 521 | ||
522 | static enum event_result | 522 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, | 523 | char *sys, char *event) |
524 | struct perf_event_attr *attr) | ||
525 | { | 524 | { |
526 | const char *evt_name; | 525 | int ret; |
527 | char *flags = NULL, *comma_loc; | ||
528 | char sys_name[MAX_EVENT_LENGTH]; | ||
529 | unsigned int sys_length, evt_length; | ||
530 | |||
531 | if (debugfs_valid_mountpoint(tracing_events_path)) | ||
532 | return 0; | ||
533 | |||
534 | evt_name = strchr(*strp, ':'); | ||
535 | if (!evt_name) | ||
536 | return EVT_FAILED; | ||
537 | |||
538 | sys_length = evt_name - *strp; | ||
539 | if (sys_length >= MAX_EVENT_LENGTH) | ||
540 | return 0; | ||
541 | 526 | ||
542 | strncpy(sys_name, *strp, sys_length); | 527 | ret = debugfs_valid_mountpoint(tracing_events_path); |
543 | sys_name[sys_length] = '\0'; | 528 | if (ret) |
544 | evt_name = evt_name + 1; | 529 | return ret; |
545 | 530 | ||
546 | comma_loc = strchr(evt_name, ','); | 531 | return strpbrk(event, "*?") ? |
547 | if (comma_loc) { | 532 | add_tracepoint_multi(list, idx, sys, event) : |
548 | /* take the event name up to the comma */ | 533 | add_tracepoint(list, idx, sys, event); |
549 | evt_name = strndup(evt_name, comma_loc - evt_name); | ||
550 | } | ||
551 | flags = strchr(evt_name, ':'); | ||
552 | if (flags) { | ||
553 | /* split it out: */ | ||
554 | evt_name = strndup(evt_name, flags - evt_name); | ||
555 | flags++; | ||
556 | } | ||
557 | |||
558 | evt_length = strlen(evt_name); | ||
559 | if (evt_length >= MAX_EVENT_LENGTH) | ||
560 | return EVT_FAILED; | ||
561 | if (strpbrk(evt_name, "*?")) { | ||
562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | ||
563 | return parse_multiple_tracepoint_event(evlist, sys_name, | ||
564 | evt_name, flags); | ||
565 | } else { | ||
566 | return parse_single_tracepoint_event(sys_name, evt_name, | ||
567 | evt_length, attr, strp); | ||
568 | } | ||
569 | } | 534 | } |
570 | 535 | ||
571 | static enum event_result | 536 | static int |
572 | parse_breakpoint_type(const char *type, const char **strp, | 537 | parse_breakpoint_type(const char *type, struct perf_event_attr *attr) |
573 | struct perf_event_attr *attr) | ||
574 | { | 538 | { |
575 | int i; | 539 | int i; |
576 | 540 | ||
577 | for (i = 0; i < 3; i++) { | 541 | for (i = 0; i < 3; i++) { |
578 | if (!type[i]) | 542 | if (!type || !type[i]) |
579 | break; | 543 | break; |
580 | 544 | ||
581 | switch (type[i]) { | 545 | switch (type[i]) { |
@@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp, | |||
589 | attr->bp_type |= HW_BREAKPOINT_X; | 553 | attr->bp_type |= HW_BREAKPOINT_X; |
590 | break; | 554 | break; |
591 | default: | 555 | default: |
592 | return EVT_FAILED; | 556 | return -EINVAL; |
593 | } | 557 | } |
594 | } | 558 | } |
559 | |||
595 | if (!attr->bp_type) /* Default */ | 560 | if (!attr->bp_type) /* Default */ |
596 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | 561 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; |
597 | 562 | ||
598 | *strp = type + i; | 563 | return 0; |
599 | |||
600 | return EVT_HANDLED; | ||
601 | } | 564 | } |
602 | 565 | ||
603 | static enum event_result | 566 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
604 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | 567 | void *ptr, char *type) |
605 | { | 568 | { |
606 | const char *target; | 569 | struct perf_event_attr attr; |
607 | const char *type; | 570 | char name[MAX_NAME_LEN]; |
608 | char *endaddr; | ||
609 | u64 addr; | ||
610 | enum event_result err; | ||
611 | |||
612 | target = strchr(*strp, ':'); | ||
613 | if (!target) | ||
614 | return EVT_FAILED; | ||
615 | |||
616 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
617 | return EVT_FAILED; | ||
618 | |||
619 | target++; | ||
620 | |||
621 | addr = strtoull(target, &endaddr, 0); | ||
622 | if (target == endaddr) | ||
623 | return EVT_FAILED; | ||
624 | |||
625 | attr->bp_addr = addr; | ||
626 | *strp = endaddr; | ||
627 | 571 | ||
628 | type = strchr(target, ':'); | 572 | memset(&attr, 0, sizeof(attr)); |
573 | attr.bp_addr = (unsigned long) ptr; | ||
629 | 574 | ||
630 | /* If no type is defined, just rw as default */ | 575 | if (parse_breakpoint_type(type, &attr)) |
631 | if (!type) { | 576 | return -EINVAL; |
632 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
633 | } else { | ||
634 | err = parse_breakpoint_type(++type, strp, attr); | ||
635 | if (err == EVT_FAILED) | ||
636 | return EVT_FAILED; | ||
637 | } | ||
638 | 577 | ||
639 | /* | 578 | /* |
640 | * We should find a nice way to override the access length | 579 | * We should find a nice way to override the access length |
641 | * Provide some defaults for now | 580 | * Provide some defaults for now |
642 | */ | 581 | */ |
643 | if (attr->bp_type == HW_BREAKPOINT_X) | 582 | if (attr.bp_type == HW_BREAKPOINT_X) |
644 | attr->bp_len = sizeof(long); | 583 | attr.bp_len = sizeof(long); |
645 | else | 584 | else |
646 | attr->bp_len = HW_BREAKPOINT_LEN_4; | 585 | attr.bp_len = HW_BREAKPOINT_LEN_4; |
647 | 586 | ||
648 | attr->type = PERF_TYPE_BREAKPOINT; | 587 | attr.type = PERF_TYPE_BREAKPOINT; |
649 | 588 | ||
650 | return EVT_HANDLED; | 589 | snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); |
590 | return add_event(list, idx, &attr, name); | ||
651 | } | 591 | } |
652 | 592 | ||
653 | static int check_events(const char *str, unsigned int i) | 593 | static int config_term(struct perf_event_attr *attr, |
594 | struct parse_events__term *term) | ||
654 | { | 595 | { |
655 | int n; | 596 | switch (term->type) { |
597 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
598 | attr->config = term->val.num; | ||
599 | break; | ||
600 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
601 | attr->config1 = term->val.num; | ||
602 | break; | ||
603 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
604 | attr->config2 = term->val.num; | ||
605 | break; | ||
606 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
607 | attr->sample_period = term->val.num; | ||
608 | break; | ||
609 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
610 | /* | ||
611 | * TODO uncomment when the field is available | ||
612 | * attr->branch_sample_type = term->val.num; | ||
613 | */ | ||
614 | break; | ||
615 | default: | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||
656 | 620 | ||
657 | n = strlen(event_symbols[i].symbol); | 621 | static int config_attr(struct perf_event_attr *attr, |
658 | if (!strncasecmp(str, event_symbols[i].symbol, n)) | 622 | struct list_head *head, int fail) |
659 | return n; | 623 | { |
624 | struct parse_events__term *term; | ||
660 | 625 | ||
661 | n = strlen(event_symbols[i].alias); | 626 | list_for_each_entry(term, head, list) |
662 | if (n) { | 627 | if (config_term(attr, term) && fail) |
663 | if (!strncasecmp(str, event_symbols[i].alias, n)) | 628 | return -EINVAL; |
664 | return n; | ||
665 | } | ||
666 | 629 | ||
667 | return 0; | 630 | return 0; |
668 | } | 631 | } |
669 | 632 | ||
670 | static enum event_result | 633 | int parse_events_add_numeric(struct list_head *list, int *idx, |
671 | parse_symbolic_event(const char **strp, struct perf_event_attr *attr) | 634 | unsigned long type, unsigned long config, |
635 | struct list_head *head_config) | ||
672 | { | 636 | { |
673 | const char *str = *strp; | 637 | struct perf_event_attr attr; |
674 | unsigned int i; | 638 | |
675 | int n; | 639 | memset(&attr, 0, sizeof(attr)); |
676 | 640 | attr.type = type; | |
677 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | 641 | attr.config = config; |
678 | n = check_events(str, i); | 642 | |
679 | if (n > 0) { | 643 | if (head_config && |
680 | attr->type = event_symbols[i].type; | 644 | config_attr(&attr, head_config, 1)) |
681 | attr->config = event_symbols[i].config; | 645 | return -EINVAL; |
682 | *strp = str + n; | 646 | |
683 | return EVT_HANDLED; | 647 | return add_event(list, idx, &attr, |
684 | } | 648 | (char *) __event_name(type, config)); |
685 | } | ||
686 | return EVT_FAILED; | ||
687 | } | 649 | } |
688 | 650 | ||
689 | static enum event_result | 651 | int parse_events_add_pmu(struct list_head *list, int *idx, |
690 | parse_raw_event(const char **strp, struct perf_event_attr *attr) | 652 | char *name, struct list_head *head_config) |
691 | { | 653 | { |
692 | const char *str = *strp; | 654 | struct perf_event_attr attr; |
693 | u64 config; | 655 | struct perf_pmu *pmu; |
694 | int n; | 656 | |
695 | 657 | pmu = perf_pmu__find(name); | |
696 | if (*str != 'r') | 658 | if (!pmu) |
697 | return EVT_FAILED; | 659 | return -EINVAL; |
698 | n = hex2u64(str + 1, &config); | 660 | |
699 | if (n > 0) { | 661 | memset(&attr, 0, sizeof(attr)); |
700 | const char *end = str + n + 1; | 662 | |
701 | if (*end != '\0' && *end != ',' && *end != ':') | 663 | /* |
702 | return EVT_FAILED; | 664 | * Configure hardcoded terms first, no need to check |
703 | 665 | * return value when called with fail == 0 ;) | |
704 | *strp = end; | 666 | */ |
705 | attr->type = PERF_TYPE_RAW; | 667 | config_attr(&attr, head_config, 0); |
706 | attr->config = config; | 668 | |
707 | return EVT_HANDLED; | 669 | if (perf_pmu__config(pmu, &attr, head_config)) |
708 | } | 670 | return -EINVAL; |
709 | return EVT_FAILED; | 671 | |
672 | return add_event(list, idx, &attr, (char *) "pmu"); | ||
710 | } | 673 | } |
711 | 674 | ||
712 | static enum event_result | 675 | void parse_events_update_lists(struct list_head *list_event, |
713 | parse_numeric_event(const char **strp, struct perf_event_attr *attr) | 676 | struct list_head *list_all) |
714 | { | 677 | { |
715 | const char *str = *strp; | 678 | /* |
716 | char *endp; | 679 | * Called for single event definition. Update the |
717 | unsigned long type; | 680 | * 'all event' list, and reinit the 'signle event' |
718 | u64 config; | 681 | * list, for next event definition. |
719 | 682 | */ | |
720 | type = strtoul(str, &endp, 0); | 683 | list_splice_tail(list_event, list_all); |
721 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | 684 | INIT_LIST_HEAD(list_event); |
722 | str = endp + 1; | ||
723 | config = strtoul(str, &endp, 0); | ||
724 | if (endp > str) { | ||
725 | attr->type = type; | ||
726 | attr->config = config; | ||
727 | *strp = endp; | ||
728 | return EVT_HANDLED; | ||
729 | } | ||
730 | } | ||
731 | return EVT_FAILED; | ||
732 | } | 685 | } |
733 | 686 | ||
734 | static int | 687 | int parse_events_modifier(struct list_head *list, char *str) |
735 | parse_event_modifier(const char **strp, struct perf_event_attr *attr) | ||
736 | { | 688 | { |
737 | const char *str = *strp; | 689 | struct perf_evsel *evsel; |
738 | int exclude = 0, exclude_GH = 0; | 690 | int exclude = 0, exclude_GH = 0; |
739 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; | 691 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; |
740 | 692 | ||
741 | if (!*str) | 693 | if (str == NULL) |
742 | return 0; | 694 | return 0; |
743 | 695 | ||
744 | if (*str == ',') | ||
745 | return 0; | ||
746 | |||
747 | if (*str++ != ':') | ||
748 | return -1; | ||
749 | |||
750 | while (*str) { | 696 | while (*str) { |
751 | if (*str == 'u') { | 697 | if (*str == 'u') { |
752 | if (!exclude) | 698 | if (!exclude) |
@@ -775,111 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
775 | 721 | ||
776 | ++str; | 722 | ++str; |
777 | } | 723 | } |
778 | if (str < *strp + 2) | ||
779 | return -1; | ||
780 | 724 | ||
781 | *strp = str; | 725 | /* |
726 | * precise ip: | ||
727 | * | ||
728 | * 0 - SAMPLE_IP can have arbitrary skid | ||
729 | * 1 - SAMPLE_IP must have constant skid | ||
730 | * 2 - SAMPLE_IP requested to have 0 skid | ||
731 | * 3 - SAMPLE_IP must have 0 skid | ||
732 | * | ||
733 | * See also PERF_RECORD_MISC_EXACT_IP | ||
734 | */ | ||
735 | if (precise > 3) | ||
736 | return -EINVAL; | ||
782 | 737 | ||
783 | attr->exclude_user = eu; | 738 | list_for_each_entry(evsel, list, node) { |
784 | attr->exclude_kernel = ek; | 739 | evsel->attr.exclude_user = eu; |
785 | attr->exclude_hv = eh; | 740 | evsel->attr.exclude_kernel = ek; |
786 | attr->precise_ip = precise; | 741 | evsel->attr.exclude_hv = eh; |
787 | attr->exclude_host = eH; | 742 | evsel->attr.precise_ip = precise; |
788 | attr->exclude_guest = eG; | 743 | evsel->attr.exclude_host = eH; |
744 | evsel->attr.exclude_guest = eG; | ||
745 | } | ||
789 | 746 | ||
790 | return 0; | 747 | return 0; |
791 | } | 748 | } |
792 | 749 | ||
793 | /* | 750 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) |
794 | * Each event can have multiple symbolic names. | ||
795 | * Symbolic names are (almost) exactly matched. | ||
796 | */ | ||
797 | static enum event_result | ||
798 | parse_event_symbols(struct perf_evlist *evlist, const char **str, | ||
799 | struct perf_event_attr *attr) | ||
800 | { | 751 | { |
801 | enum event_result ret; | 752 | LIST_HEAD(list); |
802 | 753 | LIST_HEAD(list_tmp); | |
803 | ret = parse_tracepoint_event(evlist, str, attr); | 754 | YY_BUFFER_STATE buffer; |
804 | if (ret != EVT_FAILED) | 755 | int ret, idx = evlist->nr_entries; |
805 | goto modifier; | ||
806 | |||
807 | ret = parse_raw_event(str, attr); | ||
808 | if (ret != EVT_FAILED) | ||
809 | goto modifier; | ||
810 | 756 | ||
811 | ret = parse_numeric_event(str, attr); | 757 | buffer = parse_events__scan_string(str); |
812 | if (ret != EVT_FAILED) | ||
813 | goto modifier; | ||
814 | 758 | ||
815 | ret = parse_symbolic_event(str, attr); | 759 | ret = parse_events_parse(&list, &list_tmp, &idx); |
816 | if (ret != EVT_FAILED) | ||
817 | goto modifier; | ||
818 | 760 | ||
819 | ret = parse_generic_hw_event(str, attr); | 761 | parse_events__flush_buffer(buffer); |
820 | if (ret != EVT_FAILED) | 762 | parse_events__delete_buffer(buffer); |
821 | goto modifier; | ||
822 | 763 | ||
823 | ret = parse_breakpoint_event(str, attr); | 764 | if (!ret) { |
824 | if (ret != EVT_FAILED) | 765 | int entries = idx - evlist->nr_entries; |
825 | goto modifier; | 766 | perf_evlist__splice_list_tail(evlist, &list, entries); |
826 | 767 | return 0; | |
827 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
828 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
829 | return EVT_FAILED; | ||
830 | |||
831 | modifier: | ||
832 | if (parse_event_modifier(str, attr) < 0) { | ||
833 | fprintf(stderr, "invalid event modifier: '%s'\n", *str); | ||
834 | fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n"); | ||
835 | |||
836 | return EVT_FAILED; | ||
837 | } | 768 | } |
838 | 769 | ||
770 | /* | ||
771 | * There are 2 users - builtin-record and builtin-test objects. | ||
772 | * Both call perf_evlist__delete in case of error, so we dont | ||
773 | * need to bother. | ||
774 | */ | ||
775 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
776 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
839 | return ret; | 777 | return ret; |
840 | } | 778 | } |
841 | 779 | ||
842 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) | ||
843 | { | ||
844 | struct perf_event_attr attr; | ||
845 | enum event_result ret; | ||
846 | const char *ostr; | ||
847 | |||
848 | for (;;) { | ||
849 | ostr = str; | ||
850 | memset(&attr, 0, sizeof(attr)); | ||
851 | event_attr_init(&attr); | ||
852 | ret = parse_event_symbols(evlist, &str, &attr); | ||
853 | if (ret == EVT_FAILED) | ||
854 | return -1; | ||
855 | |||
856 | if (!(*str == 0 || *str == ',' || isspace(*str))) | ||
857 | return -1; | ||
858 | |||
859 | if (ret != EVT_HANDLED_ALL) { | ||
860 | struct perf_evsel *evsel; | ||
861 | evsel = perf_evsel__new(&attr, evlist->nr_entries); | ||
862 | if (evsel == NULL) | ||
863 | return -1; | ||
864 | perf_evlist__add(evlist, evsel); | ||
865 | |||
866 | evsel->name = calloc(str - ostr + 1, 1); | ||
867 | if (!evsel->name) | ||
868 | return -1; | ||
869 | strncpy(evsel->name, ostr, str - ostr); | ||
870 | } | ||
871 | |||
872 | if (*str == 0) | ||
873 | break; | ||
874 | if (*str == ',') | ||
875 | ++str; | ||
876 | while (isspace(*str)) | ||
877 | ++str; | ||
878 | } | ||
879 | |||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | int parse_events_option(const struct option *opt, const char *str, | 780 | int parse_events_option(const struct option *opt, const char *str, |
884 | int unset __used) | 781 | int unset __used) |
885 | { | 782 | { |
@@ -1052,8 +949,6 @@ int print_hwcache_events(const char *event_glob) | |||
1052 | return printed; | 949 | return printed; |
1053 | } | 950 | } |
1054 | 951 | ||
1055 | #define MAX_NAME_LEN 100 | ||
1056 | |||
1057 | /* | 952 | /* |
1058 | * Print the help text for the event symbols: | 953 | * Print the help text for the event symbols: |
1059 | */ | 954 | */ |
@@ -1102,8 +997,12 @@ void print_events(const char *event_glob) | |||
1102 | 997 | ||
1103 | printf("\n"); | 998 | printf("\n"); |
1104 | printf(" %-50s [%s]\n", | 999 | printf(" %-50s [%s]\n", |
1105 | "rNNN (see 'perf list --help' on how to encode it)", | 1000 | "rNNN", |
1106 | event_type_descriptors[PERF_TYPE_RAW]); | 1001 | event_type_descriptors[PERF_TYPE_RAW]); |
1002 | printf(" %-50s [%s]\n", | ||
1003 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | ||
1004 | event_type_descriptors[PERF_TYPE_RAW]); | ||
1005 | printf(" (see 'perf list --help' on how to encode it)\n"); | ||
1107 | printf("\n"); | 1006 | printf("\n"); |
1108 | 1007 | ||
1109 | printf(" %-50s [%s]\n", | 1008 | printf(" %-50s [%s]\n", |
@@ -1113,3 +1012,51 @@ void print_events(const char *event_glob) | |||
1113 | 1012 | ||
1114 | print_tracepoint_events(NULL, NULL); | 1013 | print_tracepoint_events(NULL, NULL); |
1115 | } | 1014 | } |
1015 | |||
1016 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | ||
1017 | { | ||
1018 | return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX; | ||
1019 | } | ||
1020 | |||
1021 | int parse_events__new_term(struct parse_events__term **_term, int type, | ||
1022 | char *config, char *str, long num) | ||
1023 | { | ||
1024 | struct parse_events__term *term; | ||
1025 | |||
1026 | term = zalloc(sizeof(*term)); | ||
1027 | if (!term) | ||
1028 | return -ENOMEM; | ||
1029 | |||
1030 | INIT_LIST_HEAD(&term->list); | ||
1031 | term->type = type; | ||
1032 | term->config = config; | ||
1033 | |||
1034 | switch (type) { | ||
1035 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
1036 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
1037 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
1038 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
1039 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
1040 | case PARSE_EVENTS__TERM_TYPE_NUM: | ||
1041 | term->val.num = num; | ||
1042 | break; | ||
1043 | case PARSE_EVENTS__TERM_TYPE_STR: | ||
1044 | term->val.str = str; | ||
1045 | break; | ||
1046 | default: | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | |||
1050 | *_term = term; | ||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | void parse_events__free_terms(struct list_head *terms) | ||
1055 | { | ||
1056 | struct parse_events__term *term, *h; | ||
1057 | |||
1058 | list_for_each_entry_safe(term, h, terms, list) | ||
1059 | free(term); | ||
1060 | |||
1061 | free(terms); | ||
1062 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 7e0cbe75d5f..ca069f89338 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
33 | 33 | ||
34 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
35 | 35 | ||
36 | enum { | ||
37 | PARSE_EVENTS__TERM_TYPE_CONFIG, | ||
38 | PARSE_EVENTS__TERM_TYPE_CONFIG1, | ||
39 | PARSE_EVENTS__TERM_TYPE_CONFIG2, | ||
40 | PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD, | ||
41 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
42 | PARSE_EVENTS__TERM_TYPE_NUM, | ||
43 | PARSE_EVENTS__TERM_TYPE_STR, | ||
44 | |||
45 | PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX = | ||
46 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
47 | }; | ||
48 | |||
49 | struct parse_events__term { | ||
50 | char *config; | ||
51 | union { | ||
52 | char *str; | ||
53 | long num; | ||
54 | } val; | ||
55 | int type; | ||
56 | |||
57 | struct list_head list; | ||
58 | }; | ||
59 | |||
60 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | ||
61 | int parse_events__new_term(struct parse_events__term **term, int type, | ||
62 | char *config, char *str, long num); | ||
63 | void parse_events__free_terms(struct list_head *terms); | ||
64 | int parse_events_modifier(struct list_head *list __used, char *str __used); | ||
65 | int parse_events_add_tracepoint(struct list_head *list, int *idx, | ||
66 | char *sys, char *event); | ||
67 | int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config, | ||
68 | unsigned long config1, unsigned long config2, | ||
69 | char *mod); | ||
70 | int parse_events_add_numeric(struct list_head *list, int *idx, | ||
71 | unsigned long type, unsigned long config, | ||
72 | struct list_head *head_config); | ||
73 | int parse_events_add_cache(struct list_head *list, int *idx, | ||
74 | char *type, char *op_result1, char *op_result2); | ||
75 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | ||
76 | void *ptr, char *type); | ||
77 | int parse_events_add_pmu(struct list_head *list, int *idx, | ||
78 | char *pmu , struct list_head *head_config); | ||
79 | void parse_events_update_lists(struct list_head *list_event, | ||
80 | struct list_head *list_all); | ||
81 | void parse_events_error(struct list_head *list_all, | ||
82 | struct list_head *list_event, | ||
83 | int *idx, char const *msg); | ||
84 | |||
36 | void print_events(const char *event_glob); | 85 | void print_events(const char *event_glob); |
37 | void print_events_type(u8 type); | 86 | void print_events_type(u8 type); |
38 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | 87 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l new file mode 100644 index 00000000000..05d766e3ecb --- /dev/null +++ b/tools/perf/util/parse-events.l | |||
@@ -0,0 +1,127 @@ | |||
1 | |||
2 | %option prefix="parse_events_" | ||
3 | |||
4 | %{ | ||
5 | #include <errno.h> | ||
6 | #include "../perf.h" | ||
7 | #include "parse-events-bison.h" | ||
8 | #include "parse-events.h" | ||
9 | |||
10 | static int __value(char *str, int base, int token) | ||
11 | { | ||
12 | long num; | ||
13 | |||
14 | errno = 0; | ||
15 | num = strtoul(str, NULL, base); | ||
16 | if (errno) | ||
17 | return PE_ERROR; | ||
18 | |||
19 | parse_events_lval.num = num; | ||
20 | return token; | ||
21 | } | ||
22 | |||
23 | static int value(int base) | ||
24 | { | ||
25 | return __value(parse_events_text, base, PE_VALUE); | ||
26 | } | ||
27 | |||
28 | static int raw(void) | ||
29 | { | ||
30 | return __value(parse_events_text + 1, 16, PE_RAW); | ||
31 | } | ||
32 | |||
33 | static int str(int token) | ||
34 | { | ||
35 | parse_events_lval.str = strdup(parse_events_text); | ||
36 | return token; | ||
37 | } | ||
38 | |||
39 | static int sym(int type, int config) | ||
40 | { | ||
41 | parse_events_lval.num = (type << 16) + config; | ||
42 | return PE_VALUE_SYM; | ||
43 | } | ||
44 | |||
45 | static int term(int type) | ||
46 | { | ||
47 | parse_events_lval.num = type; | ||
48 | return PE_TERM; | ||
49 | } | ||
50 | |||
51 | %} | ||
52 | |||
53 | num_dec [0-9]+ | ||
54 | num_hex 0x[a-fA-F0-9]+ | ||
55 | num_raw_hex [a-fA-F0-9]+ | ||
56 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* | ||
57 | modifier_event [ukhp]{1,5} | ||
58 | modifier_bp [rwx] | ||
59 | |||
60 | %% | ||
61 | cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | ||
62 | stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | ||
63 | stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | ||
64 | instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } | ||
65 | cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } | ||
66 | cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } | ||
67 | branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } | ||
68 | branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } | ||
69 | bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } | ||
70 | ref-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } | ||
71 | cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } | ||
72 | task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } | ||
73 | page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } | ||
74 | minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } | ||
75 | major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } | ||
76 | context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } | ||
77 | cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } | ||
78 | alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } | ||
79 | emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | ||
80 | |||
81 | L1-dcache|l1-d|l1d|L1-data | | ||
82 | L1-icache|l1-i|l1i|L1-instruction | | ||
83 | LLC|L2 | | ||
84 | dTLB|d-tlb|Data-TLB | | ||
85 | iTLB|i-tlb|Instruction-TLB | | ||
86 | branch|branches|bpu|btb|bpc | | ||
87 | node { return str(PE_NAME_CACHE_TYPE); } | ||
88 | |||
89 | load|loads|read | | ||
90 | store|stores|write | | ||
91 | prefetch|prefetches | | ||
92 | speculative-read|speculative-load | | ||
93 | refs|Reference|ops|access | | ||
94 | misses|miss { return str(PE_NAME_CACHE_OP_RESULT); } | ||
95 | |||
96 | /* | ||
97 | * These are event config hardcoded term names to be specified | ||
98 | * within xxx/.../ syntax. So far we dont clash with other names, | ||
99 | * so we can put them here directly. In case the we have a conflict | ||
100 | * in future, this needs to go into '//' condition block. | ||
101 | */ | ||
102 | config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } | ||
103 | config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } | ||
104 | config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } | ||
105 | period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | ||
106 | branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | ||
107 | |||
108 | mem: { return PE_PREFIX_MEM; } | ||
109 | r{num_raw_hex} { return raw(); } | ||
110 | {num_dec} { return value(10); } | ||
111 | {num_hex} { return value(16); } | ||
112 | |||
113 | {modifier_event} { return str(PE_MODIFIER_EVENT); } | ||
114 | {modifier_bp} { return str(PE_MODIFIER_BP); } | ||
115 | {name} { return str(PE_NAME); } | ||
116 | "/" { return '/'; } | ||
117 | - { return '-'; } | ||
118 | , { return ','; } | ||
119 | : { return ':'; } | ||
120 | = { return '='; } | ||
121 | |||
122 | %% | ||
123 | |||
124 | int parse_events_wrap(void) | ||
125 | { | ||
126 | return 1; | ||
127 | } | ||
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y new file mode 100644 index 00000000000..d9637da7333 --- /dev/null +++ b/tools/perf/util/parse-events.y | |||
@@ -0,0 +1,229 @@ | |||
1 | |||
2 | %name-prefix "parse_events_" | ||
3 | %parse-param {struct list_head *list_all} | ||
4 | %parse-param {struct list_head *list_event} | ||
5 | %parse-param {int *idx} | ||
6 | |||
7 | %{ | ||
8 | |||
9 | #define YYDEBUG 1 | ||
10 | |||
11 | #include <linux/compiler.h> | ||
12 | #include <linux/list.h> | ||
13 | #include "types.h" | ||
14 | #include "util.h" | ||
15 | #include "parse-events.h" | ||
16 | |||
17 | extern int parse_events_lex (void); | ||
18 | |||
19 | #define ABORT_ON(val) \ | ||
20 | do { \ | ||
21 | if (val) \ | ||
22 | YYABORT; \ | ||
23 | } while (0) | ||
24 | |||
25 | %} | ||
26 | |||
27 | %token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM | ||
28 | %token PE_NAME | ||
29 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP | ||
30 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | ||
31 | %token PE_PREFIX_MEM PE_PREFIX_RAW | ||
32 | %token PE_ERROR | ||
33 | %type <num> PE_VALUE | ||
34 | %type <num> PE_VALUE_SYM | ||
35 | %type <num> PE_RAW | ||
36 | %type <num> PE_TERM | ||
37 | %type <str> PE_NAME | ||
38 | %type <str> PE_NAME_CACHE_TYPE | ||
39 | %type <str> PE_NAME_CACHE_OP_RESULT | ||
40 | %type <str> PE_MODIFIER_EVENT | ||
41 | %type <str> PE_MODIFIER_BP | ||
42 | %type <head> event_config | ||
43 | %type <term> event_term | ||
44 | |||
45 | %union | ||
46 | { | ||
47 | char *str; | ||
48 | unsigned long num; | ||
49 | struct list_head *head; | ||
50 | struct parse_events__term *term; | ||
51 | } | ||
52 | %% | ||
53 | |||
54 | events: | ||
55 | events ',' event | event | ||
56 | |||
57 | event: | ||
58 | event_def PE_MODIFIER_EVENT | ||
59 | { | ||
60 | /* | ||
61 | * Apply modifier on all events added by single event definition | ||
62 | * (there could be more events added for multiple tracepoint | ||
63 | * definitions via '*?'. | ||
64 | */ | ||
65 | ABORT_ON(parse_events_modifier(list_event, $2)); | ||
66 | parse_events_update_lists(list_event, list_all); | ||
67 | } | ||
68 | | | ||
69 | event_def | ||
70 | { | ||
71 | parse_events_update_lists(list_event, list_all); | ||
72 | } | ||
73 | |||
74 | event_def: event_pmu | | ||
75 | event_legacy_symbol | | ||
76 | event_legacy_cache sep_dc | | ||
77 | event_legacy_mem | | ||
78 | event_legacy_tracepoint sep_dc | | ||
79 | event_legacy_numeric sep_dc | | ||
80 | event_legacy_raw sep_dc | ||
81 | |||
82 | event_pmu: | ||
83 | PE_NAME '/' event_config '/' | ||
84 | { | ||
85 | ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3)); | ||
86 | parse_events__free_terms($3); | ||
87 | } | ||
88 | |||
89 | event_legacy_symbol: | ||
90 | PE_VALUE_SYM '/' event_config '/' | ||
91 | { | ||
92 | int type = $1 >> 16; | ||
93 | int config = $1 & 255; | ||
94 | |||
95 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3)); | ||
96 | parse_events__free_terms($3); | ||
97 | } | ||
98 | | | ||
99 | PE_VALUE_SYM sep_slash_dc | ||
100 | { | ||
101 | int type = $1 >> 16; | ||
102 | int config = $1 & 255; | ||
103 | |||
104 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL)); | ||
105 | } | ||
106 | |||
107 | event_legacy_cache: | ||
108 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | ||
109 | { | ||
110 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5)); | ||
111 | } | ||
112 | | | ||
113 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | ||
114 | { | ||
115 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL)); | ||
116 | } | ||
117 | | | ||
118 | PE_NAME_CACHE_TYPE | ||
119 | { | ||
120 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL)); | ||
121 | } | ||
122 | |||
123 | event_legacy_mem: | ||
124 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | ||
125 | { | ||
126 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4)); | ||
127 | } | ||
128 | | | ||
129 | PE_PREFIX_MEM PE_VALUE sep_dc | ||
130 | { | ||
131 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL)); | ||
132 | } | ||
133 | |||
134 | event_legacy_tracepoint: | ||
135 | PE_NAME ':' PE_NAME | ||
136 | { | ||
137 | ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3)); | ||
138 | } | ||
139 | |||
140 | event_legacy_numeric: | ||
141 | PE_VALUE ':' PE_VALUE | ||
142 | { | ||
143 | ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL)); | ||
144 | } | ||
145 | |||
146 | event_legacy_raw: | ||
147 | PE_RAW | ||
148 | { | ||
149 | ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL)); | ||
150 | } | ||
151 | |||
152 | event_config: | ||
153 | event_config ',' event_term | ||
154 | { | ||
155 | struct list_head *head = $1; | ||
156 | struct parse_events__term *term = $3; | ||
157 | |||
158 | ABORT_ON(!head); | ||
159 | list_add_tail(&term->list, head); | ||
160 | $$ = $1; | ||
161 | } | ||
162 | | | ||
163 | event_term | ||
164 | { | ||
165 | struct list_head *head = malloc(sizeof(*head)); | ||
166 | struct parse_events__term *term = $1; | ||
167 | |||
168 | ABORT_ON(!head); | ||
169 | INIT_LIST_HEAD(head); | ||
170 | list_add_tail(&term->list, head); | ||
171 | $$ = head; | ||
172 | } | ||
173 | |||
174 | event_term: | ||
175 | PE_NAME '=' PE_NAME | ||
176 | { | ||
177 | struct parse_events__term *term; | ||
178 | |||
179 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR, | ||
180 | $1, $3, 0)); | ||
181 | $$ = term; | ||
182 | } | ||
183 | | | ||
184 | PE_NAME '=' PE_VALUE | ||
185 | { | ||
186 | struct parse_events__term *term; | ||
187 | |||
188 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
189 | $1, NULL, $3)); | ||
190 | $$ = term; | ||
191 | } | ||
192 | | | ||
193 | PE_NAME | ||
194 | { | ||
195 | struct parse_events__term *term; | ||
196 | |||
197 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
198 | $1, NULL, 1)); | ||
199 | $$ = term; | ||
200 | } | ||
201 | | | ||
202 | PE_TERM '=' PE_VALUE | ||
203 | { | ||
204 | struct parse_events__term *term; | ||
205 | |||
206 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3)); | ||
207 | $$ = term; | ||
208 | } | ||
209 | | | ||
210 | PE_TERM | ||
211 | { | ||
212 | struct parse_events__term *term; | ||
213 | |||
214 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1)); | ||
215 | $$ = term; | ||
216 | } | ||
217 | |||
218 | sep_dc: ':' | | ||
219 | |||
220 | sep_slash_dc: '/' | ':' | | ||
221 | |||
222 | %% | ||
223 | |||
224 | void parse_events_error(struct list_head *list_all __used, | ||
225 | struct list_head *list_event __used, | ||
226 | int *idx __used, | ||
227 | char const *msg __used) | ||
228 | { | ||
229 | } | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c new file mode 100644 index 00000000000..cb08a118e81 --- /dev/null +++ b/tools/perf/util/pmu.c | |||
@@ -0,0 +1,469 @@ | |||
1 | |||
2 | #include <linux/list.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <stdio.h> | ||
7 | #include <dirent.h> | ||
8 | #include "sysfs.h" | ||
9 | #include "util.h" | ||
10 | #include "pmu.h" | ||
11 | #include "parse-events.h" | ||
12 | |||
13 | int perf_pmu_parse(struct list_head *list, char *name); | ||
14 | extern FILE *perf_pmu_in; | ||
15 | |||
16 | static LIST_HEAD(pmus); | ||
17 | |||
18 | /* | ||
19 | * Parse & process all the sysfs attributes located under | ||
20 | * the directory specified in 'dir' parameter. | ||
21 | */ | ||
22 | static int pmu_format_parse(char *dir, struct list_head *head) | ||
23 | { | ||
24 | struct dirent *evt_ent; | ||
25 | DIR *format_dir; | ||
26 | int ret = 0; | ||
27 | |||
28 | format_dir = opendir(dir); | ||
29 | if (!format_dir) | ||
30 | return -EINVAL; | ||
31 | |||
32 | while (!ret && (evt_ent = readdir(format_dir))) { | ||
33 | char path[PATH_MAX]; | ||
34 | char *name = evt_ent->d_name; | ||
35 | FILE *file; | ||
36 | |||
37 | if (!strcmp(name, ".") || !strcmp(name, "..")) | ||
38 | continue; | ||
39 | |||
40 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | ||
41 | |||
42 | ret = -EINVAL; | ||
43 | file = fopen(path, "r"); | ||
44 | if (!file) | ||
45 | break; | ||
46 | |||
47 | perf_pmu_in = file; | ||
48 | ret = perf_pmu_parse(head, name); | ||
49 | fclose(file); | ||
50 | } | ||
51 | |||
52 | closedir(format_dir); | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Reading/parsing the default pmu format definition, which should be | ||
58 | * located at: | ||
59 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. | ||
60 | */ | ||
61 | static int pmu_format(char *name, struct list_head *format) | ||
62 | { | ||
63 | struct stat st; | ||
64 | char path[PATH_MAX]; | ||
65 | const char *sysfs; | ||
66 | |||
67 | sysfs = sysfs_find_mountpoint(); | ||
68 | if (!sysfs) | ||
69 | return -1; | ||
70 | |||
71 | snprintf(path, PATH_MAX, | ||
72 | "%s/bus/event_source/devices/%s/format", sysfs, name); | ||
73 | |||
74 | if (stat(path, &st) < 0) | ||
75 | return -1; | ||
76 | |||
77 | if (pmu_format_parse(path, format)) | ||
78 | return -1; | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Reading/parsing the default pmu type value, which should be | ||
85 | * located at: | ||
86 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. | ||
87 | */ | ||
88 | static int pmu_type(char *name, __u32 *type) | ||
89 | { | ||
90 | struct stat st; | ||
91 | char path[PATH_MAX]; | ||
92 | const char *sysfs; | ||
93 | FILE *file; | ||
94 | int ret = 0; | ||
95 | |||
96 | sysfs = sysfs_find_mountpoint(); | ||
97 | if (!sysfs) | ||
98 | return -1; | ||
99 | |||
100 | snprintf(path, PATH_MAX, | ||
101 | "%s/bus/event_source/devices/%s/type", sysfs, name); | ||
102 | |||
103 | if (stat(path, &st) < 0) | ||
104 | return -1; | ||
105 | |||
106 | file = fopen(path, "r"); | ||
107 | if (!file) | ||
108 | return -EINVAL; | ||
109 | |||
110 | if (1 != fscanf(file, "%u", type)) | ||
111 | ret = -1; | ||
112 | |||
113 | fclose(file); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static struct perf_pmu *pmu_lookup(char *name) | ||
118 | { | ||
119 | struct perf_pmu *pmu; | ||
120 | LIST_HEAD(format); | ||
121 | __u32 type; | ||
122 | |||
123 | /* | ||
124 | * The pmu data we store & need consists of the pmu | ||
125 | * type value and format definitions. Load both right | ||
126 | * now. | ||
127 | */ | ||
128 | if (pmu_format(name, &format)) | ||
129 | return NULL; | ||
130 | |||
131 | if (pmu_type(name, &type)) | ||
132 | return NULL; | ||
133 | |||
134 | pmu = zalloc(sizeof(*pmu)); | ||
135 | if (!pmu) | ||
136 | return NULL; | ||
137 | |||
138 | INIT_LIST_HEAD(&pmu->format); | ||
139 | list_splice(&format, &pmu->format); | ||
140 | pmu->name = strdup(name); | ||
141 | pmu->type = type; | ||
142 | return pmu; | ||
143 | } | ||
144 | |||
145 | static struct perf_pmu *pmu_find(char *name) | ||
146 | { | ||
147 | struct perf_pmu *pmu; | ||
148 | |||
149 | list_for_each_entry(pmu, &pmus, list) | ||
150 | if (!strcmp(pmu->name, name)) | ||
151 | return pmu; | ||
152 | |||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | struct perf_pmu *perf_pmu__find(char *name) | ||
157 | { | ||
158 | struct perf_pmu *pmu; | ||
159 | |||
160 | /* | ||
161 | * Once PMU is loaded it stays in the list, | ||
162 | * so we keep us from multiple reading/parsing | ||
163 | * the pmu format definitions. | ||
164 | */ | ||
165 | pmu = pmu_find(name); | ||
166 | if (pmu) | ||
167 | return pmu; | ||
168 | |||
169 | return pmu_lookup(name); | ||
170 | } | ||
171 | |||
172 | static struct perf_pmu__format* | ||
173 | pmu_find_format(struct list_head *formats, char *name) | ||
174 | { | ||
175 | struct perf_pmu__format *format; | ||
176 | |||
177 | list_for_each_entry(format, formats, list) | ||
178 | if (!strcmp(format->name, name)) | ||
179 | return format; | ||
180 | |||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Returns value based on the format definition (format parameter) | ||
186 | * and unformated value (value parameter). | ||
187 | * | ||
188 | * TODO maybe optimize a little ;) | ||
189 | */ | ||
190 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | ||
191 | { | ||
192 | unsigned long fbit, vbit; | ||
193 | __u64 v = 0; | ||
194 | |||
195 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | ||
196 | |||
197 | if (!test_bit(fbit, format)) | ||
198 | continue; | ||
199 | |||
200 | if (!(value & (1llu << vbit++))) | ||
201 | continue; | ||
202 | |||
203 | v |= (1llu << fbit); | ||
204 | } | ||
205 | |||
206 | return v; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Setup one of config[12] attr members based on the | ||
211 | * user input data - temr parameter. | ||
212 | */ | ||
213 | static int pmu_config_term(struct list_head *formats, | ||
214 | struct perf_event_attr *attr, | ||
215 | struct parse_events__term *term) | ||
216 | { | ||
217 | struct perf_pmu__format *format; | ||
218 | __u64 *vp; | ||
219 | |||
220 | /* | ||
221 | * Support only for hardcoded and numnerial terms. | ||
222 | * Hardcoded terms should be already in, so nothing | ||
223 | * to be done for them. | ||
224 | */ | ||
225 | if (parse_events__is_hardcoded_term(term)) | ||
226 | return 0; | ||
227 | |||
228 | if (term->type != PARSE_EVENTS__TERM_TYPE_NUM) | ||
229 | return -EINVAL; | ||
230 | |||
231 | format = pmu_find_format(formats, term->config); | ||
232 | if (!format) | ||
233 | return -EINVAL; | ||
234 | |||
235 | switch (format->value) { | ||
236 | case PERF_PMU_FORMAT_VALUE_CONFIG: | ||
237 | vp = &attr->config; | ||
238 | break; | ||
239 | case PERF_PMU_FORMAT_VALUE_CONFIG1: | ||
240 | vp = &attr->config1; | ||
241 | break; | ||
242 | case PERF_PMU_FORMAT_VALUE_CONFIG2: | ||
243 | vp = &attr->config2; | ||
244 | break; | ||
245 | default: | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | |||
249 | *vp |= pmu_format_value(format->bits, term->val.num); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, | ||
254 | struct list_head *head_terms) | ||
255 | { | ||
256 | struct parse_events__term *term, *h; | ||
257 | |||
258 | list_for_each_entry_safe(term, h, head_terms, list) | ||
259 | if (pmu_config_term(formats, attr, term)) | ||
260 | return -EINVAL; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Configures event's 'attr' parameter based on the: | ||
267 | * 1) users input - specified in terms parameter | ||
268 | * 2) pmu format definitions - specified by pmu parameter | ||
269 | */ | ||
270 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
271 | struct list_head *head_terms) | ||
272 | { | ||
273 | attr->type = pmu->type; | ||
274 | return pmu_config(&pmu->format, attr, head_terms); | ||
275 | } | ||
276 | |||
277 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
278 | int config, unsigned long *bits) | ||
279 | { | ||
280 | struct perf_pmu__format *format; | ||
281 | |||
282 | format = zalloc(sizeof(*format)); | ||
283 | if (!format) | ||
284 | return -ENOMEM; | ||
285 | |||
286 | format->name = strdup(name); | ||
287 | format->value = config; | ||
288 | memcpy(format->bits, bits, sizeof(format->bits)); | ||
289 | |||
290 | list_add_tail(&format->list, list); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | void perf_pmu__set_format(unsigned long *bits, long from, long to) | ||
295 | { | ||
296 | long b; | ||
297 | |||
298 | if (!to) | ||
299 | to = from; | ||
300 | |||
301 | memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); | ||
302 | for (b = from; b <= to; b++) | ||
303 | set_bit(b, bits); | ||
304 | } | ||
305 | |||
306 | /* Simulated format definitions. */ | ||
307 | static struct test_format { | ||
308 | const char *name; | ||
309 | const char *value; | ||
310 | } test_formats[] = { | ||
311 | { "krava01", "config:0-1,62-63\n", }, | ||
312 | { "krava02", "config:10-17\n", }, | ||
313 | { "krava03", "config:5\n", }, | ||
314 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
315 | { "krava12", "config1:63\n", }, | ||
316 | { "krava13", "config1:45-47\n", }, | ||
317 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
318 | { "krava22", "config2:8,18,48,58\n", }, | ||
319 | { "krava23", "config2:28-29,38\n", }, | ||
320 | }; | ||
321 | |||
322 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
323 | |||
324 | /* Simulated users input. */ | ||
325 | static struct parse_events__term test_terms[] = { | ||
326 | { | ||
327 | .config = (char *) "krava01", | ||
328 | .val.num = 15, | ||
329 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
330 | }, | ||
331 | { | ||
332 | .config = (char *) "krava02", | ||
333 | .val.num = 170, | ||
334 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
335 | }, | ||
336 | { | ||
337 | .config = (char *) "krava03", | ||
338 | .val.num = 1, | ||
339 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
340 | }, | ||
341 | { | ||
342 | .config = (char *) "krava11", | ||
343 | .val.num = 27, | ||
344 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
345 | }, | ||
346 | { | ||
347 | .config = (char *) "krava12", | ||
348 | .val.num = 1, | ||
349 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
350 | }, | ||
351 | { | ||
352 | .config = (char *) "krava13", | ||
353 | .val.num = 2, | ||
354 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
355 | }, | ||
356 | { | ||
357 | .config = (char *) "krava21", | ||
358 | .val.num = 119, | ||
359 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
360 | }, | ||
361 | { | ||
362 | .config = (char *) "krava22", | ||
363 | .val.num = 11, | ||
364 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
365 | }, | ||
366 | { | ||
367 | .config = (char *) "krava23", | ||
368 | .val.num = 2, | ||
369 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
370 | }, | ||
371 | }; | ||
372 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
373 | |||
374 | /* | ||
375 | * Prepare format directory data, exported by kernel | ||
376 | * at /sys/bus/event_source/devices/<dev>/format. | ||
377 | */ | ||
378 | static char *test_format_dir_get(void) | ||
379 | { | ||
380 | static char dir[PATH_MAX]; | ||
381 | unsigned int i; | ||
382 | |||
383 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
384 | if (!mkdtemp(dir)) | ||
385 | return NULL; | ||
386 | |||
387 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
388 | static char name[PATH_MAX]; | ||
389 | struct test_format *format = &test_formats[i]; | ||
390 | FILE *file; | ||
391 | |||
392 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
393 | |||
394 | file = fopen(name, "w"); | ||
395 | if (!file) | ||
396 | return NULL; | ||
397 | |||
398 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
399 | break; | ||
400 | |||
401 | fclose(file); | ||
402 | } | ||
403 | |||
404 | return dir; | ||
405 | } | ||
406 | |||
407 | /* Cleanup format directory. */ | ||
408 | static int test_format_dir_put(char *dir) | ||
409 | { | ||
410 | char buf[PATH_MAX]; | ||
411 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
412 | if (system(buf)) | ||
413 | return -1; | ||
414 | |||
415 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
416 | return system(buf); | ||
417 | } | ||
418 | |||
419 | static struct list_head *test_terms_list(void) | ||
420 | { | ||
421 | static LIST_HEAD(terms); | ||
422 | unsigned int i; | ||
423 | |||
424 | for (i = 0; i < TERMS_CNT; i++) | ||
425 | list_add_tail(&test_terms[i].list, &terms); | ||
426 | |||
427 | return &terms; | ||
428 | } | ||
429 | |||
430 | #undef TERMS_CNT | ||
431 | |||
432 | int perf_pmu__test(void) | ||
433 | { | ||
434 | char *format = test_format_dir_get(); | ||
435 | LIST_HEAD(formats); | ||
436 | struct list_head *terms = test_terms_list(); | ||
437 | int ret; | ||
438 | |||
439 | if (!format) | ||
440 | return -EINVAL; | ||
441 | |||
442 | do { | ||
443 | struct perf_event_attr attr; | ||
444 | |||
445 | memset(&attr, 0, sizeof(attr)); | ||
446 | |||
447 | ret = pmu_format_parse(format, &formats); | ||
448 | if (ret) | ||
449 | break; | ||
450 | |||
451 | ret = pmu_config(&formats, &attr, terms); | ||
452 | if (ret) | ||
453 | break; | ||
454 | |||
455 | ret = -EINVAL; | ||
456 | |||
457 | if (attr.config != 0xc00000000002a823) | ||
458 | break; | ||
459 | if (attr.config1 != 0x8000400000000145) | ||
460 | break; | ||
461 | if (attr.config2 != 0x0400000020041d07) | ||
462 | break; | ||
463 | |||
464 | ret = 0; | ||
465 | } while (0); | ||
466 | |||
467 | test_format_dir_put(format); | ||
468 | return ret; | ||
469 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h new file mode 100644 index 00000000000..68c0db965e1 --- /dev/null +++ b/tools/perf/util/pmu.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __PMU_H | ||
2 | #define __PMU_H | ||
3 | |||
4 | #include <linux/bitops.h> | ||
5 | #include "../../../include/linux/perf_event.h" | ||
6 | |||
7 | enum { | ||
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
9 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
10 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
11 | }; | ||
12 | |||
13 | #define PERF_PMU_FORMAT_BITS 64 | ||
14 | |||
15 | struct perf_pmu__format { | ||
16 | char *name; | ||
17 | int value; | ||
18 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
19 | struct list_head list; | ||
20 | }; | ||
21 | |||
22 | struct perf_pmu { | ||
23 | char *name; | ||
24 | __u32 type; | ||
25 | struct list_head format; | ||
26 | struct list_head list; | ||
27 | }; | ||
28 | |||
29 | struct perf_pmu *perf_pmu__find(char *name); | ||
30 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
31 | struct list_head *head_terms); | ||
32 | |||
33 | int perf_pmu_wrap(void); | ||
34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | ||
35 | |||
36 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
37 | int config, unsigned long *bits); | ||
38 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | ||
39 | |||
40 | int perf_pmu__test(void); | ||
41 | #endif /* __PMU_H */ | ||
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l new file mode 100644 index 00000000000..a15d9fbd7c0 --- /dev/null +++ b/tools/perf/util/pmu.l | |||
@@ -0,0 +1,43 @@ | |||
1 | %option prefix="perf_pmu_" | ||
2 | |||
3 | %{ | ||
4 | #include <stdlib.h> | ||
5 | #include <linux/bitops.h> | ||
6 | #include "pmu.h" | ||
7 | #include "pmu-bison.h" | ||
8 | |||
9 | static int value(int base) | ||
10 | { | ||
11 | long num; | ||
12 | |||
13 | errno = 0; | ||
14 | num = strtoul(perf_pmu_text, NULL, base); | ||
15 | if (errno) | ||
16 | return PP_ERROR; | ||
17 | |||
18 | perf_pmu_lval.num = num; | ||
19 | return PP_VALUE; | ||
20 | } | ||
21 | |||
22 | %} | ||
23 | |||
24 | num_dec [0-9]+ | ||
25 | |||
26 | %% | ||
27 | |||
28 | {num_dec} { return value(10); } | ||
29 | config { return PP_CONFIG; } | ||
30 | config1 { return PP_CONFIG1; } | ||
31 | config2 { return PP_CONFIG2; } | ||
32 | - { return '-'; } | ||
33 | : { return ':'; } | ||
34 | , { return ','; } | ||
35 | . { ; } | ||
36 | \n { ; } | ||
37 | |||
38 | %% | ||
39 | |||
40 | int perf_pmu_wrap(void) | ||
41 | { | ||
42 | return 1; | ||
43 | } | ||
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y new file mode 100644 index 00000000000..20ea77e9316 --- /dev/null +++ b/tools/perf/util/pmu.y | |||
@@ -0,0 +1,93 @@ | |||
1 | |||
2 | %name-prefix "perf_pmu_" | ||
3 | %parse-param {struct list_head *format} | ||
4 | %parse-param {char *name} | ||
5 | |||
6 | %{ | ||
7 | |||
8 | #include <linux/compiler.h> | ||
9 | #include <linux/list.h> | ||
10 | #include <linux/bitmap.h> | ||
11 | #include <string.h> | ||
12 | #include "pmu.h" | ||
13 | |||
14 | extern int perf_pmu_lex (void); | ||
15 | |||
16 | #define ABORT_ON(val) \ | ||
17 | do { \ | ||
18 | if (val) \ | ||
19 | YYABORT; \ | ||
20 | } while (0) | ||
21 | |||
22 | %} | ||
23 | |||
24 | %token PP_CONFIG PP_CONFIG1 PP_CONFIG2 | ||
25 | %token PP_VALUE PP_ERROR | ||
26 | %type <num> PP_VALUE | ||
27 | %type <bits> bit_term | ||
28 | %type <bits> bits | ||
29 | |||
30 | %union | ||
31 | { | ||
32 | unsigned long num; | ||
33 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
34 | } | ||
35 | |||
36 | %% | ||
37 | |||
38 | format: | ||
39 | format format_term | ||
40 | | | ||
41 | format_term | ||
42 | |||
43 | format_term: | ||
44 | PP_CONFIG ':' bits | ||
45 | { | ||
46 | ABORT_ON(perf_pmu__new_format(format, name, | ||
47 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
48 | $3)); | ||
49 | } | ||
50 | | | ||
51 | PP_CONFIG1 ':' bits | ||
52 | { | ||
53 | ABORT_ON(perf_pmu__new_format(format, name, | ||
54 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
55 | $3)); | ||
56 | } | ||
57 | | | ||
58 | PP_CONFIG2 ':' bits | ||
59 | { | ||
60 | ABORT_ON(perf_pmu__new_format(format, name, | ||
61 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
62 | $3)); | ||
63 | } | ||
64 | |||
65 | bits: | ||
66 | bits ',' bit_term | ||
67 | { | ||
68 | bitmap_or($$, $1, $3, 64); | ||
69 | } | ||
70 | | | ||
71 | bit_term | ||
72 | { | ||
73 | memcpy($$, $1, sizeof($1)); | ||
74 | } | ||
75 | |||
76 | bit_term: | ||
77 | PP_VALUE '-' PP_VALUE | ||
78 | { | ||
79 | perf_pmu__set_format($$, $1, $3); | ||
80 | } | ||
81 | | | ||
82 | PP_VALUE | ||
83 | { | ||
84 | perf_pmu__set_format($$, $1, 0); | ||
85 | } | ||
86 | |||
87 | %% | ||
88 | |||
89 | void perf_pmu_error(struct list_head *list __used, | ||
90 | char *name __used, | ||
91 | char const *msg __used) | ||
92 | { | ||
93 | } | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 2cc162d3b78..d448984ed78 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -972,10 +972,12 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
972 | struct dwarf_callback_param *param = data; | 972 | struct dwarf_callback_param *param = data; |
973 | struct probe_finder *pf = param->data; | 973 | struct probe_finder *pf = param->data; |
974 | struct perf_probe_point *pp = &pf->pev->point; | 974 | struct perf_probe_point *pp = &pf->pev->point; |
975 | Dwarf_Attribute attr; | ||
975 | 976 | ||
976 | /* Check tag and diename */ | 977 | /* Check tag and diename */ |
977 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 978 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
978 | !die_compare_name(sp_die, pp->function)) | 979 | !die_compare_name(sp_die, pp->function) || |
980 | dwarf_attr(sp_die, DW_AT_declaration, &attr)) | ||
979 | return DWARF_CB_OK; | 981 | return DWARF_CB_OK; |
980 | 982 | ||
981 | /* Check declared file */ | 983 | /* Check declared file */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 002ebbf59f4..9412e3b05f6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); |
143 | hists__init(&self->hists); | ||
143 | 144 | ||
144 | if (mode == O_RDONLY) { | 145 | if (mode == O_RDONLY) { |
145 | if (perf_session__open(self, force) < 0) | 146 | if (perf_session__open(self, force) < 0) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5dd83c3e2c0..c0a028c3eba 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <errno.h> | 2 | #include <errno.h> |
3 | #include <libgen.h> | ||
4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
5 | #include <stdio.h> | 4 | #include <stdio.h> |
6 | #include <string.h> | 5 | #include <string.h> |
@@ -51,6 +50,8 @@ struct symbol_conf symbol_conf = { | |||
51 | 50 | ||
52 | int dso__name_len(const struct dso *dso) | 51 | int dso__name_len(const struct dso *dso) |
53 | { | 52 | { |
53 | if (!dso) | ||
54 | return strlen("[unknown]"); | ||
54 | if (verbose) | 55 | if (verbose) |
55 | return dso->long_name_len; | 56 | return dso->long_name_len; |
56 | 57 | ||
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index a4088ced1e6..dfd1bd8371a 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -722,7 +722,7 @@ static char *event_read_name(void) | |||
722 | static int event_read_id(void) | 722 | static int event_read_id(void) |
723 | { | 723 | { |
724 | char *token; | 724 | char *token; |
725 | int id; | 725 | int id = -1; |
726 | 726 | ||
727 | if (read_expected_item(EVENT_ITEM, "ID") < 0) | 727 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
728 | return -1; | 728 | return -1; |
@@ -731,15 +731,13 @@ static int event_read_id(void) | |||
731 | return -1; | 731 | return -1; |
732 | 732 | ||
733 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 733 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
734 | goto fail; | 734 | goto free; |
735 | 735 | ||
736 | id = strtoul(token, NULL, 0); | 736 | id = strtoul(token, NULL, 0); |
737 | free_token(token); | ||
738 | return id; | ||
739 | 737 | ||
740 | fail: | 738 | free: |
741 | free_token(token); | 739 | free_token(token); |
742 | return -1; | 740 | return id; |
743 | } | 741 | } |
744 | 742 | ||
745 | static int field_is_string(struct format_field *field) | 743 | static int field_is_string(struct format_field *field) |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 84d761b730c..6ee82f60fea 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -49,6 +49,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout, | |||
49 | const char *format, ...); | 49 | const char *format, ...); |
50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); | 50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); |
51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); | 51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); |
52 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
53 | const char *exit_msg, int delay_sec); | ||
52 | 54 | ||
53 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); | 55 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); |
54 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); | 56 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index fa530fcc764..d7a1c4afe28 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -879,6 +879,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
879 | char *options[16]; | 879 | char *options[16]; |
880 | int nr_options = 0; | 880 | int nr_options = 0; |
881 | int key = -1; | 881 | int key = -1; |
882 | char buf[64]; | ||
882 | 883 | ||
883 | if (browser == NULL) | 884 | if (browser == NULL) |
884 | return -1; | 885 | return -1; |
@@ -933,6 +934,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
933 | goto zoom_dso; | 934 | goto zoom_dso; |
934 | case 't': | 935 | case 't': |
935 | goto zoom_thread; | 936 | goto zoom_thread; |
937 | case 's': | ||
938 | if (ui_browser__input_window("Symbol to show", | ||
939 | "Please enter the name of symbol you want to see", | ||
940 | buf, "ENTER: OK, ESC: Cancel", | ||
941 | delay_secs * 2) == K_ENTER) { | ||
942 | self->symbol_filter_str = *buf ? buf : NULL; | ||
943 | hists__filter_by_symbol(self); | ||
944 | hist_browser__reset(browser); | ||
945 | } | ||
946 | continue; | ||
936 | case K_F1: | 947 | case K_F1: |
937 | case 'h': | 948 | case 'h': |
938 | case '?': | 949 | case '?': |
@@ -950,7 +961,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
950 | "C Collapse all callchains\n" | 961 | "C Collapse all callchains\n" |
951 | "E Expand all callchains\n" | 962 | "E Expand all callchains\n" |
952 | "d Zoom into current DSO\n" | 963 | "d Zoom into current DSO\n" |
953 | "t Zoom into current Thread"); | 964 | "t Zoom into current Thread\n" |
965 | "s Filter symbol by name"); | ||
954 | continue; | 966 | continue; |
955 | case K_ENTER: | 967 | case K_ENTER: |
956 | case K_RIGHT: | 968 | case K_RIGHT: |
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h index 3458b198576..809eca5707f 100644 --- a/tools/perf/util/ui/keysyms.h +++ b/tools/perf/util/ui/keysyms.h | |||
@@ -16,6 +16,8 @@ | |||
16 | #define K_TAB '\t' | 16 | #define K_TAB '\t' |
17 | #define K_UNTAB SL_KEY_UNTAB | 17 | #define K_UNTAB SL_KEY_UNTAB |
18 | #define K_UP SL_KEY_UP | 18 | #define K_UP SL_KEY_UP |
19 | #define K_BKSPC 0x7f | ||
20 | #define K_DEL SL_KEY_DELETE | ||
19 | 21 | ||
20 | /* Not really keys */ | 22 | /* Not really keys */ |
21 | #define K_TIMER -1 | 23 | #define K_TIMER -1 |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 45daa7c41da..ad4374a16bb 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[]) | |||
69 | return popup_menu__run(&menu); | 69 | return popup_menu__run(&menu); |
70 | } | 70 | } |
71 | 71 | ||
72 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
73 | const char *exit_msg, int delay_secs) | ||
74 | { | ||
75 | int x, y, len, key; | ||
76 | int max_len = 60, nr_lines = 0; | ||
77 | static char buf[50]; | ||
78 | const char *t; | ||
79 | |||
80 | t = text; | ||
81 | while (1) { | ||
82 | const char *sep = strchr(t, '\n'); | ||
83 | |||
84 | if (sep == NULL) | ||
85 | sep = strchr(t, '\0'); | ||
86 | len = sep - t; | ||
87 | if (max_len < len) | ||
88 | max_len = len; | ||
89 | ++nr_lines; | ||
90 | if (*sep == '\0') | ||
91 | break; | ||
92 | t = sep + 1; | ||
93 | } | ||
94 | |||
95 | max_len += 2; | ||
96 | nr_lines += 8; | ||
97 | y = SLtt_Screen_Rows / 2 - nr_lines / 2; | ||
98 | x = SLtt_Screen_Cols / 2 - max_len / 2; | ||
99 | |||
100 | SLsmg_set_color(0); | ||
101 | SLsmg_draw_box(y, x++, nr_lines, max_len); | ||
102 | if (title) { | ||
103 | SLsmg_gotorc(y, x + 1); | ||
104 | SLsmg_write_string((char *)title); | ||
105 | } | ||
106 | SLsmg_gotorc(++y, x); | ||
107 | nr_lines -= 7; | ||
108 | max_len -= 2; | ||
109 | SLsmg_write_wrapped_string((unsigned char *)text, y, x, | ||
110 | nr_lines, max_len, 1); | ||
111 | y += nr_lines; | ||
112 | len = 5; | ||
113 | while (len--) { | ||
114 | SLsmg_gotorc(y + len - 1, x); | ||
115 | SLsmg_write_nstring((char *)" ", max_len); | ||
116 | } | ||
117 | SLsmg_draw_box(y++, x + 1, 3, max_len - 2); | ||
118 | |||
119 | SLsmg_gotorc(y + 3, x); | ||
120 | SLsmg_write_nstring((char *)exit_msg, max_len); | ||
121 | SLsmg_refresh(); | ||
122 | |||
123 | x += 2; | ||
124 | len = 0; | ||
125 | key = ui__getch(delay_secs); | ||
126 | while (key != K_TIMER && key != K_ENTER && key != K_ESC) { | ||
127 | if (key == K_BKSPC) { | ||
128 | if (len == 0) | ||
129 | goto next_key; | ||
130 | SLsmg_gotorc(y, x + --len); | ||
131 | SLsmg_write_char(' '); | ||
132 | } else { | ||
133 | buf[len] = key; | ||
134 | SLsmg_gotorc(y, x + len++); | ||
135 | SLsmg_write_char(key); | ||
136 | } | ||
137 | SLsmg_refresh(); | ||
138 | |||
139 | /* XXX more graceful overflow handling needed */ | ||
140 | if (len == sizeof(buf) - 1) { | ||
141 | ui_helpline__push("maximum size of symbol name reached!"); | ||
142 | key = K_ENTER; | ||
143 | break; | ||
144 | } | ||
145 | next_key: | ||
146 | key = ui__getch(delay_secs); | ||
147 | } | ||
148 | |||
149 | buf[len] = '\0'; | ||
150 | strncpy(input, buf, len+1); | ||
151 | return key; | ||
152 | } | ||
153 | |||
72 | int ui__question_window(const char *title, const char *text, | 154 | int ui__question_window(const char *title, const char *text, |
73 | const char *exit_msg, int delay_secs) | 155 | const char *exit_msg, int delay_secs) |
74 | { | 156 | { |