linux/tools/perf/util/hist.c

210 lines
3.9 KiB
C

#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;
}