#include "util/cache.h"
#include <linux/rbtree.h>
#include "util/symbol.h"
-#include "util/string.h"
#include "util/callchain.h"
#include "util/strlist.h"
#include "util/values.h"
static char callchain_default_opt[] = "fractal,0.5";
+static struct event_stat_id *get_stats(struct perf_session *self,
+ u64 event_stream, u32 type, u64 config)
+{
+ struct rb_node **p = &self->stats_by_id.rb_node;
+ struct rb_node *parent = NULL;
+ struct event_stat_id *iter, *new;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct event_stat_id, rb_node);
+ if (iter->config == config)
+ return iter;
+
+
+ if (config > iter->config)
+ p = &(*p)->rb_right;
+ else
+ p = &(*p)->rb_left;
+ }
+
+ new = malloc(sizeof(struct event_stat_id));
+ if (new == NULL)
+ return NULL;
+ memset(new, 0, sizeof(struct event_stat_id));
+ new->event_stream = event_stream;
+ new->config = config;
+ new->type = type;
+ rb_link_node(&new->rb_node, parent, p);
+ rb_insert_color(&new->rb_node, &self->stats_by_id);
+ return new;
+}
+
static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al,
- struct ip_callchain *chain, u64 count)
+ struct sample_data *data)
{
- struct symbol **syms = NULL, *parent = NULL;
+ struct map_symbol *syms = NULL;
+ struct symbol *parent = NULL;
bool hit;
+ int err;
struct hist_entry *he;
+ struct event_stat_id *stats;
+ struct perf_event_attr *attr;
- if ((sort__has_parent || symbol_conf.use_callchain) && chain)
+ if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) {
syms = perf_session__resolve_callchain(self, al->thread,
- chain, &parent);
- he = __perf_session__add_hist_entry(self, al, parent, count, &hit);
+ data->callchain, &parent);
+ if (syms == NULL)
+ return -ENOMEM;
+ }
+
+ attr = perf_header__find_attr(data->id, &self->header);
+ if (attr)
+ stats = get_stats(self, data->id, attr->type, attr->config);
+ else
+ stats = get_stats(self, data->id, 0, 0);
+ if (stats == NULL)
+ return -ENOMEM;
+ he = __perf_session__add_hist_entry(&stats->hists, al, parent,
+ data->period, &hit);
if (he == NULL)
return -ENOMEM;
if (hit)
- he->count += count;
+ he->count += data->period;
if (symbol_conf.use_callchain) {
if (!hit)
- callchain_init(&he->callchain);
- append_chain(&he->callchain, chain, syms);
+ callchain_init(he->callchain);
+ err = append_chain(he->callchain, data->callchain, syms);
free(syms);
+
+ if (err)
+ return err;
}
return 0;
return 0;
}
+static int add_event_total(struct perf_session *session,
+ struct sample_data *data,
+ struct perf_event_attr *attr)
+{
+ struct event_stat_id *stats;
+
+ if (attr)
+ stats = get_stats(session, data->id, attr->type, attr->config);
+ else
+ stats = get_stats(session, data->id, 0, 0);
+
+ if (!stats)
+ return -ENOMEM;
+
+ stats->stats.total += data->period;
+ session->events_stats.total += data->period;
+ return 0;
+}
+
static int process_sample_event(event_t *event, struct perf_session *session)
{
struct sample_data data = { .period = 1, };
struct addr_location al;
+ struct perf_event_attr *attr;
event__parse_sample(event, session->sample_type, &data);
if (al.filtered || (hide_unresolved && al.sym == NULL))
return 0;
- if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) {
+ if (perf_session__add_hist_entry(session, &al, &data)) {
pr_debug("problem incrementing symbol count, skipping event\n");
return -1;
}
- session->events_stats.total += data.period;
+ attr = perf_header__find_attr(data.id, &session->header);
+
+ if (add_event_total(session, &data, attr)) {
+ pr_debug("problem adding event count\n");
+ return -1;
+ }
+
return 0;
}
{
int ret = -EINVAL;
struct perf_session *session;
+ struct rb_node *next;
+ const char *help = "For a higher level overview, try: perf report --sort comm,dso";
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
if (verbose > 2)
dsos__fprintf(stdout);
- perf_session__collapse_resort(session);
- perf_session__output_resort(session, session->events_stats.total);
- fprintf(stdout, "# Samples: %Ld\n#\n", session->events_stats.total);
- perf_session__fprintf_hists(session, NULL, false, stdout);
- if (sort_order == default_sort_order &&
- parent_pattern == default_parent_pattern)
- fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n");
+ next = rb_first(&session->stats_by_id);
+ while (next) {
+ struct event_stat_id *stats;
+ u64 nr_hists;
+
+ stats = rb_entry(next, struct event_stat_id, rb_node);
+ perf_session__collapse_resort(&stats->hists);
+ nr_hists = perf_session__output_resort(&stats->hists,
+ stats->stats.total);
+ if (use_browser)
+ perf_session__browse_hists(&stats->hists, nr_hists,
+ stats->stats.total, help,
+ input_name);
+ else {
+ if (rb_first(&session->stats_by_id) ==
+ rb_last(&session->stats_by_id))
+ fprintf(stdout, "# Samples: %Ld\n#\n",
+ stats->stats.total);
+ else
+ fprintf(stdout, "# Samples: %Ld %s\n#\n",
+ stats->stats.total,
+ __event_name(stats->type, stats->config));
+
+ perf_session__fprintf_hists(&stats->hists, NULL, false, stdout,
+ stats->stats.total);
+ fprintf(stdout, "\n\n");
+ }
- if (show_threads) {
- bool raw_printing_style = !strcmp(pretty_printing_style, "raw");
- perf_read_values_display(stdout, &show_threads_values,
- raw_printing_style);
- perf_read_values_destroy(&show_threads_values);
+ next = rb_next(&stats->rb_node);
+ }
+
+ if (!use_browser && sort_order == default_sort_order &&
+ parent_pattern == default_parent_pattern) {
+ fprintf(stdout, "#\n# (%s)\n#\n", help);
+
+ if (show_threads) {
+ bool style = !strcmp(pretty_printing_style, "raw");
+ perf_read_values_display(stdout, &show_threads_values,
+ style);
+ perf_read_values_destroy(&show_threads_values);
+ }
}
out_delete:
perf_session__delete(session);
{
argc = parse_options(argc, argv, options, report_usage, 0);
- setup_pager();
+ setup_browser();
if (symbol__init() < 0)
return -1;
setup_sorting(report_usage, options);
if (parent_pattern != default_parent_pattern) {
- sort_dimension__add("parent");
+ if (sort_dimension__add("parent") < 0)
+ return -1;
sort_parent.elide = 1;
} else
symbol_conf.exclude_other = false;