2013-05-21 06:14:42

by Namhyung Kim

[permalink] [raw]
Subject: [RFC 0/7] perf report/gtk: Add support for hierarchy view

Hi guys,

This patchset implements a new feature that collects hist entries in a
hierachical manner. That means lower-level entries are belonged to an
upper-level entry. The entry hierachy is built on the sort keys
given, so users can set it whatever they want. It only shows
top-level entries first, and user can expand/collapse it dynamically.

This is only implemented in --gtk currently, since it's well-matched
to the GTK tree view widget and I didn't want to be bothered with the
TUI browser code at this stage. :)

I don't attach a screenshot now, but this is an hypothetical example
on stdio. An usual output of perf report look like:

Overhead Command Shared Object Symbol
........ ....... ................. ........................................
22.70% swapper [kernel.kallsyms] [k] intel_idle
8.36% swapper [kernel.kallsyms] [k] irqtime_acouunt_process_tick.isra.74
1.33% swapper [kernel.kallsyms] [k] __schedule


If --hierarchy (-H) option is given, it'll look like (after expanding
all children):

Overhead Command / Shared Object / Symbol
........ ..............................................
32.39% - swapper
32.39% - [kernel.kallsyms]
22.70% [k] intel_idle
8.36% [k] irqtime_acouunt_process_tick.isra.74
1.33% [k] __schedule

(The overhead of an upper-level entry is the sum of lower-level
entries' overhead).

You can get it from 'perf/hierarchy-v1' branch on my tree at:

git://git.kernel.org/pub/scm/linux/kernel/git/namhyung-perf.git


Any comments are welcome, thanks!
Namhyung


Namhyung Kim (7):
perf hists: Basic support of hierarchical view
perf gtk/hists: Use GtkTreeStore instead of GtkListStore
perf gtk/hists: Factor out perf_gtk__add_entries()
perf gtk/hists: Add support for hierachical output
perf report: Add -H (--hierarchy) option
perf gtk/hists: Add a double-click handler for hierarchy mode
perf gtk/hists: Set rules hint for the hist browser

tools/perf/Documentation/perf-report.txt | 5 +
tools/perf/builtin-report.c | 6 +
tools/perf/ui/gtk/hists.c | 192 ++++++++++++++----
tools/perf/util/hist.c | 325 +++++++++++++++++++++++++++----
tools/perf/util/sort.h | 3 +
tools/perf/util/symbol.h | 3 +-
6 files changed, 460 insertions(+), 74 deletions(-)

--
1.7.11.7


2013-05-21 06:14:49

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 7/7] perf gtk/hists: Set rules hint for the hist browser

From: Namhyung Kim <[email protected]>

The 'rules' means that every second line of the tree view has a shaded
background, which makes it easier to see which cell belongs to which
row in the tree view. It can be useful for a tree view that has a lot
of rows.

Cc: Pekka Enberg <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/ui/gtk/hists.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index e161317236fe..b108d2fb1130 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -339,6 +339,8 @@ add:
G_CALLBACK(on_row_activated), NULL);
}

+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
+
gtk_container_add(GTK_CONTAINER(window), view);
}

--
1.7.11.7

2013-05-21 06:14:46

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 2/7] perf gtk/hists: Use GtkTreeStore instead of GtkListStore

From: Namhyung Kim <[email protected]>

The GtkTreeStore can save items in a tree-like way. This is a
preparation for supporting hierachical output in the hist browser.

Cc: Pekka Enberg <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/ui/gtk/hists.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 6f259b3d14e2..ba59d5d63d74 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -130,7 +130,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
GType col_types[MAX_COLUMNS];
GtkCellRenderer *renderer;
struct sort_entry *se;
- GtkListStore *store;
+ GtkTreeStore *store;
struct rb_node *nd;
GtkWidget *view;
int col_idx;
@@ -155,7 +155,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
col_types[nr_cols++] = G_TYPE_STRING;
}

- store = gtk_list_store_newv(nr_cols, col_types);
+ store = gtk_tree_store_newv(nr_cols, col_types);

view = gtk_tree_view_new();

@@ -193,7 +193,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
if (h->filtered)
continue;

- gtk_list_store_append(store, &iter);
+ gtk_tree_store_append(store, &iter, NULL);

col_idx = 0;

@@ -203,7 +203,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
else
fmt->entry(&hpp, h);

- gtk_list_store_set(store, &iter, col_idx++, s, -1);
+ gtk_tree_store_set(store, &iter, col_idx++, s, -1);
}

list_for_each_entry(se, &hist_entry__sort_list, list) {
@@ -213,7 +213,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
se->se_snprintf(h, s, ARRAY_SIZE(s),
hists__col_len(hists, se->se_width_idx));

- gtk_list_store_set(store, &iter, col_idx++, s, -1);
+ gtk_tree_store_set(store, &iter, col_idx++, s, -1);
}
}

--
1.7.11.7

2013-05-21 06:15:13

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 6/7] perf gtk/hists: Add a double-click handler for hierarchy mode

From: Namhyung Kim <[email protected]>

If hierarchy mode is enabled, add "row-activated" signal handler for
handling double-click or pressing ENTER key action.

Cc: Pekka Enberg <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/ui/gtk/hists.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index fb4b5ff00319..e161317236fe 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -220,6 +220,18 @@ static void perf_gtk__add_entries_hierarchy(struct hists *hists,
__perf_gtk__add_entries_hierarchy(hists, root, store, NULL, hpp, se);
}

+static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
+ GtkTreeViewColumn *col __maybe_unused,
+ gpointer user_data __maybe_unused)
+{
+ bool expanded = gtk_tree_view_row_expanded(view, path);
+
+ if (expanded)
+ gtk_tree_view_collapse_row(view, path);
+ else
+ gtk_tree_view_expand_row(view, path, FALSE);
+}
+
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
@@ -322,6 +334,9 @@ add:
expander_col);

perf_gtk__add_entries_hierarchy(hists, &hpp, store);
+
+ g_signal_connect(view, "row-activated",
+ G_CALLBACK(on_row_activated), NULL);
}

gtk_container_add(GTK_CONTAINER(window), view);
--
1.7.11.7

2013-05-21 06:15:35

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 5/7] perf report: Add -H (--hierarchy) option

From: Namhyung Kim <[email protected]>

It's for enabling the hierarchical view.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/Documentation/perf-report.txt | 5 +++++
tools/perf/builtin-report.c | 6 ++++++
2 files changed, 11 insertions(+)

diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 7d5f4f38aa52..3c86b9f83c54 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -210,6 +210,11 @@ OPTIONS
Demangle symbol names to human readable form. It's enabled by default,
disable with --no-demangle.

+-H::
+--hierarchy::
+ Show events in hierarchical view. Each events are grouped under prior
+ sort keys. Currently --gtk output is supported only.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index d45bf9b0361d..9b9a35b296d8 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -809,6 +809,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
"Disable symbol demangling"),
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
+ OPT_BOOLEAN('H', "hierarchy", &symbol_conf.hierarchy, "Hierarchical view"),
OPT_END()
};

@@ -833,6 +834,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
input_name = "perf.data";
}

+ if (symbol_conf.hierarchy && use_browser != 2) {
+ pr_err("--hierarchy option is only used for --gtk output\n");
+ return -EINVAL;
+ }
+
if (strcmp(input_name, "-") != 0)
setup_browser(true);
else {
--
1.7.11.7

2013-05-21 06:14:44

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 1/7] perf hists: Basic support of hierarchical view

From: Namhyung Kim <[email protected]>

In a hierarchical view, the events sorted and grouped on first key,
and then second key, and so on. Now each hist entry has 3 of hroots
to save it's children entries.

Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/util/hist.c | 325 +++++++++++++++++++++++++++++++++++++++++------
tools/perf/util/sort.h | 3 +
tools/perf/util/symbol.h | 3 +-
3 files changed, 291 insertions(+), 40 deletions(-)

diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 72b4eec820c3..80b981d52582 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -348,7 +348,7 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0;
}

-static struct hist_entry *add_hist_entry(struct hists *hists,
+static struct hist_entry *__add_hist_entry(struct hists *hists,
struct hist_entry *entry,
struct addr_location *al,
u64 period,
@@ -417,6 +417,96 @@ out_unlock:
return he;
}

+static struct hist_entry *__add_hist_entry_hierarchy(struct hists *hists,
+ struct hist_entry *entry,
+ struct addr_location *al,
+ u64 period, u64 weight)
+{
+ struct rb_root *root;
+ struct rb_node **p;
+ struct rb_node *parent = NULL;
+ struct hist_entry *he = NULL;
+ struct sort_entry *se;
+ int cmp;
+
+ pthread_mutex_lock(&hists->lock);
+
+ root = hists->entries_in;
+ p = &root->rb_node;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ while (*p != NULL) {
+ parent = *p;
+ he = rb_entry(parent, struct hist_entry, rb_node_in);
+
+ /*
+ * Make sure that it receives arguments in a same order as
+ * hist_entry__collapse() so that we can use an appropriate
+ * function when searching an entry regardless which sort
+ * keys were used.
+ */
+ cmp = se->se_cmp(he, entry);
+
+ if (!cmp) {
+ he_stat__add_period(&he->stat, period, weight);
+
+ /*
+ * This mem info was allocated from machine__resolve_mem
+ * and will not be used anymore.
+ */
+ free(entry->mem_info);
+
+ /* If the map of an existing hist_entry has
+ * become out-of-date due to an exec() or
+ * similar, update it. Otherwise we will
+ * mis-adjust symbol addresses when computing
+ * the history counter to increment.
+ */
+ if (he->ms.map != entry->ms.map) {
+ he->ms.map = entry->ms.map;
+ if (he->ms.map)
+ he->ms.map->referenced = true;
+ }
+ goto next;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ he = hist_entry__new(entry);
+ if (!he)
+ goto out_unlock;
+
+ rb_link_node(&he->rb_node_in, parent, p);
+ rb_insert_color(&he->rb_node_in, root);
+next:
+ hist_entry__add_cpumode_period(he, al->cpumode, period);
+
+ root = &he->rb_hroot_in;
+ p = &root->rb_node;
+ parent = NULL;
+ }
+
+out_unlock:
+ pthread_mutex_unlock(&hists->lock);
+ return he;
+}
+
+static struct hist_entry *add_hist_entry(struct hists *hists,
+ struct hist_entry *entry,
+ struct addr_location *al,
+ u64 period,
+ u64 weight)
+{
+ if (!symbol_conf.hierarchy)
+ return __add_hist_entry(hists, entry, al, period, weight);
+
+ return __add_hist_entry_hierarchy(hists, entry, al, period, weight);
+}
+
struct hist_entry *__hists__add_mem_entry(struct hists *self,
struct addr_location *al,
struct symbol *sym_parent,
@@ -548,6 +638,8 @@ void hist_entry__free(struct hist_entry *he)
free(he);
}

+static void hists__apply_filters(struct hists *hists, struct hist_entry *he);
+
/*
* collapse the histogram
*/
@@ -591,56 +683,148 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
return true;
}

-static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
+static void hists__collapse_resort_entries(struct hists *hists,
+ struct rb_root *root_in,
+ struct rb_root *root)
{
- struct rb_root *root;
-
- pthread_mutex_lock(&hists->lock);
+ struct rb_node *next;
+ struct hist_entry *he;

- root = hists->entries_in;
- if (++hists->entries_in > &hists->entries_in_array[1])
- hists->entries_in = &hists->entries_in_array[0];
+ next = rb_first(root_in);

- pthread_mutex_unlock(&hists->lock);
+ while (next) {
+ he = rb_entry(next, struct hist_entry, rb_node_in);
+ next = rb_next(&he->rb_node_in);

- return root;
+ rb_erase(&he->rb_node_in, root_in);
+ if (hists__collapse_insert_entry(hists, root, he)) {
+ /*
+ * If it wasn't combined with one of the entries already
+ * collapsed, we need to apply the filters that may have
+ * been set by, say, the hist_browser.
+ */
+ hists__apply_filters(hists, he);
+ }
+ }
}

-static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
+static struct hist_entry *
+__collapse_insert_entry_hierarchy(struct rb_root *root, struct hist_entry *he,
+ struct sort_entry *se)
{
- hists__filter_entry_by_dso(hists, he);
- hists__filter_entry_by_thread(hists, he);
- hists__filter_entry_by_symbol(hists, he);
+ struct rb_node **p = &root->rb_node;
+ struct rb_node *parent = NULL;
+ struct hist_entry *iter;
+ int64_t cmp;
+
+ while (*p != NULL) {
+ int64_t (*f)(struct hist_entry *, struct hist_entry *);
+ f = se->se_collapse ?: se->se_cmp;
+
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node_in);
+
+ cmp = f(iter, he);
+
+ if (!cmp) {
+ he_stat__add_stat(&iter->stat, &he->stat);
+
+ if (symbol_conf.use_callchain) {
+ callchain_cursor_reset(&callchain_cursor);
+ callchain_merge(&callchain_cursor,
+ iter->callchain,
+ he->callchain);
+ }
+ return iter;
+ }
+
+ if (cmp < 0)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ rb_link_node(&he->rb_node_in, parent, p);
+ rb_insert_color(&he->rb_node_in, root);
+ return he;
}

-static void __hists__collapse_resort(struct hists *hists, bool threaded)
+static void
+hists__collapse_resort_entries_hierarchy(struct hists *hists,
+ struct rb_root *root_in,
+ struct rb_root *root,
+ struct sort_entry *se)
{
- struct rb_root *root;
struct rb_node *next;
- struct hist_entry *n;
+ struct hist_entry *he;
+ struct sort_entry *se_next;

- if (!sort__need_collapse && !threaded)
+ next = rb_first(root_in);
+ if (next == NULL)
return;

- root = hists__get_rotate_entries_in(hists);
- next = rb_first(root);
+ BUG_ON(&se->list == &hist_entry__sort_list);
+ se_next = list_entry(se->list.next, struct sort_entry, list);

while (next) {
- n = rb_entry(next, struct hist_entry, rb_node_in);
- next = rb_next(&n->rb_node_in);
+ struct hist_entry *he_new;
+
+ he = rb_entry(next, struct hist_entry, rb_node_in);
+ next = rb_next(&he->rb_node_in);

- rb_erase(&n->rb_node_in, root);
- if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
+ rb_erase(&he->rb_node_in, root_in);
+ he_new = __collapse_insert_entry_hierarchy(root, he, se);
+ if (he == he_new) {
/*
* If it wasn't combined with one of the entries already
* collapsed, we need to apply the filters that may have
* been set by, say, the hist_browser.
*/
- hists__apply_filters(hists, n);
+ hists__apply_filters(hists, he);
}
+
+ hists__collapse_resort_entries_hierarchy(hists, &he->rb_hroot_in,
+ &he_new->rb_hroot_collapsed, se_next);
+
+ if (he != he_new)
+ hist_entry__free(he);
}
}

+static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
+{
+ struct rb_root *root;
+
+ pthread_mutex_lock(&hists->lock);
+
+ root = hists->entries_in;
+ if (++hists->entries_in > &hists->entries_in_array[1])
+ hists->entries_in = &hists->entries_in_array[0];
+
+ pthread_mutex_unlock(&hists->lock);
+
+ return root;
+}
+
+static void __hists__collapse_resort(struct hists *hists, bool threaded)
+{
+ struct rb_root *root_in;
+ struct rb_root *root;
+ struct sort_entry *se;
+
+ if (!sort__need_collapse && !threaded)
+ return;
+
+ root_in = hists__get_rotate_entries_in(hists);
+ root = &hists->entries_collapsed;
+
+ if (!symbol_conf.hierarchy)
+ return hists__collapse_resort_entries(hists, root_in, root);
+
+ se = list_first_entry(&hist_entry__sort_list, struct sort_entry, list);
+ hists__collapse_resort_entries_hierarchy(hists, root_in, root, se);
+}
+
void hists__collapse_resort(struct hists *hists)
{
return __hists__collapse_resort(hists, false);
@@ -711,11 +895,11 @@ out:
return ret;
}

-static void __hists__insert_output_entry(struct rb_root *entries,
+static void __hists__insert_output_entry(struct rb_root *root,
struct hist_entry *he,
u64 min_callchain_hits)
{
- struct rb_node **p = &entries->rb_node;
+ struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;

@@ -734,37 +918,92 @@ static void __hists__insert_output_entry(struct rb_root *entries,
}

rb_link_node(&he->rb_node, parent, p);
- rb_insert_color(&he->rb_node, entries);
+ rb_insert_color(&he->rb_node, root);
+}
+
+static void hists__output_resort_entries(struct hists *hists,
+ struct rb_root *root_in,
+ struct rb_root *root,
+ u64 min_callchain_hits)
+{
+ struct rb_node *next;
+ struct hist_entry *he;
+
+ next = rb_first(root_in);
+
+ while (next) {
+ he = rb_entry(next, struct hist_entry, rb_node_in);
+ next = rb_next(&he->rb_node_in);
+
+ __hists__insert_output_entry(root, he, min_callchain_hits);
+ hists__inc_nr_entries(hists, he);
+ }
+}
+
+static void hists__output_resort_entries_hierarchy(struct hists *hists,
+ struct rb_root *root_in,
+ struct rb_root *root,
+ u64 min_callchain_hits,
+ bool threaded)
+{
+ struct rb_node *next;
+ struct hist_entry *he;
+ struct rb_root *hroot_in;
+
+ next = rb_first(root_in);
+
+ if (next == NULL) {
+ he = list_entry(root, struct hist_entry, rb_hroot);
+ /* only count leaf nodes */
+ hists__inc_nr_entries(hists, he);
+ return;
+ }
+
+ while (next) {
+ he = rb_entry(next, struct hist_entry, rb_node_in);
+ next = rb_next(&he->rb_node_in);
+
+ __hists__insert_output_entry(root, he, min_callchain_hits);
+
+ if (sort__need_collapse || threaded)
+ hroot_in = &he->rb_hroot_collapsed;
+ else
+ hroot_in = &he->rb_hroot_in;
+
+ hists__output_resort_entries_hierarchy(hists, hroot_in,
+ &he->rb_hroot,
+ min_callchain_hits,
+ threaded);
+ }
}

static void __hists__output_resort(struct hists *hists, bool threaded)
{
+ struct rb_root *root_in;
struct rb_root *root;
- struct rb_node *next;
- struct hist_entry *n;
u64 min_callchain_hits;

min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);

if (sort__need_collapse || threaded)
- root = &hists->entries_collapsed;
+ root_in = &hists->entries_collapsed;
else
- root = hists->entries_in;
+ root_in = hists->entries_in;

- next = rb_first(root);
hists->entries = RB_ROOT;
+ root = &hists->entries;

hists->nr_entries = 0;
hists->stats.total_period = 0;
hists__reset_col_len(hists);

- while (next) {
- n = rb_entry(next, struct hist_entry, rb_node_in);
- next = rb_next(&n->rb_node_in);
-
- __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
- hists__inc_nr_entries(hists, n);
+ if (!symbol_conf.hierarchy) {
+ return hists__output_resort_entries(hists, root_in, root,
+ min_callchain_hits);
}
+
+ hists__output_resort_entries_hierarchy(hists, root_in, root,
+ min_callchain_hits, threaded);
}

void hists__output_resort(struct hists *hists)
@@ -889,6 +1128,14 @@ void hists__filter_by_symbol(struct hists *hists)
}
}

+static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
+{
+ hists__filter_entry_by_dso(hists, he);
+ hists__filter_entry_by_thread(hists, he);
+ hists__filter_entry_by_symbol(hists, he);
+}
+
+
int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
{
return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip);
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 51f1b5a854e7..6c2ba362ca05 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -75,6 +75,9 @@ struct hist_entry_diff {
struct hist_entry {
struct rb_node rb_node_in;
struct rb_node rb_node;
+ struct rb_root rb_hroot_in;
+ struct rb_root rb_hroot_collapsed;
+ struct rb_root rb_hroot;
union {
struct list_head node;
struct list_head head;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 5f720dc076da..ee0cfd8b785e 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -98,7 +98,8 @@ struct symbol_conf {
annotate_asm_raw,
annotate_src,
event_group,
- demangle;
+ demangle,
+ hierarchy;
const char *vmlinux_name,
*kallsyms_name,
*source_prefix,
--
1.7.11.7

2013-05-21 06:15:54

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 4/7] perf gtk/hists: Add support for hierachical output

From: Namhyung Kim <[email protected]>

When the symbol_conf.hierarchy is set, perf will collapse entries by a
first sort key, and then second key, and so on in a hierarchical
manner. It'll be looked like a tree view so set the expander column
to a column that displays sort entries.

Cc: Pekka Enberg <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/ui/gtk/hists.c | 109 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 103 insertions(+), 6 deletions(-)

diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index e1b8f702daf2..fb4b5ff00319 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -164,6 +164,62 @@ static void perf_gtk__add_entries(struct hists *hists, struct perf_hpp *hpp,
}
}

+static void __perf_gtk__add_entries_hierarchy(struct hists *hists,
+ struct rb_root *root,
+ GtkTreeStore *store,
+ GtkTreeIter *parent,
+ struct perf_hpp *hpp,
+ struct sort_entry *se)
+{
+ int col_idx;
+ struct rb_node *nd;
+ struct perf_hpp_fmt *fmt;
+
+ for (nd = rb_first(root); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ GtkTreeIter iter;
+ struct sort_entry *next_se;
+
+ if (h->filtered)
+ continue;
+
+ gtk_tree_store_append(store, &iter, parent);
+
+ col_idx = 0;
+
+ perf_hpp__for_each_format(fmt) {
+ if (fmt->color)
+ fmt->color(hpp, h);
+ else
+ fmt->entry(hpp, h);
+
+ gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);
+ }
+
+ se->se_snprintf(h, hpp->buf, hpp->size,
+ hists__col_len(hists, se->se_width_idx));
+
+ gtk_tree_store_set(store, &iter, col_idx++, ltrim(hpp->buf), -1);
+
+ next_se = list_entry(se->list.next, struct sort_entry, list);
+ __perf_gtk__add_entries_hierarchy(hists, &h->rb_hroot, store,
+ &iter, hpp, next_se);
+ }
+}
+
+static void perf_gtk__add_entries_hierarchy(struct hists *hists,
+ struct perf_hpp *hpp,
+ GtkTreeStore *store)
+{
+ struct rb_root *root;
+ struct sort_entry *se;
+
+ root = &hists->entries;
+ se = list_first_entry(&hist_entry__sort_list, struct sort_entry, list);
+
+ __perf_gtk__add_entries_hierarchy(hists, root, store, NULL, hpp, se);
+}
+
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
@@ -173,6 +229,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
GtkTreeStore *store;
GtkWidget *view;
int col_idx;
+ int sort_col = 0;
int nr_cols;
char s[512];

@@ -187,11 +244,19 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
perf_hpp__for_each_format(fmt)
col_types[nr_cols++] = G_TYPE_STRING;

- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
-
- col_types[nr_cols++] = G_TYPE_STRING;
+ if (symbol_conf.hierarchy) {
+ /*
+ * In --hierarchy mode, all sort entries are displayed in a
+ * single column.
+ */
+ col_types[nr_cols] = G_TYPE_STRING;
+ sort_col = nr_cols++;
+ } else {
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+ col_types[nr_cols++] = G_TYPE_STRING;
+ }
}

store = gtk_tree_store_newv(nr_cols, col_types);
@@ -211,6 +276,26 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
col_idx++, NULL);
}

+ if (symbol_conf.hierarchy) {
+ bool first = true;
+
+ s[0] = '\0';
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (!first)
+ strcat(s, " / ");
+
+ strcat(s, se->se_header);
+
+ first = false;
+ }
+
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, s,
+ renderer, "text",
+ col_idx++, NULL);
+ goto add;
+ }
+
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
continue;
@@ -221,11 +306,23 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
col_idx++, NULL);
}

+add:
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));

g_object_unref(GTK_TREE_MODEL(store));

- perf_gtk__add_entries(hists, &hpp, store);
+ if (!symbol_conf.hierarchy)
+ perf_gtk__add_entries(hists, &hpp, store);
+ else {
+ GtkTreeViewColumn *expander_col;
+
+ expander_col = gtk_tree_view_get_column(GTK_TREE_VIEW(view),
+ sort_col);
+ gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
+ expander_col);
+
+ perf_gtk__add_entries_hierarchy(hists, &hpp, store);
+ }

gtk_container_add(GTK_CONTAINER(window), view);
}
--
1.7.11.7

2013-05-21 06:16:12

by Namhyung Kim

[permalink] [raw]
Subject: [PATCH 3/7] perf gtk/hists: Factor out perf_gtk__add_entries()

From: Namhyung Kim <[email protected]>

This is a preparation for supporting hierarchical output in the hist
browser.

Cc: Pekka Enberg <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
---
tools/perf/ui/gtk/hists.c | 72 +++++++++++++++++++++++++++--------------------
1 file changed, 41 insertions(+), 31 deletions(-)

diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index ba59d5d63d74..e1b8f702daf2 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -124,6 +124,46 @@ void perf_gtk__init_hpp(void)
perf_gtk__hpp_color_overhead_guest_us;
}

+static void perf_gtk__add_entries(struct hists *hists, struct perf_hpp *hpp,
+ GtkTreeStore *store)
+{
+ int col_idx;
+ struct rb_node *nd;
+ struct perf_hpp_fmt *fmt;
+ struct sort_entry *se;
+
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+ GtkTreeIter iter;
+
+ if (h->filtered)
+ continue;
+
+ gtk_tree_store_append(store, &iter, NULL);
+
+ col_idx = 0;
+
+ perf_hpp__for_each_format(fmt) {
+ if (fmt->color)
+ fmt->color(hpp, h);
+ else
+ fmt->entry(hpp, h);
+
+ gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ se->se_snprintf(h, hpp->buf, hpp->size,
+ hists__col_len(hists, se->se_width_idx));
+
+ gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);
+ }
+ }
+}
+
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
struct perf_hpp_fmt *fmt;
@@ -131,7 +171,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
GtkCellRenderer *renderer;
struct sort_entry *se;
GtkTreeStore *store;
- struct rb_node *nd;
GtkWidget *view;
int col_idx;
int nr_cols;
@@ -186,36 +225,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)

g_object_unref(GTK_TREE_MODEL(store));

- for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
- GtkTreeIter iter;
-
- if (h->filtered)
- continue;
-
- gtk_tree_store_append(store, &iter, NULL);
-
- col_idx = 0;
-
- perf_hpp__for_each_format(fmt) {
- if (fmt->color)
- fmt->color(&hpp, h);
- else
- fmt->entry(&hpp, h);
-
- gtk_tree_store_set(store, &iter, col_idx++, s, -1);
- }
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
-
- se->se_snprintf(h, s, ARRAY_SIZE(s),
- hists__col_len(hists, se->se_width_idx));
-
- gtk_tree_store_set(store, &iter, col_idx++, s, -1);
- }
- }
+ perf_gtk__add_entries(hists, &hpp, store);

gtk_container_add(GTK_CONTAINER(window), view);
}
--
1.7.11.7

2013-05-21 07:04:09

by Pekka Enberg

[permalink] [raw]
Subject: Re: [RFC 0/7] perf report/gtk: Add support for hierarchy view

Hello,

On Tue, May 21, 2013 at 9:14 AM, Namhyung Kim <[email protected]> wrote:
> This patchset implements a new feature that collects hist entries in a
> hierachical manner. That means lower-level entries are belonged to an
> upper-level entry. The entry hierachy is built on the sort keys
> given, so users can set it whatever they want. It only shows
> top-level entries first, and user can expand/collapse it dynamically.
>
> This is only implemented in --gtk currently, since it's well-matched
> to the GTK tree view widget and I didn't want to be bothered with the
> TUI browser code at this stage. :)

Can use this infrastructure to support call-graphs in the GTK UI?

Pekka

2013-05-21 07:26:31

by Namhyung Kim

[permalink] [raw]
Subject: Re: [RFC 0/7] perf report/gtk: Add support for hierarchy view

Hi Pekka,

On Tue, 21 May 2013 10:04:05 +0300, Pekka Enberg wrote:
> Hello,
>
> On Tue, May 21, 2013 at 9:14 AM, Namhyung Kim <[email protected]> wrote:
>> This patchset implements a new feature that collects hist entries in a
>> hierachical manner. That means lower-level entries are belonged to an
>> upper-level entry. The entry hierachy is built on the sort keys
>> given, so users can set it whatever they want. It only shows
>> top-level entries first, and user can expand/collapse it dynamically.
>>
>> This is only implemented in --gtk currently, since it's well-matched
>> to the GTK tree view widget and I didn't want to be bothered with the
>> TUI browser code at this stage. :)
>
> Can use this infrastructure to support call-graphs in the GTK UI?

Yes, I think it's doable. I don't have an idea how to use both of the
features at the same time though. Anyway, I'll try to add support for
callchains later.

Thanks,
Namhyung

2013-05-22 08:45:18

by Pekka Enberg

[permalink] [raw]
Subject: Re: [RFC 0/7] perf report/gtk: Add support for hierarchy view

On 05/21/2013 10:26 AM, Namhyung Kim wrote:
> On Tue, 21 May 2013 10:04:05 +0300, Pekka Enberg wrote:
>> Hello,
>>
>> On Tue, May 21, 2013 at 9:14 AM, Namhyung Kim <[email protected]> wrote:
>>> This patchset implements a new feature that collects hist entries in a
>>> hierachical manner. That means lower-level entries are belonged to an
>>> upper-level entry. The entry hierachy is built on the sort keys
>>> given, so users can set it whatever they want. It only shows
>>> top-level entries first, and user can expand/collapse it dynamically.
>>>
>>> This is only implemented in --gtk currently, since it's well-matched
>>> to the GTK tree view widget and I didn't want to be bothered with the
>>> TUI browser code at this stage. :)
>>
>> Can use this infrastructure to support call-graphs in the GTK UI?
>
> Yes, I think it's doable. I don't have an idea how to use both of the
> features at the same time though. Anyway, I'll try to add support for
> callchains later.

So I think hierarchy view makes tons of sense but it needs to be
orthogonal with call-graphs. Preferably in such a way that if you have
both hierarchy view and call-graphs enabled, you can seamlessly drill
down from process level all the way to call-graph leaf nodes.

Pekka