#include "hist.h" struct rb_root hist; struct rb_root collapse_hists; struct rb_root output_hists; int callchain; struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_REL, .min_percent = 0.5 }; /* * histogram, sorted on item, collects counts */ struct hist_entry *__hist_entry__add(struct addr_location *al, struct symbol *sym_parent, u64 count, bool *hit) { struct rb_node **p = &hist.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, &hist); *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 */ void collapse__insert_entry(struct hist_entry *he) { struct rb_node **p = &collapse_hists.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, &collapse_hists); } void collapse__resort(void) { struct rb_node *next; struct hist_entry *n; if (!sort__need_collapse) return; next = rb_first(&hist); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, &hist); collapse__insert_entry(n); } } /* * reverse the map, sort on count. */ void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) { struct rb_node **p = &output_hists.rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; if (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, &output_hists); } void output__resort(u64 total_samples) { struct rb_node *next; struct hist_entry *n; struct rb_root *tree = &hist; u64 min_callchain_hits; min_callchain_hits = total_samples * (callchain_param.min_percent / 100); if (sort__need_collapse) tree = &collapse_hists; next = rb_first(tree); while (next) { n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); rb_erase(&n->rb_node, tree); output__insert_entry(n, min_callchain_hits); } }