|
@@ -177,8 +177,10 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
|
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
|
|
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
|
|
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
|
|
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
|
|
|
|
|
|
- if (h->srcline)
|
|
|
|
- hists__new_col_len(hists, HISTC_SRCLINE, strlen(h->srcline));
|
|
|
|
|
|
+ if (h->srcline) {
|
|
|
|
+ len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header));
|
|
|
|
+ hists__new_col_len(hists, HISTC_SRCLINE, len);
|
|
|
|
+ }
|
|
|
|
|
|
if (h->srcfile)
|
|
if (h->srcfile)
|
|
hists__new_col_len(hists, HISTC_SRCFILE, strlen(h->srcfile));
|
|
hists__new_col_len(hists, HISTC_SRCFILE, strlen(h->srcfile));
|
|
@@ -417,6 +419,8 @@ static int hist_entry__init(struct hist_entry *he,
|
|
}
|
|
}
|
|
INIT_LIST_HEAD(&he->pairs.node);
|
|
INIT_LIST_HEAD(&he->pairs.node);
|
|
thread__get(he->thread);
|
|
thread__get(he->thread);
|
|
|
|
+ he->hroot_in = RB_ROOT;
|
|
|
|
+ he->hroot_out = RB_ROOT;
|
|
|
|
|
|
if (!symbol_conf.report_hierarchy)
|
|
if (!symbol_conf.report_hierarchy)
|
|
he->leaf = true;
|
|
he->leaf = true;
|
|
@@ -2149,6 +2153,50 @@ out:
|
|
return he;
|
|
return he;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct hist_entry *add_dummy_hierarchy_entry(struct hists *hists,
|
|
|
|
+ struct rb_root *root,
|
|
|
|
+ struct hist_entry *pair)
|
|
|
|
+{
|
|
|
|
+ struct rb_node **p;
|
|
|
|
+ struct rb_node *parent = NULL;
|
|
|
|
+ struct hist_entry *he;
|
|
|
|
+ struct perf_hpp_fmt *fmt;
|
|
|
|
+
|
|
|
|
+ p = &root->rb_node;
|
|
|
|
+ while (*p != NULL) {
|
|
|
|
+ int64_t cmp = 0;
|
|
|
|
+
|
|
|
|
+ parent = *p;
|
|
|
|
+ he = rb_entry(parent, struct hist_entry, rb_node_in);
|
|
|
|
+
|
|
|
|
+ perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
|
|
|
+ cmp = fmt->collapse(fmt, he, pair);
|
|
|
|
+ if (cmp)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (!cmp)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if (cmp < 0)
|
|
|
|
+ p = &parent->rb_left;
|
|
|
|
+ else
|
|
|
|
+ p = &parent->rb_right;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ he = hist_entry__new(pair, true);
|
|
|
|
+ if (he) {
|
|
|
|
+ rb_link_node(&he->rb_node_in, parent, p);
|
|
|
|
+ rb_insert_color(&he->rb_node_in, root);
|
|
|
|
+
|
|
|
|
+ he->dummy = true;
|
|
|
|
+ he->hists = hists;
|
|
|
|
+ memset(&he->stat, 0, sizeof(he->stat));
|
|
|
|
+ hists__inc_stats(hists, he);
|
|
|
|
+ }
|
|
|
|
+out:
|
|
|
|
+ return he;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct hist_entry *hists__find_entry(struct hists *hists,
|
|
static struct hist_entry *hists__find_entry(struct hists *hists,
|
|
struct hist_entry *he)
|
|
struct hist_entry *he)
|
|
{
|
|
{
|
|
@@ -2174,6 +2222,51 @@ static struct hist_entry *hists__find_entry(struct hists *hists,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static struct hist_entry *hists__find_hierarchy_entry(struct rb_root *root,
|
|
|
|
+ struct hist_entry *he)
|
|
|
|
+{
|
|
|
|
+ struct rb_node *n = root->rb_node;
|
|
|
|
+
|
|
|
|
+ while (n) {
|
|
|
|
+ struct hist_entry *iter;
|
|
|
|
+ struct perf_hpp_fmt *fmt;
|
|
|
|
+ int64_t cmp = 0;
|
|
|
|
+
|
|
|
|
+ iter = rb_entry(n, struct hist_entry, rb_node_in);
|
|
|
|
+ perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
|
|
|
+ cmp = fmt->collapse(fmt, iter, he);
|
|
|
|
+ if (cmp)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (cmp < 0)
|
|
|
|
+ n = n->rb_left;
|
|
|
|
+ else if (cmp > 0)
|
|
|
|
+ n = n->rb_right;
|
|
|
|
+ else
|
|
|
|
+ return iter;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hists__match_hierarchy(struct rb_root *leader_root,
|
|
|
|
+ struct rb_root *other_root)
|
|
|
|
+{
|
|
|
|
+ struct rb_node *nd;
|
|
|
|
+ struct hist_entry *pos, *pair;
|
|
|
|
+
|
|
|
|
+ for (nd = rb_first(leader_root); nd; nd = rb_next(nd)) {
|
|
|
|
+ pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
|
|
|
+ pair = hists__find_hierarchy_entry(other_root, pos);
|
|
|
|
+
|
|
|
|
+ if (pair) {
|
|
|
|
+ hist_entry__add_pair(pair, pos);
|
|
|
|
+ hists__match_hierarchy(&pos->hroot_in, &pair->hroot_in);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Look for pairs to link to the leader buckets (hist_entries):
|
|
* Look for pairs to link to the leader buckets (hist_entries):
|
|
*/
|
|
*/
|
|
@@ -2183,6 +2276,12 @@ void hists__match(struct hists *leader, struct hists *other)
|
|
struct rb_node *nd;
|
|
struct rb_node *nd;
|
|
struct hist_entry *pos, *pair;
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
|
|
|
+ if (symbol_conf.report_hierarchy) {
|
|
|
|
+ /* hierarchy report always collapses entries */
|
|
|
|
+ return hists__match_hierarchy(&leader->entries_collapsed,
|
|
|
|
+ &other->entries_collapsed);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (hists__has(leader, need_collapse))
|
|
if (hists__has(leader, need_collapse))
|
|
root = &leader->entries_collapsed;
|
|
root = &leader->entries_collapsed;
|
|
else
|
|
else
|
|
@@ -2197,6 +2296,50 @@ void hists__match(struct hists *leader, struct hists *other)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int hists__link_hierarchy(struct hists *leader_hists,
|
|
|
|
+ struct hist_entry *parent,
|
|
|
|
+ struct rb_root *leader_root,
|
|
|
|
+ struct rb_root *other_root)
|
|
|
|
+{
|
|
|
|
+ struct rb_node *nd;
|
|
|
|
+ struct hist_entry *pos, *leader;
|
|
|
|
+
|
|
|
|
+ for (nd = rb_first(other_root); nd; nd = rb_next(nd)) {
|
|
|
|
+ pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
|
|
|
+
|
|
|
|
+ if (hist_entry__has_pairs(pos)) {
|
|
|
|
+ bool found = false;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(leader, &pos->pairs.head, pairs.node) {
|
|
|
|
+ if (leader->hists == leader_hists) {
|
|
|
|
+ found = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (!found)
|
|
|
|
+ return -1;
|
|
|
|
+ } else {
|
|
|
|
+ leader = add_dummy_hierarchy_entry(leader_hists,
|
|
|
|
+ leader_root, pos);
|
|
|
|
+ if (leader == NULL)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ /* do not point parent in the pos */
|
|
|
|
+ leader->parent_he = parent;
|
|
|
|
+
|
|
|
|
+ hist_entry__add_pair(pos, leader);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!pos->leaf) {
|
|
|
|
+ if (hists__link_hierarchy(leader_hists, leader,
|
|
|
|
+ &leader->hroot_in,
|
|
|
|
+ &pos->hroot_in) < 0)
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Look for entries in the other hists that are not present in the leader, if
|
|
* Look for entries in the other hists that are not present in the leader, if
|
|
* we find them, just add a dummy entry on the leader hists, with period=0,
|
|
* we find them, just add a dummy entry on the leader hists, with period=0,
|
|
@@ -2208,6 +2351,13 @@ int hists__link(struct hists *leader, struct hists *other)
|
|
struct rb_node *nd;
|
|
struct rb_node *nd;
|
|
struct hist_entry *pos, *pair;
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
|
|
|
+ if (symbol_conf.report_hierarchy) {
|
|
|
|
+ /* hierarchy report always collapses entries */
|
|
|
|
+ return hists__link_hierarchy(leader, NULL,
|
|
|
|
+ &leader->entries_collapsed,
|
|
|
|
+ &other->entries_collapsed);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (hists__has(other, need_collapse))
|
|
if (hists__has(other, need_collapse))
|
|
root = &other->entries_collapsed;
|
|
root = &other->entries_collapsed;
|
|
else
|
|
else
|