diff options
| -rw-r--r-- | tools/perf/builtin-annotate.c | 80 | ||||
| -rw-r--r-- | tools/perf/builtin-report.c | 160 | ||||
| -rw-r--r-- | tools/perf/util/evsel.h | 2 | ||||
| -rw-r--r-- | tools/perf/util/header.c | 31 | ||||
| -rw-r--r-- | tools/perf/util/header.h | 2 | ||||
| -rw-r--r-- | tools/perf/util/hist.c | 6 | ||||
| -rw-r--r-- | tools/perf/util/hist.h | 12 | ||||
| -rw-r--r-- | tools/perf/util/session.c | 63 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 16 | ||||
| -rw-r--r-- | tools/perf/util/ui/browsers/hists.c | 32 |
10 files changed, 208 insertions, 196 deletions
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 427182953fd7..695de4b5ae63 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -19,6 +19,8 @@ | |||
| 19 | #include "perf.h" | 19 | #include "perf.h" |
| 20 | #include "util/debug.h" | 20 | #include "util/debug.h" |
| 21 | 21 | ||
| 22 | #include "util/evlist.h" | ||
| 23 | #include "util/evsel.h" | ||
| 22 | #include "util/annotate.h" | 24 | #include "util/annotate.h" |
| 23 | #include "util/event.h" | 25 | #include "util/event.h" |
| 24 | #include "util/parse-options.h" | 26 | #include "util/parse-options.h" |
| @@ -38,9 +40,13 @@ static bool print_line; | |||
| 38 | 40 | ||
| 39 | static const char *sym_hist_filter; | 41 | static const char *sym_hist_filter; |
| 40 | 42 | ||
| 41 | static int hists__add_entry(struct hists *self, struct addr_location *al) | 43 | static int perf_evlist__add_sample(struct perf_evlist *evlist, |
| 44 | struct perf_sample *sample, | ||
| 45 | struct addr_location *al) | ||
| 42 | { | 46 | { |
| 47 | struct perf_evsel *evsel; | ||
| 43 | struct hist_entry *he; | 48 | struct hist_entry *he; |
| 49 | int ret; | ||
| 44 | 50 | ||
| 45 | if (sym_hist_filter != NULL && | 51 | if (sym_hist_filter != NULL && |
| 46 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { | 52 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { |
| @@ -53,23 +59,35 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
| 53 | return 0; | 59 | return 0; |
| 54 | } | 60 | } |
| 55 | 61 | ||
| 56 | he = __hists__add_entry(self, al, NULL, 1); | 62 | evsel = perf_evlist__id2evsel(evlist, sample->id); |
| 63 | if (evsel == NULL) { | ||
| 64 | /* | ||
| 65 | * FIXME: Propagate this back, but at least we're in a builtin, | ||
| 66 | * where exit() is allowed. ;-) | ||
| 67 | */ | ||
| 68 | ui__warning("Invalid %s file, contains samples with id not in " | ||
| 69 | "its header!\n", input_name); | ||
| 70 | exit_browser(0); | ||
| 71 | exit(1); | ||
| 72 | } | ||
| 73 | |||
| 74 | he = __hists__add_entry(&evsel->hists, al, NULL, 1); | ||
| 57 | if (he == NULL) | 75 | if (he == NULL) |
| 58 | return -ENOMEM; | 76 | return -ENOMEM; |
| 59 | 77 | ||
| 78 | ret = 0; | ||
| 60 | if (he->ms.sym != NULL) { | 79 | if (he->ms.sym != NULL) { |
| 61 | /* | ||
| 62 | * All aggregated on the first sym_hist. | ||
| 63 | */ | ||
| 64 | struct annotation *notes = symbol__annotation(he->ms.sym); | 80 | struct annotation *notes = symbol__annotation(he->ms.sym); |
| 65 | if (notes->src == NULL && | 81 | if (notes->src == NULL && |
| 66 | symbol__alloc_hist(he->ms.sym, 1) < 0) | 82 | symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0) |
| 67 | return -ENOMEM; | 83 | return -ENOMEM; |
| 68 | 84 | ||
| 69 | return hist_entry__inc_addr_samples(he, 0, al->addr); | 85 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
| 70 | } | 86 | } |
| 71 | 87 | ||
| 72 | return 0; | 88 | evsel->hists.stats.total_period += sample->period; |
| 89 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
| 90 | return ret; | ||
| 73 | } | 91 | } |
| 74 | 92 | ||
| 75 | static int process_sample_event(union perf_event *event, | 93 | static int process_sample_event(union perf_event *event, |
| @@ -85,7 +103,7 @@ static int process_sample_event(union perf_event *event, | |||
| 85 | return -1; | 103 | return -1; |
| 86 | } | 104 | } |
| 87 | 105 | ||
| 88 | if (!al.filtered && hists__add_entry(&session->hists, &al)) { | 106 | if (!al.filtered && perf_evlist__add_sample(session->evlist, sample, &al)) { |
| 89 | pr_warning("problem incrementing symbol count, " | 107 | pr_warning("problem incrementing symbol count, " |
| 90 | "skipping event\n"); | 108 | "skipping event\n"); |
| 91 | return -1; | 109 | return -1; |
| @@ -100,7 +118,7 @@ static int hist_entry__tty_annotate(struct hist_entry *he, int evidx) | |||
| 100 | print_line, full_paths, 0, 0); | 118 | print_line, full_paths, 0, 0); |
| 101 | } | 119 | } |
| 102 | 120 | ||
| 103 | static void hists__find_annotations(struct hists *self) | 121 | static void hists__find_annotations(struct hists *self, int evidx) |
| 104 | { | 122 | { |
| 105 | struct rb_node *nd = rb_first(&self->entries), *next; | 123 | struct rb_node *nd = rb_first(&self->entries), *next; |
| 106 | int key = KEY_RIGHT; | 124 | int key = KEY_RIGHT; |
| @@ -123,8 +141,7 @@ find_next: | |||
| 123 | } | 141 | } |
| 124 | 142 | ||
| 125 | if (use_browser > 0) { | 143 | if (use_browser > 0) { |
| 126 | /* For now all is aggregated on the first */ | 144 | key = hist_entry__tui_annotate(he, evidx); |
| 127 | key = hist_entry__tui_annotate(he, 0); | ||
| 128 | switch (key) { | 145 | switch (key) { |
| 129 | case KEY_RIGHT: | 146 | case KEY_RIGHT: |
| 130 | next = rb_next(nd); | 147 | next = rb_next(nd); |
| @@ -139,8 +156,7 @@ find_next: | |||
| 139 | if (next != NULL) | 156 | if (next != NULL) |
| 140 | nd = next; | 157 | nd = next; |
| 141 | } else { | 158 | } else { |
| 142 | /* For now all is aggregated on the first */ | 159 | hist_entry__tty_annotate(he, evidx); |
| 143 | hist_entry__tty_annotate(he, 0); | ||
| 144 | nd = rb_next(nd); | 160 | nd = rb_next(nd); |
| 145 | /* | 161 | /* |
| 146 | * Since we have a hist_entry per IP for the same | 162 | * Since we have a hist_entry per IP for the same |
| @@ -166,6 +182,8 @@ static int __cmd_annotate(void) | |||
| 166 | { | 182 | { |
| 167 | int ret; | 183 | int ret; |
| 168 | struct perf_session *session; | 184 | struct perf_session *session; |
| 185 | struct perf_evsel *pos; | ||
| 186 | u64 total_nr_samples; | ||
| 169 | 187 | ||
| 170 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); | 188 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); |
| 171 | if (session == NULL) | 189 | if (session == NULL) |
| @@ -186,12 +204,36 @@ static int __cmd_annotate(void) | |||
| 186 | if (verbose > 2) | 204 | if (verbose > 2) |
| 187 | perf_session__fprintf_dsos(session, stdout); | 205 | perf_session__fprintf_dsos(session, stdout); |
| 188 | 206 | ||
| 189 | hists__collapse_resort(&session->hists); | 207 | total_nr_samples = 0; |
| 190 | hists__output_resort(&session->hists); | 208 | list_for_each_entry(pos, &session->evlist->entries, node) { |
| 191 | hists__find_annotations(&session->hists); | 209 | struct hists *hists = &pos->hists; |
| 192 | out_delete: | 210 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 193 | perf_session__delete(session); | 211 | |
| 212 | if (nr_samples > 0) { | ||
| 213 | total_nr_samples += nr_samples; | ||
| 214 | hists__collapse_resort(hists); | ||
| 215 | hists__output_resort(hists); | ||
| 216 | hists__find_annotations(hists, pos->idx); | ||
| 217 | } | ||
| 218 | } | ||
| 194 | 219 | ||
| 220 | if (total_nr_samples == 0) { | ||
| 221 | ui__warning("The %s file has no samples!\n", input_name); | ||
| 222 | goto out_delete; | ||
| 223 | } | ||
| 224 | out_delete: | ||
| 225 | /* | ||
| 226 | * Speed up the exit process, for large files this can | ||
| 227 | * take quite a while. | ||
| 228 | * | ||
| 229 | * XXX Enable this when using valgrind or if we ever | ||
| 230 | * librarize this command. | ||
| 231 | * | ||
| 232 | * Also experiment with obstacks to see how much speed | ||
| 233 | * up we'll get here. | ||
| 234 | * | ||
| 235 | * perf_session__delete(session); | ||
| 236 | */ | ||
| 195 | return ret; | 237 | return ret; |
| 196 | } | 238 | } |
| 197 | 239 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index dddcc7ea2bec..1c399eae5f7b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -21,6 +21,8 @@ | |||
| 21 | 21 | ||
| 22 | #include "perf.h" | 22 | #include "perf.h" |
| 23 | #include "util/debug.h" | 23 | #include "util/debug.h" |
| 24 | #include "util/evlist.h" | ||
| 25 | #include "util/evsel.h" | ||
| 24 | #include "util/header.h" | 26 | #include "util/header.h" |
| 25 | #include "util/session.h" | 27 | #include "util/session.h" |
| 26 | 28 | ||
| @@ -46,39 +48,6 @@ static const char *pretty_printing_style = default_pretty_printing_style; | |||
| 46 | static char callchain_default_opt[] = "fractal,0.5"; | 48 | static char callchain_default_opt[] = "fractal,0.5"; |
| 47 | static symbol_filter_t annotate_init; | 49 | static symbol_filter_t annotate_init; |
| 48 | 50 | ||
| 49 | static struct hists *perf_session__hists_findnew(struct perf_session *self, | ||
| 50 | u64 event_stream, u32 type, | ||
| 51 | u64 config) | ||
| 52 | { | ||
| 53 | struct rb_node **p = &self->hists_tree.rb_node; | ||
| 54 | struct rb_node *parent = NULL; | ||
| 55 | struct hists *iter, *new; | ||
| 56 | |||
| 57 | while (*p != NULL) { | ||
| 58 | parent = *p; | ||
| 59 | iter = rb_entry(parent, struct hists, rb_node); | ||
| 60 | if (iter->config == config) | ||
| 61 | return iter; | ||
| 62 | |||
| 63 | |||
| 64 | if (config > iter->config) | ||
| 65 | p = &(*p)->rb_right; | ||
| 66 | else | ||
| 67 | p = &(*p)->rb_left; | ||
| 68 | } | ||
| 69 | |||
| 70 | new = malloc(sizeof(struct hists)); | ||
| 71 | if (new == NULL) | ||
| 72 | return NULL; | ||
| 73 | memset(new, 0, sizeof(struct hists)); | ||
| 74 | new->event_stream = event_stream; | ||
| 75 | new->config = config; | ||
| 76 | new->type = type; | ||
| 77 | rb_link_node(&new->rb_node, parent, p); | ||
| 78 | rb_insert_color(&new->rb_node, &self->hists_tree); | ||
| 79 | return new; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int perf_session__add_hist_entry(struct perf_session *session, | 51 | static int perf_session__add_hist_entry(struct perf_session *session, |
| 83 | struct addr_location *al, | 52 | struct addr_location *al, |
| 84 | struct perf_sample *sample) | 53 | struct perf_sample *sample) |
| @@ -86,8 +55,7 @@ static int perf_session__add_hist_entry(struct perf_session *session, | |||
| 86 | struct symbol *parent = NULL; | 55 | struct symbol *parent = NULL; |
| 87 | int err = 0; | 56 | int err = 0; |
| 88 | struct hist_entry *he; | 57 | struct hist_entry *he; |
| 89 | struct hists *hists; | 58 | struct perf_evsel *evsel; |
| 90 | struct perf_event_attr *attr; | ||
| 91 | 59 | ||
| 92 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 60 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
| 93 | err = perf_session__resolve_callchain(session, al->thread, | 61 | err = perf_session__resolve_callchain(session, al->thread, |
| @@ -96,15 +64,19 @@ static int perf_session__add_hist_entry(struct perf_session *session, | |||
| 96 | return err; | 64 | return err; |
| 97 | } | 65 | } |
| 98 | 66 | ||
| 99 | attr = perf_header__find_attr(sample->id, &session->header); | 67 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
| 100 | if (attr) | 68 | if (evsel == NULL) { |
| 101 | hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config); | 69 | /* |
| 102 | else | 70 | * FIXME: Propagate this back, but at least we're in a builtin, |
| 103 | hists = perf_session__hists_findnew(session, sample->id, 0, 0); | 71 | * where exit() is allowed. ;-) |
| 104 | if (hists == NULL) | 72 | */ |
| 105 | return -ENOMEM; | 73 | ui__warning("Invalid %s file, contains samples with id not in " |
| 74 | "its header!\n", input_name); | ||
| 75 | exit_browser(0); | ||
| 76 | exit(1); | ||
| 77 | } | ||
| 106 | 78 | ||
| 107 | he = __hists__add_entry(hists, al, parent, sample->period); | 79 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); |
| 108 | if (he == NULL) | 80 | if (he == NULL) |
| 109 | return -ENOMEM; | 81 | return -ENOMEM; |
| 110 | 82 | ||
| @@ -120,52 +92,30 @@ static int perf_session__add_hist_entry(struct perf_session *session, | |||
| 120 | * code will not use it. | 92 | * code will not use it. |
| 121 | */ | 93 | */ |
| 122 | if (al->sym != NULL && use_browser > 0) { | 94 | if (al->sym != NULL && use_browser > 0) { |
| 123 | /* | ||
| 124 | * All aggregated on the first sym_hist. | ||
| 125 | */ | ||
| 126 | struct annotation *notes = symbol__annotation(he->ms.sym); | 95 | struct annotation *notes = symbol__annotation(he->ms.sym); |
| 96 | |||
| 97 | assert(evsel != NULL); | ||
| 98 | |||
| 99 | err = -ENOMEM; | ||
| 127 | if (notes->src == NULL && | 100 | if (notes->src == NULL && |
| 128 | symbol__alloc_hist(he->ms.sym, 1) < 0) | 101 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) |
| 129 | err = -ENOMEM; | 102 | goto out; |
| 130 | else | 103 | |
| 131 | err = hist_entry__inc_addr_samples(he, 0, al->addr); | 104 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
| 132 | } | 105 | } |
| 133 | 106 | ||
| 107 | evsel->hists.stats.total_period += sample->period; | ||
| 108 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
| 109 | out: | ||
| 134 | return err; | 110 | return err; |
| 135 | } | 111 | } |
| 136 | 112 | ||
| 137 | static int add_event_total(struct perf_session *session, | ||
| 138 | struct perf_sample *sample, | ||
| 139 | struct perf_event_attr *attr) | ||
| 140 | { | ||
| 141 | struct hists *hists; | ||
| 142 | |||
| 143 | if (attr) | ||
| 144 | hists = perf_session__hists_findnew(session, sample->id, | ||
| 145 | attr->type, attr->config); | ||
| 146 | else | ||
| 147 | hists = perf_session__hists_findnew(session, sample->id, 0, 0); | ||
| 148 | |||
| 149 | if (!hists) | ||
| 150 | return -ENOMEM; | ||
| 151 | |||
| 152 | hists->stats.total_period += sample->period; | ||
| 153 | /* | ||
| 154 | * FIXME: add_event_total should be moved from here to | ||
| 155 | * perf_session__process_event so that the proper hist is passed to | ||
| 156 | * the event_op methods. | ||
| 157 | */ | ||
| 158 | hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); | ||
| 159 | session->hists.stats.total_period += sample->period; | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | 113 | ||
| 163 | static int process_sample_event(union perf_event *event, | 114 | static int process_sample_event(union perf_event *event, |
| 164 | struct perf_sample *sample, | 115 | struct perf_sample *sample, |
| 165 | struct perf_session *session) | 116 | struct perf_session *session) |
| 166 | { | 117 | { |
| 167 | struct addr_location al; | 118 | struct addr_location al; |
| 168 | struct perf_event_attr *attr; | ||
| 169 | 119 | ||
| 170 | if (perf_event__preprocess_sample(event, session, &al, sample, | 120 | if (perf_event__preprocess_sample(event, session, &al, sample, |
| 171 | annotate_init) < 0) { | 121 | annotate_init) < 0) { |
| @@ -182,27 +132,17 @@ static int process_sample_event(union perf_event *event, | |||
| 182 | return -1; | 132 | return -1; |
| 183 | } | 133 | } |
| 184 | 134 | ||
| 185 | attr = perf_header__find_attr(sample->id, &session->header); | ||
| 186 | |||
| 187 | if (add_event_total(session, sample, attr)) { | ||
| 188 | pr_debug("problem adding event period\n"); | ||
| 189 | return -1; | ||
| 190 | } | ||
| 191 | |||
| 192 | return 0; | 135 | return 0; |
| 193 | } | 136 | } |
| 194 | 137 | ||
| 195 | static int process_read_event(union perf_event *event, | 138 | static int process_read_event(union perf_event *event, |
| 196 | struct perf_sample *sample __used, | 139 | struct perf_sample *sample __used, |
| 197 | struct perf_session *session __used) | 140 | struct perf_session *session) |
| 198 | { | 141 | { |
| 199 | struct perf_event_attr *attr; | 142 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, |
| 200 | 143 | event->read.id); | |
| 201 | attr = perf_header__find_attr(event->read.id, &session->header); | ||
| 202 | |||
| 203 | if (show_threads) { | 144 | if (show_threads) { |
| 204 | const char *name = attr ? __event_name(attr->type, attr->config) | 145 | const char *name = evsel ? event_name(evsel) : "unknown"; |
| 205 | : "unknown"; | ||
| 206 | perf_read_values_add_value(&show_threads_values, | 146 | perf_read_values_add_value(&show_threads_values, |
| 207 | event->read.pid, event->read.tid, | 147 | event->read.pid, event->read.tid, |
| 208 | event->read.id, | 148 | event->read.id, |
| @@ -211,7 +151,7 @@ static int process_read_event(union perf_event *event, | |||
| 211 | } | 151 | } |
| 212 | 152 | ||
| 213 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, | 153 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, |
| 214 | attr ? __event_name(attr->type, attr->config) : "FAIL", | 154 | evsel ? event_name(evsel) : "FAIL", |
| 215 | event->read.value); | 155 | event->read.value); |
| 216 | 156 | ||
| 217 | return 0; | 157 | return 0; |
| @@ -282,21 +222,20 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
| 282 | return ret + fprintf(fp, "\n#\n"); | 222 | return ret + fprintf(fp, "\n#\n"); |
| 283 | } | 223 | } |
| 284 | 224 | ||
| 285 | static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | 225 | static int hists__tty_browse_tree(struct perf_evlist *evlist, const char *help) |
| 286 | { | 226 | { |
| 287 | struct rb_node *next = rb_first(tree); | 227 | struct perf_evsel *pos; |
| 288 | 228 | ||
| 289 | while (next) { | 229 | list_for_each_entry(pos, &evlist->entries, node) { |
| 290 | struct hists *hists = rb_entry(next, struct hists, rb_node); | 230 | struct hists *hists = &pos->hists; |
| 291 | const char *evname = NULL; | 231 | const char *evname = NULL; |
| 292 | 232 | ||
| 293 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | 233 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) |
| 294 | evname = __event_name(hists->type, hists->config); | 234 | evname = event_name(pos); |
| 295 | 235 | ||
| 296 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 236 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
| 297 | hists__fprintf(hists, NULL, false, stdout); | 237 | hists__fprintf(hists, NULL, false, stdout); |
| 298 | fprintf(stdout, "\n\n"); | 238 | fprintf(stdout, "\n\n"); |
| 299 | next = rb_next(&hists->rb_node); | ||
| 300 | } | 239 | } |
| 301 | 240 | ||
| 302 | if (sort_order == default_sort_order && | 241 | if (sort_order == default_sort_order && |
| @@ -317,8 +256,9 @@ static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | |||
| 317 | static int __cmd_report(void) | 256 | static int __cmd_report(void) |
| 318 | { | 257 | { |
| 319 | int ret = -EINVAL; | 258 | int ret = -EINVAL; |
| 259 | u64 nr_samples; | ||
| 320 | struct perf_session *session; | 260 | struct perf_session *session; |
| 321 | struct rb_node *next; | 261 | struct perf_evsel *pos; |
| 322 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 262 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
| 323 | 263 | ||
| 324 | signal(SIGINT, sig_handler); | 264 | signal(SIGINT, sig_handler); |
| @@ -349,26 +289,24 @@ static int __cmd_report(void) | |||
| 349 | if (verbose > 2) | 289 | if (verbose > 2) |
| 350 | perf_session__fprintf_dsos(session, stdout); | 290 | perf_session__fprintf_dsos(session, stdout); |
| 351 | 291 | ||
| 352 | next = rb_first(&session->hists_tree); | 292 | nr_samples = 0; |
| 353 | 293 | list_for_each_entry(pos, &session->evlist->entries, node) { | |
| 354 | if (next == NULL) { | 294 | struct hists *hists = &pos->hists; |
| 355 | ui__warning("The %s file has no samples!\n", input_name); | ||
| 356 | goto out_delete; | ||
| 357 | } | ||
| 358 | |||
| 359 | while (next) { | ||
| 360 | struct hists *hists; | ||
| 361 | 295 | ||
| 362 | hists = rb_entry(next, struct hists, rb_node); | ||
| 363 | hists__collapse_resort(hists); | 296 | hists__collapse_resort(hists); |
| 364 | hists__output_resort(hists); | 297 | hists__output_resort(hists); |
| 365 | next = rb_next(&hists->rb_node); | 298 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 299 | } | ||
| 300 | |||
| 301 | if (nr_samples == 0) { | ||
| 302 | ui__warning("The %s file has no samples!\n", input_name); | ||
| 303 | goto out_delete; | ||
| 366 | } | 304 | } |
| 367 | 305 | ||
| 368 | if (use_browser > 0) | 306 | if (use_browser > 0) |
| 369 | hists__tui_browse_tree(&session->hists_tree, help, 0); | 307 | hists__tui_browse_tree(session->evlist, help); |
| 370 | else | 308 | else |
| 371 | hists__tty_browse_tree(&session->hists_tree, help); | 309 | hists__tty_browse_tree(session->evlist, help); |
| 372 | 310 | ||
| 373 | out_delete: | 311 | out_delete: |
| 374 | /* | 312 | /* |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index f6fc8f651a25..281b60e5fc7b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "types.h" | 7 | #include "types.h" |
| 8 | #include "xyarray.h" | 8 | #include "xyarray.h" |
| 9 | #include "cgroup.h" | 9 | #include "cgroup.h" |
| 10 | #include "hist.h" | ||
| 10 | 11 | ||
| 11 | struct perf_counts_values { | 12 | struct perf_counts_values { |
| 12 | union { | 13 | union { |
| @@ -51,6 +52,7 @@ struct perf_evsel { | |||
| 51 | struct xyarray *id; | 52 | struct xyarray *id; |
| 52 | struct perf_counts *counts; | 53 | struct perf_counts *counts; |
| 53 | int idx; | 54 | int idx; |
| 55 | struct hists hists; | ||
| 54 | char *name; | 56 | char *name; |
| 55 | void *priv; | 57 | void *priv; |
| 56 | struct cgroup_sel *cgrp; | 58 | struct cgroup_sel *cgrp; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 72c124dc5781..108b0db7bbef 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -969,37 +969,6 @@ bool perf_header__sample_id_all(const struct perf_header *header) | |||
| 969 | return value; | 969 | return value; |
| 970 | } | 970 | } |
| 971 | 971 | ||
| 972 | struct perf_event_attr * | ||
| 973 | perf_header__find_attr(u64 id, struct perf_header *header) | ||
| 974 | { | ||
| 975 | int i; | ||
| 976 | |||
| 977 | /* | ||
| 978 | * We set id to -1 if the data file doesn't contain sample | ||
| 979 | * ids. This can happen when the data file contains one type | ||
| 980 | * of event and in that case, the header can still store the | ||
| 981 | * event attribute information. Check for this and avoid | ||
| 982 | * walking through the entire list of ids which may be large. | ||
| 983 | */ | ||
| 984 | if (id == -1ULL) { | ||
| 985 | if (header->attrs > 0) | ||
| 986 | return &header->attr[0]->attr; | ||
| 987 | return NULL; | ||
| 988 | } | ||
| 989 | |||
| 990 | for (i = 0; i < header->attrs; i++) { | ||
| 991 | struct perf_header_attr *attr = header->attr[i]; | ||
| 992 | int j; | ||
| 993 | |||
| 994 | for (j = 0; j < attr->ids; j++) { | ||
| 995 | if (attr->id[j] == id) | ||
| 996 | return &attr->attr; | ||
| 997 | } | ||
| 998 | } | ||
| 999 | |||
| 1000 | return NULL; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 972 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
| 1004 | perf_event__handler_t process, | 973 | perf_event__handler_t process, |
| 1005 | struct perf_session *session) | 974 | struct perf_session *session) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index f042cebcec1e..2fab13348aab 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -85,8 +85,6 @@ int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | |||
| 85 | 85 | ||
| 86 | u64 perf_header__sample_type(struct perf_header *header); | 86 | u64 perf_header__sample_type(struct perf_header *header); |
| 87 | bool perf_header__sample_id_all(const struct perf_header *header); | 87 | bool perf_header__sample_id_all(const struct perf_header *header); |
| 88 | struct perf_event_attr * | ||
| 89 | perf_header__find_attr(u64 id, struct perf_header *header); | ||
| 90 | void perf_header__set_feat(struct perf_header *self, int feat); | 88 | void perf_header__set_feat(struct perf_header *self, int feat); |
| 91 | void perf_header__clear_feat(struct perf_header *self, int feat); | 89 | void perf_header__clear_feat(struct perf_header *self, int feat); |
| 92 | bool perf_header__has_feat(const struct perf_header *self, int feat); | 90 | bool perf_header__has_feat(const struct perf_header *self, int feat); |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f7ad6bdbc667..627a02e03c57 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -984,8 +984,12 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
| 984 | size_t ret = 0; | 984 | size_t ret = 0; |
| 985 | 985 | ||
| 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
| 987 | const char *name = perf_event__name(i); | 987 | const char *name; |
| 988 | 988 | ||
| 989 | if (self->stats.nr_events[i] == 0) | ||
| 990 | continue; | ||
| 991 | |||
| 992 | name = perf_event__name(i); | ||
| 989 | if (!strcmp(name, "UNKNOWN")) | 993 | if (!strcmp(name, "UNKNOWN")) |
| 990 | continue; | 994 | continue; |
| 991 | 995 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 37c79089de09..0d38b435827b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -42,13 +42,10 @@ enum hist_column { | |||
| 42 | }; | 42 | }; |
| 43 | 43 | ||
| 44 | struct hists { | 44 | struct hists { |
| 45 | struct rb_node rb_node; | ||
| 46 | struct rb_root entries; | 45 | struct rb_root entries; |
| 47 | u64 nr_entries; | 46 | u64 nr_entries; |
| 48 | struct events_stats stats; | 47 | struct events_stats stats; |
| 49 | u64 config; | ||
| 50 | u64 event_stream; | 48 | u64 event_stream; |
| 51 | u32 type; | ||
| 52 | u16 col_len[HISTC_NR_COLS]; | 49 | u16 col_len[HISTC_NR_COLS]; |
| 53 | /* Best would be to reuse the session callchain cursor */ | 50 | /* Best would be to reuse the session callchain cursor */ |
| 54 | struct callchain_cursor callchain_cursor; | 51 | struct callchain_cursor callchain_cursor; |
| @@ -87,6 +84,8 @@ u16 hists__col_len(struct hists *self, enum hist_column col); | |||
| 87 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 84 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
| 88 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 85 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
| 89 | 86 | ||
| 87 | struct perf_evlist; | ||
| 88 | |||
| 90 | #ifdef NO_NEWT_SUPPORT | 89 | #ifdef NO_NEWT_SUPPORT |
| 91 | static inline int hists__browse(struct hists *self __used, | 90 | static inline int hists__browse(struct hists *self __used, |
| 92 | const char *helpline __used, | 91 | const char *helpline __used, |
| @@ -95,9 +94,8 @@ static inline int hists__browse(struct hists *self __used, | |||
| 95 | return 0; | 94 | return 0; |
| 96 | } | 95 | } |
| 97 | 96 | ||
| 98 | static inline int hists__tui_browse_tree(struct rb_root *self __used, | 97 | static inline int hists__tui_browse_tree(struct perf_evlist *evlist __used, |
| 99 | const char *help __used, | 98 | const char *help __used) |
| 100 | int evidx __used) | ||
| 101 | { | 99 | { |
| 102 | return 0; | 100 | return 0; |
| 103 | } | 101 | } |
| @@ -118,7 +116,7 @@ int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | |||
| 118 | #define KEY_LEFT NEWT_KEY_LEFT | 116 | #define KEY_LEFT NEWT_KEY_LEFT |
| 119 | #define KEY_RIGHT NEWT_KEY_RIGHT | 117 | #define KEY_RIGHT NEWT_KEY_RIGHT |
| 120 | 118 | ||
| 121 | int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx); | 119 | int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help); |
| 122 | #endif | 120 | #endif |
| 123 | 121 | ||
| 124 | unsigned int hists__sort_list_width(struct hists *self); | 122 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a3a871f7bda3..0d414199889d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -7,10 +7,52 @@ | |||
| 7 | #include <sys/types.h> | 7 | #include <sys/types.h> |
| 8 | #include <sys/mman.h> | 8 | #include <sys/mman.h> |
| 9 | 9 | ||
| 10 | #include "evlist.h" | ||
| 11 | #include "evsel.h" | ||
| 10 | #include "session.h" | 12 | #include "session.h" |
| 11 | #include "sort.h" | 13 | #include "sort.h" |
| 12 | #include "util.h" | 14 | #include "util.h" |
| 13 | 15 | ||
| 16 | static int perf_session__read_evlist(struct perf_session *session) | ||
| 17 | { | ||
| 18 | int i, j; | ||
| 19 | |||
| 20 | session->evlist = perf_evlist__new(NULL, NULL); | ||
| 21 | if (session->evlist == NULL) | ||
| 22 | return -ENOMEM; | ||
| 23 | |||
| 24 | for (i = 0; i < session->header.attrs; ++i) { | ||
| 25 | struct perf_header_attr *hattr = session->header.attr[i]; | ||
| 26 | struct perf_evsel *evsel = perf_evsel__new(&hattr->attr, i); | ||
| 27 | |||
| 28 | if (evsel == NULL) | ||
| 29 | goto out_delete_evlist; | ||
| 30 | /* | ||
| 31 | * Do it before so that if perf_evsel__alloc_id fails, this | ||
| 32 | * entry gets purged too at perf_evlist__delete(). | ||
| 33 | */ | ||
| 34 | perf_evlist__add(session->evlist, evsel); | ||
| 35 | /* | ||
| 36 | * We don't have the cpu and thread maps on the header, so | ||
| 37 | * for allocating the perf_sample_id table we fake 1 cpu and | ||
| 38 | * hattr->ids threads. | ||
| 39 | */ | ||
| 40 | if (perf_evsel__alloc_id(evsel, 1, hattr->ids)) | ||
| 41 | goto out_delete_evlist; | ||
| 42 | |||
| 43 | for (j = 0; j < hattr->ids; ++j) | ||
| 44 | perf_evlist__id_hash(session->evlist, evsel, 0, j, | ||
| 45 | hattr->id[j]); | ||
| 46 | } | ||
| 47 | |||
| 48 | return 0; | ||
| 49 | |||
| 50 | out_delete_evlist: | ||
| 51 | perf_evlist__delete(session->evlist); | ||
| 52 | session->evlist = NULL; | ||
| 53 | return -ENOMEM; | ||
| 54 | } | ||
| 55 | |||
| 14 | static int perf_session__open(struct perf_session *self, bool force) | 56 | static int perf_session__open(struct perf_session *self, bool force) |
| 15 | { | 57 | { |
| 16 | struct stat input_stat; | 58 | struct stat input_stat; |
| @@ -56,6 +98,11 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
| 56 | goto out_close; | 98 | goto out_close; |
| 57 | } | 99 | } |
| 58 | 100 | ||
| 101 | if (perf_session__read_evlist(self) < 0) { | ||
| 102 | pr_err("Not enough memory to read the event selector list\n"); | ||
| 103 | goto out_close; | ||
| 104 | } | ||
| 105 | |||
| 59 | self->size = input_stat.st_size; | 106 | self->size = input_stat.st_size; |
| 60 | return 0; | 107 | return 0; |
| 61 | 108 | ||
| @@ -141,7 +188,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
| 141 | memcpy(self->filename, filename, len); | 188 | memcpy(self->filename, filename, len); |
| 142 | self->threads = RB_ROOT; | 189 | self->threads = RB_ROOT; |
| 143 | INIT_LIST_HEAD(&self->dead_threads); | 190 | INIT_LIST_HEAD(&self->dead_threads); |
| 144 | self->hists_tree = RB_ROOT; | ||
| 145 | self->last_match = NULL; | 191 | self->last_match = NULL; |
| 146 | /* | 192 | /* |
| 147 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | 193 | * On 64bit we can mmap the data file in one go. No need for tiny mmap |
| @@ -1137,3 +1183,18 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | |||
| 1137 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 1183 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); |
| 1138 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | 1184 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); |
| 1139 | } | 1185 | } |
| 1186 | |||
| 1187 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | ||
| 1188 | { | ||
| 1189 | struct perf_evsel *pos; | ||
| 1190 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | ||
| 1191 | |||
| 1192 | ret += hists__fprintf_nr_events(&session->hists, fp); | ||
| 1193 | |||
| 1194 | list_for_each_entry(pos, &session->evlist->entries, node) { | ||
| 1195 | ret += fprintf(fp, "%s stats:\n", event_name(pos)); | ||
| 1196 | ret += hists__fprintf_nr_events(&pos->hists, fp); | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | return ret; | ||
| 1200 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 977b3a1b14aa..05dd7bcb9453 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -34,12 +34,12 @@ struct perf_session { | |||
| 34 | struct thread *last_match; | 34 | struct thread *last_match; |
| 35 | struct machine host_machine; | 35 | struct machine host_machine; |
| 36 | struct rb_root machines; | 36 | struct rb_root machines; |
| 37 | struct rb_root hists_tree; | 37 | struct perf_evlist *evlist; |
| 38 | /* | 38 | /* |
| 39 | * FIXME: should point to the first entry in hists_tree and | 39 | * FIXME: Need to split this up further, we need global |
| 40 | * be a hists instance. Right now its only 'report' | 40 | * stats + per event stats. 'perf diff' also needs |
| 41 | * that is using ->hists_tree while all the rest use | 41 | * to properly support multiple events in a single |
| 42 | * ->hists. | 42 | * perf.data file. |
| 43 | */ | 43 | */ |
| 44 | struct hists hists; | 44 | struct hists hists; |
| 45 | u64 sample_type; | 45 | u64 sample_type; |
| @@ -151,11 +151,7 @@ size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | |||
| 151 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 151 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, |
| 152 | FILE *fp, bool with_hits); | 152 | FILE *fp, bool with_hits); |
| 153 | 153 | ||
| 154 | static inline | 154 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
| 155 | size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | ||
| 156 | { | ||
| 157 | return hists__fprintf_nr_events(&self->hists, fp); | ||
| 158 | } | ||
| 159 | 155 | ||
| 160 | static inline int perf_session__parse_sample(struct perf_session *session, | 156 | static inline int perf_session__parse_sample(struct perf_session *session, |
| 161 | const union perf_event *event, | 157 | const union perf_event *event, |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index c98e6f81d285..f3af4fe5cdc4 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
| @@ -7,6 +7,8 @@ | |||
| 7 | #include <newt.h> | 7 | #include <newt.h> |
| 8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
| 9 | 9 | ||
| 10 | #include "../../evsel.h" | ||
| 11 | #include "../../evlist.h" | ||
| 10 | #include "../../hist.h" | 12 | #include "../../hist.h" |
| 11 | #include "../../pstack.h" | 13 | #include "../../pstack.h" |
| 12 | #include "../../sort.h" | 14 | #include "../../sort.h" |
| @@ -987,31 +989,33 @@ out: | |||
| 987 | return key; | 989 | return key; |
| 988 | } | 990 | } |
| 989 | 991 | ||
| 990 | int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx) | 992 | int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help) |
| 991 | { | 993 | { |
| 992 | struct rb_node *first = rb_first(self), *nd = first, *next; | 994 | struct perf_evsel *pos; |
| 993 | int key = 0; | ||
| 994 | 995 | ||
| 995 | while (nd) { | 996 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); |
| 996 | struct hists *hists = rb_entry(nd, struct hists, rb_node); | 997 | while (pos) { |
| 997 | const char *ev_name = __event_name(hists->type, hists->config); | 998 | struct hists *hists = &pos->hists; |
| 999 | const char *ev_name = event_name(pos); | ||
| 1000 | int key = hists__browse(hists, help, ev_name, pos->idx); | ||
| 998 | 1001 | ||
| 999 | key = hists__browse(hists, help, ev_name, evidx); | ||
| 1000 | switch (key) { | 1002 | switch (key) { |
| 1001 | case NEWT_KEY_TAB: | 1003 | case NEWT_KEY_TAB: |
| 1002 | next = rb_next(nd); | 1004 | if (pos->node.next == &evlist->entries) |
| 1003 | if (next) | 1005 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); |
| 1004 | nd = next; | 1006 | else |
| 1007 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
| 1005 | break; | 1008 | break; |
| 1006 | case NEWT_KEY_UNTAB: | 1009 | case NEWT_KEY_UNTAB: |
| 1007 | if (nd == first) | 1010 | if (pos->node.prev == &evlist->entries) |
| 1008 | continue; | 1011 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); |
| 1009 | nd = rb_prev(nd); | 1012 | else |
| 1013 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
| 1010 | break; | 1014 | break; |
| 1011 | default: | 1015 | default: |
| 1012 | return key; | 1016 | return key; |
| 1013 | } | 1017 | } |
| 1014 | } | 1018 | } |
| 1015 | 1019 | ||
| 1016 | return key; | 1020 | return 0; |
| 1017 | } | 1021 | } |
