diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
| -rw-r--r-- | tools/perf/builtin-report.c | 225 |
1 files changed, 92 insertions, 133 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e..b1b82009ab9 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | #include "util/util.h" | 10 | #include "util/util.h" |
| 11 | 11 | ||
| 12 | #include "util/annotate.h" | ||
| 12 | #include "util/color.h" | 13 | #include "util/color.h" |
| 13 | #include <linux/list.h> | 14 | #include <linux/list.h> |
| 14 | #include "util/cache.h" | 15 | #include "util/cache.h" |
| @@ -20,6 +21,8 @@ | |||
| 20 | 21 | ||
| 21 | #include "perf.h" | 22 | #include "perf.h" |
| 22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
| 24 | #include "util/evlist.h" | ||
| 25 | #include "util/evsel.h" | ||
| 23 | #include "util/header.h" | 26 | #include "util/header.h" |
| 24 | #include "util/session.h" | 27 | #include "util/session.h" |
| 25 | 28 | ||
| @@ -43,120 +46,79 @@ static const char default_pretty_printing_style[] = "normal"; | |||
| 43 | static const char *pretty_printing_style = default_pretty_printing_style; | 46 | static const char *pretty_printing_style = default_pretty_printing_style; |
| 44 | 47 | ||
| 45 | static char callchain_default_opt[] = "fractal,0.5"; | 48 | static char callchain_default_opt[] = "fractal,0.5"; |
| 49 | static symbol_filter_t annotate_init; | ||
| 46 | 50 | ||
| 47 | static struct hists *perf_session__hists_findnew(struct perf_session *self, | 51 | static int perf_session__add_hist_entry(struct perf_session *session, |
| 48 | u64 event_stream, u32 type, | ||
| 49 | u64 config) | ||
| 50 | { | ||
| 51 | struct rb_node **p = &self->hists_tree.rb_node; | ||
| 52 | struct rb_node *parent = NULL; | ||
| 53 | struct hists *iter, *new; | ||
| 54 | |||
| 55 | while (*p != NULL) { | ||
| 56 | parent = *p; | ||
| 57 | iter = rb_entry(parent, struct hists, rb_node); | ||
| 58 | if (iter->config == config) | ||
| 59 | return iter; | ||
| 60 | |||
| 61 | |||
| 62 | if (config > iter->config) | ||
| 63 | p = &(*p)->rb_right; | ||
| 64 | else | ||
| 65 | p = &(*p)->rb_left; | ||
| 66 | } | ||
| 67 | |||
| 68 | new = malloc(sizeof(struct hists)); | ||
| 69 | if (new == NULL) | ||
| 70 | return NULL; | ||
| 71 | memset(new, 0, sizeof(struct hists)); | ||
| 72 | new->event_stream = event_stream; | ||
| 73 | new->config = config; | ||
| 74 | new->type = type; | ||
| 75 | rb_link_node(&new->rb_node, parent, p); | ||
| 76 | rb_insert_color(&new->rb_node, &self->hists_tree); | ||
| 77 | return new; | ||
| 78 | } | ||
| 79 | |||
| 80 | static int perf_session__add_hist_entry(struct perf_session *self, | ||
| 81 | struct addr_location *al, | 52 | struct addr_location *al, |
| 82 | struct sample_data *data) | 53 | struct perf_sample *sample) |
| 83 | { | 54 | { |
| 84 | struct map_symbol *syms = NULL; | ||
| 85 | struct symbol *parent = NULL; | 55 | struct symbol *parent = NULL; |
| 86 | int err = -ENOMEM; | 56 | int err = 0; |
| 87 | struct hist_entry *he; | 57 | struct hist_entry *he; |
| 88 | struct hists *hists; | 58 | struct perf_evsel *evsel; |
| 89 | struct perf_event_attr *attr; | 59 | |
| 90 | 60 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | |
| 91 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { | 61 | err = perf_session__resolve_callchain(session, al->thread, |
| 92 | syms = perf_session__resolve_callchain(self, al->thread, | 62 | sample->callchain, &parent); |
| 93 | data->callchain, &parent); | 63 | if (err) |
| 94 | if (syms == NULL) | 64 | return err; |
| 95 | return -ENOMEM; | ||
| 96 | } | 65 | } |
| 97 | 66 | ||
| 98 | attr = perf_header__find_attr(data->id, &self->header); | 67 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
| 99 | if (attr) | 68 | if (evsel == NULL) { |
| 100 | hists = perf_session__hists_findnew(self, data->id, attr->type, attr->config); | 69 | /* |
| 101 | else | 70 | * FIXME: Propagate this back, but at least we're in a builtin, |
| 102 | hists = perf_session__hists_findnew(self, data->id, 0, 0); | 71 | * where exit() is allowed. ;-) |
| 103 | if (hists == NULL) | 72 | */ |
| 104 | goto out_free_syms; | 73 | ui__warning("Invalid %s file, contains samples with id %" PRIu64 " not in " |
| 105 | he = __hists__add_entry(hists, al, parent, data->period); | 74 | "its header!\n", input_name, sample->id); |
| 75 | exit_browser(0); | ||
| 76 | exit(1); | ||
| 77 | } | ||
| 78 | |||
| 79 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | ||
| 106 | if (he == NULL) | 80 | if (he == NULL) |
| 107 | goto out_free_syms; | 81 | return -ENOMEM; |
| 108 | err = 0; | 82 | |
| 109 | if (symbol_conf.use_callchain) { | 83 | if (symbol_conf.use_callchain) { |
| 110 | err = callchain_append(he->callchain, data->callchain, syms, | 84 | err = callchain_append(he->callchain, &session->callchain_cursor, |
| 111 | data->period); | 85 | sample->period); |
| 112 | if (err) | 86 | if (err) |
| 113 | goto out_free_syms; | 87 | return err; |
| 114 | } | 88 | } |
| 115 | /* | 89 | /* |
| 116 | * Only in the newt browser we are doing integrated annotation, | 90 | * Only in the newt browser we are doing integrated annotation, |
| 117 | * so we don't allocated the extra space needed because the stdio | 91 | * so we don't allocated the extra space needed because the stdio |
| 118 | * code will not use it. | 92 | * code will not use it. |
| 119 | */ | 93 | */ |
| 120 | if (use_browser > 0) | 94 | if (al->sym != NULL && use_browser > 0) { |
| 121 | err = hist_entry__inc_addr_samples(he, al->addr); | 95 | struct annotation *notes = symbol__annotation(he->ms.sym); |
| 122 | out_free_syms: | ||
| 123 | free(syms); | ||
| 124 | return err; | ||
| 125 | } | ||
| 126 | 96 | ||
| 127 | static int add_event_total(struct perf_session *session, | 97 | assert(evsel != NULL); |
| 128 | struct sample_data *data, | ||
| 129 | struct perf_event_attr *attr) | ||
| 130 | { | ||
| 131 | struct hists *hists; | ||
| 132 | 98 | ||
| 133 | if (attr) | 99 | err = -ENOMEM; |
| 134 | hists = perf_session__hists_findnew(session, data->id, | 100 | if (notes->src == NULL && |
| 135 | attr->type, attr->config); | 101 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) |
| 136 | else | 102 | goto out; |
| 137 | hists = perf_session__hists_findnew(session, data->id, 0, 0); | ||
| 138 | 103 | ||
| 139 | if (!hists) | 104 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
| 140 | return -ENOMEM; | 105 | } |
| 141 | 106 | ||
| 142 | hists->stats.total_period += data->period; | 107 | evsel->hists.stats.total_period += sample->period; |
| 143 | /* | 108 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
| 144 | * FIXME: add_event_total should be moved from here to | 109 | out: |
| 145 | * perf_session__process_event so that the proper hist is passed to | 110 | return err; |
| 146 | * the event_op methods. | ||
| 147 | */ | ||
| 148 | hists__inc_nr_events(hists, PERF_RECORD_SAMPLE); | ||
| 149 | session->hists.stats.total_period += data->period; | ||
| 150 | return 0; | ||
| 151 | } | 111 | } |
| 152 | 112 | ||
| 153 | static int process_sample_event(event_t *event, struct sample_data *sample, | 113 | |
| 114 | static int process_sample_event(union perf_event *event, | ||
| 115 | struct perf_sample *sample, | ||
| 154 | struct perf_session *session) | 116 | struct perf_session *session) |
| 155 | { | 117 | { |
| 156 | struct addr_location al; | 118 | struct addr_location al; |
| 157 | struct perf_event_attr *attr; | ||
| 158 | 119 | ||
| 159 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { | 120 | if (perf_event__preprocess_sample(event, session, &al, sample, |
| 121 | annotate_init) < 0) { | ||
| 160 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 122 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
| 161 | event->header.type); | 123 | event->header.type); |
| 162 | return -1; | 124 | return -1; |
| @@ -170,26 +132,17 @@ static int process_sample_event(event_t *event, struct sample_data *sample, | |||
| 170 | return -1; | 132 | return -1; |
| 171 | } | 133 | } |
| 172 | 134 | ||
| 173 | attr = perf_header__find_attr(sample->id, &session->header); | ||
| 174 | |||
| 175 | if (add_event_total(session, sample, attr)) { | ||
| 176 | pr_debug("problem adding event period\n"); | ||
| 177 | return -1; | ||
| 178 | } | ||
| 179 | |||
| 180 | return 0; | 135 | return 0; |
| 181 | } | 136 | } |
| 182 | 137 | ||
| 183 | static int process_read_event(event_t *event, struct sample_data *sample __used, | 138 | static int process_read_event(union perf_event *event, |
| 184 | struct perf_session *session __used) | 139 | struct perf_sample *sample __used, |
| 140 | struct perf_session *session) | ||
| 185 | { | 141 | { |
| 186 | struct perf_event_attr *attr; | 142 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, |
| 187 | 143 | event->read.id); | |
| 188 | attr = perf_header__find_attr(event->read.id, &session->header); | ||
| 189 | |||
| 190 | if (show_threads) { | 144 | if (show_threads) { |
| 191 | const char *name = attr ? __event_name(attr->type, attr->config) | 145 | const char *name = evsel ? event_name(evsel) : "unknown"; |
| 192 | : "unknown"; | ||
| 193 | perf_read_values_add_value(&show_threads_values, | 146 | perf_read_values_add_value(&show_threads_values, |
| 194 | event->read.pid, event->read.tid, | 147 | event->read.pid, event->read.tid, |
| 195 | event->read.id, | 148 | event->read.id, |
| @@ -198,7 +151,7 @@ static int process_read_event(event_t *event, struct sample_data *sample __used, | |||
| 198 | } | 151 | } |
| 199 | 152 | ||
| 200 | 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, |
| 201 | attr ? __event_name(attr->type, attr->config) : "FAIL", | 154 | evsel ? event_name(evsel) : "FAIL", |
| 202 | event->read.value); | 155 | event->read.value); |
| 203 | 156 | ||
| 204 | return 0; | 157 | return 0; |
| @@ -222,7 +175,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
| 222 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 175 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
| 223 | !symbol_conf.use_callchain) { | 176 | !symbol_conf.use_callchain) { |
| 224 | symbol_conf.use_callchain = true; | 177 | symbol_conf.use_callchain = true; |
| 225 | if (register_callchain_param(&callchain_param) < 0) { | 178 | if (callchain_register_param(&callchain_param) < 0) { |
| 226 | fprintf(stderr, "Can't register callchain" | 179 | fprintf(stderr, "Can't register callchain" |
| 227 | " params\n"); | 180 | " params\n"); |
| 228 | return -EINVAL; | 181 | return -EINVAL; |
| @@ -233,17 +186,17 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
| 233 | } | 186 | } |
| 234 | 187 | ||
| 235 | static struct perf_event_ops event_ops = { | 188 | static struct perf_event_ops event_ops = { |
| 236 | .sample = process_sample_event, | 189 | .sample = process_sample_event, |
| 237 | .mmap = event__process_mmap, | 190 | .mmap = perf_event__process_mmap, |
| 238 | .comm = event__process_comm, | 191 | .comm = perf_event__process_comm, |
| 239 | .exit = event__process_task, | 192 | .exit = perf_event__process_task, |
| 240 | .fork = event__process_task, | 193 | .fork = perf_event__process_task, |
| 241 | .lost = event__process_lost, | 194 | .lost = perf_event__process_lost, |
| 242 | .read = process_read_event, | 195 | .read = process_read_event, |
| 243 | .attr = event__process_attr, | 196 | .attr = perf_event__process_attr, |
| 244 | .event_type = event__process_event_type, | 197 | .event_type = perf_event__process_event_type, |
| 245 | .tracing_data = event__process_tracing_data, | 198 | .tracing_data = perf_event__process_tracing_data, |
| 246 | .build_id = event__process_build_id, | 199 | .build_id = perf_event__process_build_id, |
| 247 | .ordered_samples = true, | 200 | .ordered_samples = true, |
| 248 | .ordering_requires_timestamps = true, | 201 | .ordering_requires_timestamps = true, |
| 249 | }; | 202 | }; |
| @@ -269,21 +222,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
| 269 | return ret + fprintf(fp, "\n#\n"); | 222 | return ret + fprintf(fp, "\n#\n"); |
| 270 | } | 223 | } |
| 271 | 224 | ||
| 272 | static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | 225 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
| 226 | const char *help) | ||
| 273 | { | 227 | { |
| 274 | struct rb_node *next = rb_first(tree); | 228 | struct perf_evsel *pos; |
| 275 | 229 | ||
| 276 | while (next) { | 230 | list_for_each_entry(pos, &evlist->entries, node) { |
| 277 | struct hists *hists = rb_entry(next, struct hists, rb_node); | 231 | struct hists *hists = &pos->hists; |
| 278 | const char *evname = NULL; | 232 | const char *evname = NULL; |
| 279 | 233 | ||
| 280 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | 234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) |
| 281 | evname = __event_name(hists->type, hists->config); | 235 | evname = event_name(pos); |
| 282 | 236 | ||
| 283 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 237 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
| 284 | hists__fprintf(hists, NULL, false, stdout); | 238 | hists__fprintf(hists, NULL, false, stdout); |
| 285 | fprintf(stdout, "\n\n"); | 239 | fprintf(stdout, "\n\n"); |
| 286 | next = rb_next(&hists->rb_node); | ||
| 287 | } | 240 | } |
| 288 | 241 | ||
| 289 | if (sort_order == default_sort_order && | 242 | if (sort_order == default_sort_order && |
| @@ -304,8 +257,9 @@ static int hists__tty_browse_tree(struct rb_root *tree, const char *help) | |||
| 304 | static int __cmd_report(void) | 257 | static int __cmd_report(void) |
| 305 | { | 258 | { |
| 306 | int ret = -EINVAL; | 259 | int ret = -EINVAL; |
| 260 | u64 nr_samples; | ||
| 307 | struct perf_session *session; | 261 | struct perf_session *session; |
| 308 | struct rb_node *next; | 262 | struct perf_evsel *pos; |
| 309 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 263 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
| 310 | 264 | ||
| 311 | signal(SIGINT, sig_handler); | 265 | signal(SIGINT, sig_handler); |
| @@ -336,20 +290,24 @@ static int __cmd_report(void) | |||
| 336 | if (verbose > 2) | 290 | if (verbose > 2) |
| 337 | perf_session__fprintf_dsos(session, stdout); | 291 | perf_session__fprintf_dsos(session, stdout); |
| 338 | 292 | ||
| 339 | next = rb_first(&session->hists_tree); | 293 | nr_samples = 0; |
| 340 | while (next) { | 294 | list_for_each_entry(pos, &session->evlist->entries, node) { |
| 341 | struct hists *hists; | 295 | struct hists *hists = &pos->hists; |
| 342 | 296 | ||
| 343 | hists = rb_entry(next, struct hists, rb_node); | ||
| 344 | hists__collapse_resort(hists); | 297 | hists__collapse_resort(hists); |
| 345 | hists__output_resort(hists); | 298 | hists__output_resort(hists); |
| 346 | next = rb_next(&hists->rb_node); | 299 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 300 | } | ||
| 301 | |||
| 302 | if (nr_samples == 0) { | ||
| 303 | ui__warning("The %s file has no samples!\n", input_name); | ||
| 304 | goto out_delete; | ||
| 347 | } | 305 | } |
| 348 | 306 | ||
| 349 | if (use_browser > 0) | 307 | if (use_browser > 0) |
| 350 | hists__tui_browse_tree(&session->hists_tree, help); | 308 | perf_evlist__tui_browse_hists(session->evlist, help); |
| 351 | else | 309 | else |
| 352 | hists__tty_browse_tree(&session->hists_tree, help); | 310 | perf_evlist__tty_browse_hists(session->evlist, help); |
| 353 | 311 | ||
| 354 | out_delete: | 312 | out_delete: |
| 355 | /* | 313 | /* |
| @@ -424,7 +382,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 424 | if (tok2) | 382 | if (tok2) |
| 425 | callchain_param.print_limit = strtod(tok2, &endptr); | 383 | callchain_param.print_limit = strtod(tok2, &endptr); |
| 426 | setup: | 384 | setup: |
| 427 | if (register_callchain_param(&callchain_param) < 0) { | 385 | if (callchain_register_param(&callchain_param) < 0) { |
| 428 | fprintf(stderr, "Can't register callchain params\n"); | 386 | fprintf(stderr, "Can't register callchain params\n"); |
| 429 | return -1; | 387 | return -1; |
| 430 | } | 388 | } |
| @@ -498,7 +456,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 498 | use_browser = 1; | 456 | use_browser = 1; |
| 499 | 457 | ||
| 500 | if (strcmp(input_name, "-") != 0) | 458 | if (strcmp(input_name, "-") != 0) |
| 501 | setup_browser(); | 459 | setup_browser(true); |
| 502 | else | 460 | else |
| 503 | use_browser = 0; | 461 | use_browser = 0; |
| 504 | /* | 462 | /* |
| @@ -507,7 +465,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 507 | * implementation. | 465 | * implementation. |
| 508 | */ | 466 | */ |
| 509 | if (use_browser > 0) { | 467 | if (use_browser > 0) { |
| 510 | symbol_conf.priv_size = sizeof(struct sym_priv); | 468 | symbol_conf.priv_size = sizeof(struct annotation); |
| 469 | annotate_init = symbol__annotate_init; | ||
| 511 | /* | 470 | /* |
| 512 | * For searching by name on the "Browse map details". | 471 | * For searching by name on the "Browse map details". |
| 513 | * providing it only in verbose mode not to bloat too | 472 | * providing it only in verbose mode not to bloat too |
