#include "hist.h" #include "session.h" #include "sort.h" struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_REL, .min_percent = 0.5 }; /* * histogram, sorted on item, collects counts */ struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, struct addr_location *al, struct symbol *sym_parent, u64 count, bool *hit) { struct rb_node **p = &self->hists.rb_node; struct rb_node *parent = NULL; struct hist_entry *he; struct hist_entry entry = { .thread = al->thread, .map = al->map, .sym = al->sym, .ip = al->addr, .level = al->level, .count = count, .parent = sym_parent, }; int cmp; while (*p != NULL) { parent = *p; he = rb_entry(parent, struct hist_entry, rb_node); cmp = hist_entry__cmp(&entry, he); if (!cmp) { *hit = true; return he; } if (cmp < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } he = malloc(sizeof(*he)); if (!he) return NULL; *he = entry; rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, &self->hists); *hit = false; return he; } int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) { struct sort_entry *se; int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { cmp = se->cmp(left, right); if (cmp) break; } return cmp; } int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) { struct sort_entry *se; int64_t cmp = 0; list_for_each_entry(se, &hist_entry__sort_list, list) { int64_t (*f)(struct hist_entry *, struct hist_entry *); f = se->collapse ?: se->cmp; cmp = f(left, right); if (cmp) break; } return cmp; } void hist_entry__free(struct hist_entry *he) { free(he); } /* * collapse the histogram */ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; int64_t cmp; while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); cmp = hist_entry__collapse(iter, he); if (!cmp) { iter->count += he->count; hist_entry__free(he); return; } if (cmp < 0) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); } void perf_session__collapse_resort(struct perf_session *self) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; if (!sort__need_collapse) return; tmp = RB_ROOT; next = rb_first(&self->hists); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->hists); collapse__insert_entry(&tmp, n); } self->hists = tmp; } /* * reverse the map, sort on count. */ static void perf_session__insert_output_hist_entry(struct perf_session *self, struct rb_root *root, struct hist_entry *he, u64 min_callchain_hits) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; if (self->use_callchain) callchain_param.sort(&he->sorted_chain, &he->callchain, min_callchain_hits, &callchain_param); while (*p != NULL) { parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); if (he->count > iter->count) p = &(*p)->rb_left; else p = &(*p)->rb_right; } rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); } void perf_session__output_resort(struct perf_session *self, u64 total_samples) { struct rb_root tmp; struct rb_node *next; struct hist_entry *n; u64 min_callchain_hits; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); tmp = RB_ROOT; next = rb_first(&self->hists); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &self->hists); perf_session__insert_output_hist_entry(self, &tmp, n, min_callchain_hits); } self->hists = tmp; }