#include "perf.h"
#include "util/debug.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
#include "util/annotate.h"
#include "util/event.h"
#include "util/parse-options.h"
static const char *sym_hist_filter;
-static int hists__add_entry(struct hists *self, struct addr_location *al)
+static int perf_evlist__add_sample(struct perf_evlist *evlist,
+ struct perf_sample *sample,
+ struct addr_location *al)
{
+ struct perf_evsel *evsel;
struct hist_entry *he;
+ int ret;
if (sym_hist_filter != NULL &&
(al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
return 0;
}
- he = __hists__add_entry(self, al, NULL, 1);
+ evsel = perf_evlist__id2evsel(evlist, sample->id);
+ if (evsel == NULL) {
+ /*
+ * FIXME: Propagate this back, but at least we're in a builtin,
+ * where exit() is allowed. ;-)
+ */
+ ui__warning("Invalid %s file, contains samples with id not in "
+ "its header!\n", input_name);
+ exit_browser(0);
+ exit(1);
+ }
+
+ he = __hists__add_entry(&evsel->hists, al, NULL, 1);
if (he == NULL)
return -ENOMEM;
+ ret = 0;
if (he->ms.sym != NULL) {
- /*
- * All aggregated on the first sym_hist.
- */
struct annotation *notes = symbol__annotation(he->ms.sym);
if (notes->src == NULL &&
- symbol__alloc_hist(he->ms.sym, 1) < 0)
+ symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0)
return -ENOMEM;
- return hist_entry__inc_addr_samples(he, 0, al->addr);
+ ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
}
- return 0;
+ evsel->hists.stats.total_period += sample->period;
+ hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+ return ret;
}
static int process_sample_event(union perf_event *event,
return -1;
}
- if (!al.filtered && hists__add_entry(&session->hists, &al)) {
+ if (!al.filtered && perf_evlist__add_sample(session->evlist, sample, &al)) {
pr_warning("problem incrementing symbol count, "
"skipping event\n");
return -1;
print_line, full_paths, 0, 0);
}
-static void hists__find_annotations(struct hists *self)
+static void hists__find_annotations(struct hists *self, int evidx)
{
struct rb_node *nd = rb_first(&self->entries), *next;
int key = KEY_RIGHT;
}
if (use_browser > 0) {
- /* For now all is aggregated on the first */
- key = hist_entry__tui_annotate(he, 0);
+ key = hist_entry__tui_annotate(he, evidx);
switch (key) {
case KEY_RIGHT:
next = rb_next(nd);
if (next != NULL)
nd = next;
} else {
- /* For now all is aggregated on the first */
- hist_entry__tty_annotate(he, 0);
+ hist_entry__tty_annotate(he, evidx);
nd = rb_next(nd);
/*
* Since we have a hist_entry per IP for the same
{
int ret;
struct perf_session *session;
+ struct perf_evsel *pos;
+ u64 total_nr_samples;
session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
if (session == NULL)
if (verbose > 2)
perf_session__fprintf_dsos(session, stdout);
- hists__collapse_resort(&session->hists);
- hists__output_resort(&session->hists);
- hists__find_annotations(&session->hists);
-out_delete:
- perf_session__delete(session);
+ total_nr_samples = 0;
+ list_for_each_entry(pos, &session->evlist->entries, node) {
+ struct hists *hists = &pos->hists;
+ u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
+
+ if (nr_samples > 0) {
+ total_nr_samples += nr_samples;
+ hists__collapse_resort(hists);
+ hists__output_resort(hists);
+ hists__find_annotations(hists, pos->idx);
+ }
+ }
+ if (total_nr_samples == 0) {
+ ui__warning("The %s file has no samples!\n", input_name);
+ goto out_delete;
+ }
+out_delete:
+ /*
+ * Speed up the exit process, for large files this can
+ * take quite a while.
+ *
+ * XXX Enable this when using valgrind or if we ever
+ * librarize this command.
+ *
+ * Also experiment with obstacks to see how much speed
+ * up we'll get here.
+ *
+ * perf_session__delete(session);
+ */
return ret;
}
#include "perf.h"
#include "util/debug.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
#include "util/header.h"
#include "util/session.h"
static char callchain_default_opt[] = "fractal,0.5";
static symbol_filter_t annotate_init;
-static struct hists *perf_session__hists_findnew(struct perf_session *self,
- u64 event_stream, u32 type,
- u64 config)
-{
- struct rb_node **p = &self->hists_tree.rb_node;
- struct rb_node *parent = NULL;
- struct hists *iter, *new;
-
- while (*p != NULL) {
- parent = *p;
- iter = rb_entry(parent, struct hists, 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 hists));
- if (new == NULL)
- return NULL;
- memset(new, 0, sizeof(struct hists));
- 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->hists_tree);
- return new;
-}
-
static int perf_session__add_hist_entry(struct perf_session *session,
struct addr_location *al,
struct perf_sample *sample)
struct symbol *parent = NULL;
int err = 0;
struct hist_entry *he;
- struct hists *hists;
- struct perf_event_attr *attr;
+ struct perf_evsel *evsel;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
err = perf_session__resolve_callchain(session, al->thread,
return err;
}
- attr = perf_header__find_attr(sample->id, &session->header);
- if (attr)
- hists = perf_session__hists_findnew(session, sample->id, attr->type, attr->config);
- else
- hists = perf_session__hists_findnew(session, sample->id, 0, 0);
- if (hists == NULL)
- return -ENOMEM;
+ evsel = perf_evlist__id2evsel(session->evlist, sample->id);
+ if (evsel == NULL) {
+ /*
+ * FIXME: Propagate this back, but at least we're in a builtin,
+ * where exit() is allowed. ;-)
+ */
+ ui__warning("Invalid %s file, contains samples with id not in "
+ "its header!\n", input_name);
+ exit_browser(0);
+ exit(1);
+ }
- he = __hists__add_entry(hists, al, parent, sample->period);
+ he = __hists__add_entry(&evsel->hists, al, parent, sample->period);
if (he == NULL)
return -ENOMEM;
* code will not use it.
*/
if (al->sym != NULL && use_browser > 0) {
- /*
- * All aggregated on the first sym_hist.
- */
struct annotation *notes = symbol__annotation(he->ms.sym);
+
+ assert(evsel != NULL);
+
+ err = -ENOMEM;
if (notes->src == NULL &&
- symbol__alloc_hist(he->ms.sym, 1) < 0)
- err = -ENOMEM;
- else
- err = hist_entry__inc_addr_samples(he, 0, al->addr);
+ symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0)
+ goto out;
+
+ err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
}
+ evsel->hists.stats.total_period += sample->period;
+ hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
+out:
return err;
}
-static int add_event_total(struct perf_session *session,
- struct perf_sample *sample,
- struct perf_event_attr *attr)
-{
- struct hists *hists;
-
- if (attr)
- hists = perf_session__hists_findnew(session, sample->id,
- attr->type, attr->config);
- else
- hists = perf_session__hists_findnew(session, sample->id, 0, 0);
-
- if (!hists)
- return -ENOMEM;
-
- hists->stats.total_period += sample->period;
- /*
- * FIXME: add_event_total should be moved from here to
- * perf_session__process_event so that the proper hist is passed to
- * the event_op methods.
- */
- hists__inc_nr_events(hists, PERF_RECORD_SAMPLE);
- session->hists.stats.total_period += sample->period;
- return 0;
-}
static int process_sample_event(union perf_event *event,
struct perf_sample *sample,
struct perf_session *session)
{
struct addr_location al;
- struct perf_event_attr *attr;
if (perf_event__preprocess_sample(event, session, &al, sample,
annotate_init) < 0) {
return -1;
}
- attr = perf_header__find_attr(sample->id, &session->header);
-
- if (add_event_total(session, sample, attr)) {
- pr_debug("problem adding event period\n");
- return -1;
- }
-
return 0;
}
static int process_read_event(union perf_event *event,
struct perf_sample *sample __used,
- struct perf_session *session __used)
+ struct perf_session *session)
{
- struct perf_event_attr *attr;
-
- attr = perf_header__find_attr(event->read.id, &session->header);
-
+ struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist,
+ event->read.id);
if (show_threads) {
- const char *name = attr ? __event_name(attr->type, attr->config)
- : "unknown";
+ const char *name = evsel ? event_name(evsel) : "unknown";
perf_read_values_add_value(&show_threads_values,
event->read.pid, event->read.tid,
event->read.id,
}
dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid,
- attr ? __event_name(attr->type, attr->config) : "FAIL",
+ evsel ? event_name(evsel) : "FAIL",
event->read.value);
return 0;
return ret + fprintf(fp, "\n#\n");
}
-static int hists__tty_browse_tree(struct rb_root *tree, const char *help)
+static int hists__tty_browse_tree(struct perf_evlist *evlist, const char *help)
{
- struct rb_node *next = rb_first(tree);
+ struct perf_evsel *pos;
- while (next) {
- struct hists *hists = rb_entry(next, struct hists, rb_node);
+ list_for_each_entry(pos, &evlist->entries, node) {
+ struct hists *hists = &pos->hists;
const char *evname = NULL;
if (rb_first(&hists->entries) != rb_last(&hists->entries))
- evname = __event_name(hists->type, hists->config);
+ evname = event_name(pos);
hists__fprintf_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, NULL, false, stdout);
fprintf(stdout, "\n\n");
- next = rb_next(&hists->rb_node);
}
if (sort_order == default_sort_order &&
static int __cmd_report(void)
{
int ret = -EINVAL;
+ u64 nr_samples;
struct perf_session *session;
- struct rb_node *next;
+ struct perf_evsel *pos;
const char *help = "For a higher level overview, try: perf report --sort comm,dso";
signal(SIGINT, sig_handler);
if (verbose > 2)
perf_session__fprintf_dsos(session, stdout);
- next = rb_first(&session->hists_tree);
-
- if (next == NULL) {
- ui__warning("The %s file has no samples!\n", input_name);
- goto out_delete;
- }
-
- while (next) {
- struct hists *hists;
+ nr_samples = 0;
+ list_for_each_entry(pos, &session->evlist->entries, node) {
+ struct hists *hists = &pos->hists;
- hists = rb_entry(next, struct hists, rb_node);
hists__collapse_resort(hists);
hists__output_resort(hists);
- next = rb_next(&hists->rb_node);
+ nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
+ }
+
+ if (nr_samples == 0) {
+ ui__warning("The %s file has no samples!\n", input_name);
+ goto out_delete;
}
if (use_browser > 0)
- hists__tui_browse_tree(&session->hists_tree, help, 0);
+ hists__tui_browse_tree(session->evlist, help);
else
- hists__tty_browse_tree(&session->hists_tree, help);
+ hists__tty_browse_tree(session->evlist, help);
out_delete:
/*
#include "types.h"
#include "xyarray.h"
#include "cgroup.h"
+#include "hist.h"
struct perf_counts_values {
union {
struct xyarray *id;
struct perf_counts *counts;
int idx;
+ struct hists hists;
char *name;
void *priv;
struct cgroup_sel *cgrp;
return value;
}
-struct perf_event_attr *
-perf_header__find_attr(u64 id, struct perf_header *header)
-{
- int i;
-
- /*
- * We set id to -1 if the data file doesn't contain sample
- * ids. This can happen when the data file contains one type
- * of event and in that case, the header can still store the
- * event attribute information. Check for this and avoid
- * walking through the entire list of ids which may be large.
- */
- if (id == -1ULL) {
- if (header->attrs > 0)
- return &header->attr[0]->attr;
- return NULL;
- }
-
- for (i = 0; i < header->attrs; i++) {
- struct perf_header_attr *attr = header->attr[i];
- int j;
-
- for (j = 0; j < attr->ids; j++) {
- if (attr->id[j] == id)
- return &attr->attr;
- }
- }
-
- return NULL;
-}
-
int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
perf_event__handler_t process,
struct perf_session *session)
u64 perf_header__sample_type(struct perf_header *header);
bool perf_header__sample_id_all(const struct perf_header *header);
-struct perf_event_attr *
-perf_header__find_attr(u64 id, struct perf_header *header);
void perf_header__set_feat(struct perf_header *self, int feat);
void perf_header__clear_feat(struct perf_header *self, int feat);
bool perf_header__has_feat(const struct perf_header *self, int feat);
size_t ret = 0;
for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
- const char *name = perf_event__name(i);
+ const char *name;
+ if (self->stats.nr_events[i] == 0)
+ continue;
+
+ name = perf_event__name(i);
if (!strcmp(name, "UNKNOWN"))
continue;
};
struct hists {
- struct rb_node rb_node;
struct rb_root entries;
u64 nr_entries;
struct events_stats stats;
- u64 config;
u64 event_stream;
- u32 type;
u16 col_len[HISTC_NR_COLS];
/* Best would be to reuse the session callchain cursor */
struct callchain_cursor callchain_cursor;
void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
+struct perf_evlist;
+
#ifdef NO_NEWT_SUPPORT
static inline int hists__browse(struct hists *self __used,
const char *helpline __used,
return 0;
}
-static inline int hists__tui_browse_tree(struct rb_root *self __used,
- const char *help __used,
- int evidx __used)
+static inline int hists__tui_browse_tree(struct perf_evlist *evlist __used,
+ const char *help __used)
{
return 0;
}
#define KEY_LEFT NEWT_KEY_LEFT
#define KEY_RIGHT NEWT_KEY_RIGHT
-int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx);
+int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help);
#endif
unsigned int hists__sort_list_width(struct hists *self);
#include <sys/types.h>
#include <sys/mman.h>
+#include "evlist.h"
+#include "evsel.h"
#include "session.h"
#include "sort.h"
#include "util.h"
+static int perf_session__read_evlist(struct perf_session *session)
+{
+ int i, j;
+
+ session->evlist = perf_evlist__new(NULL, NULL);
+ if (session->evlist == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < session->header.attrs; ++i) {
+ struct perf_header_attr *hattr = session->header.attr[i];
+ struct perf_evsel *evsel = perf_evsel__new(&hattr->attr, i);
+
+ if (evsel == NULL)
+ goto out_delete_evlist;
+ /*
+ * Do it before so that if perf_evsel__alloc_id fails, this
+ * entry gets purged too at perf_evlist__delete().
+ */
+ perf_evlist__add(session->evlist, evsel);
+ /*
+ * We don't have the cpu and thread maps on the header, so
+ * for allocating the perf_sample_id table we fake 1 cpu and
+ * hattr->ids threads.
+ */
+ if (perf_evsel__alloc_id(evsel, 1, hattr->ids))
+ goto out_delete_evlist;
+
+ for (j = 0; j < hattr->ids; ++j)
+ perf_evlist__id_hash(session->evlist, evsel, 0, j,
+ hattr->id[j]);
+ }
+
+ return 0;
+
+out_delete_evlist:
+ perf_evlist__delete(session->evlist);
+ session->evlist = NULL;
+ return -ENOMEM;
+}
+
static int perf_session__open(struct perf_session *self, bool force)
{
struct stat input_stat;
goto out_close;
}
+ if (perf_session__read_evlist(self) < 0) {
+ pr_err("Not enough memory to read the event selector list\n");
+ goto out_close;
+ }
+
self->size = input_stat.st_size;
return 0;
memcpy(self->filename, filename, len);
self->threads = RB_ROOT;
INIT_LIST_HEAD(&self->dead_threads);
- self->hists_tree = RB_ROOT;
self->last_match = NULL;
/*
* On 64bit we can mmap the data file in one go. No need for tiny mmap
size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits);
return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits);
}
+
+size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
+{
+ struct perf_evsel *pos;
+ size_t ret = fprintf(fp, "Aggregated stats:\n");
+
+ ret += hists__fprintf_nr_events(&session->hists, fp);
+
+ list_for_each_entry(pos, &session->evlist->entries, node) {
+ ret += fprintf(fp, "%s stats:\n", event_name(pos));
+ ret += hists__fprintf_nr_events(&pos->hists, fp);
+ }
+
+ return ret;
+}
struct thread *last_match;
struct machine host_machine;
struct rb_root machines;
- struct rb_root hists_tree;
+ struct perf_evlist *evlist;
/*
- * FIXME: should point to the first entry in hists_tree and
- * be a hists instance. Right now its only 'report'
- * that is using ->hists_tree while all the rest use
- * ->hists.
+ * FIXME: Need to split this up further, we need global
+ * stats + per event stats. 'perf diff' also needs
+ * to properly support multiple events in a single
+ * perf.data file.
*/
struct hists hists;
u64 sample_type;
size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
FILE *fp, bool with_hits);
-static inline
-size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp)
-{
- return hists__fprintf_nr_events(&self->hists, fp);
-}
+size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
static inline int perf_session__parse_sample(struct perf_session *session,
const union perf_event *event,
#include <newt.h>
#include <linux/rbtree.h>
+#include "../../evsel.h"
+#include "../../evlist.h"
#include "../../hist.h"
#include "../../pstack.h"
#include "../../sort.h"
return key;
}
-int hists__tui_browse_tree(struct rb_root *self, const char *help, int evidx)
+int hists__tui_browse_tree(struct perf_evlist *evlist, const char *help)
{
- struct rb_node *first = rb_first(self), *nd = first, *next;
- int key = 0;
+ struct perf_evsel *pos;
- while (nd) {
- struct hists *hists = rb_entry(nd, struct hists, rb_node);
- const char *ev_name = __event_name(hists->type, hists->config);
+ pos = list_entry(evlist->entries.next, struct perf_evsel, node);
+ while (pos) {
+ struct hists *hists = &pos->hists;
+ const char *ev_name = event_name(pos);
+ int key = hists__browse(hists, help, ev_name, pos->idx);
- key = hists__browse(hists, help, ev_name, evidx);
switch (key) {
case NEWT_KEY_TAB:
- next = rb_next(nd);
- if (next)
- nd = next;
+ if (pos->node.next == &evlist->entries)
+ pos = list_entry(evlist->entries.next, struct perf_evsel, node);
+ else
+ pos = list_entry(pos->node.next, struct perf_evsel, node);
break;
case NEWT_KEY_UNTAB:
- if (nd == first)
- continue;
- nd = rb_prev(nd);
+ if (pos->node.prev == &evlist->entries)
+ pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
+ else
+ pos = list_entry(pos->node.prev, struct perf_evsel, node);
break;
default:
return key;
}
}
- return key;
+ return 0;
}