forked from Minki/linux
4e4f06e4c8
As we'll need to sort multiple times for multiple perf sessions, so that we can then do a diff. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Frédéric Weisbecker <fweisbec@gmail.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Paul Mackerras <paulus@samba.org> LKML-Reference: <1260803439-16783-1-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
210 lines
3.9 KiB
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;
|
|
}
|