Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1324158 > unrolled thread
| Started by | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| First post | 2016-02-02 15:50 +0100 |
| Last post | 2016-02-02 15:50 +0100 |
| Articles | 11 — 1 participant |
Back to article view | Back to linux.kernel
[PATCHSET 00/21] perf tools: Add support for hierachy view (v4) Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 04/21] perf callchain: Check return value of split_add_child() Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 10/21] perf hists: Add helper functions for hierarchy mode Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 15/21] perf hists browser: Support collapsing/expanding whole entries in hierarchy Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 09/21] perf hists: Resort hist entries with hierarchy Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 12/21] perf ui/stdio: Implement hierarchy output mode Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 05/21] perf callchain: Check return value of append_chain_children() Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 18/21] perf ui/gtk: Implement hierarchy output mode Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 17/21] perf hists browser: Align column header in hierarchy mode Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 21/21] perf top: Add --hierarchy option Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
[PATCH 02/21] perf callchain: Check return value of fill_node() Namhyung Kim <namhyung@kernel.org> - 2016-02-02 15:50 +0100
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCHSET 00/21] perf tools: Add support for hierachy view (v4) |
| Message-ID | <qXKrf-4kd-3@gated-at.bofh.it> |
Hello,
This is v4 attempt of my earlier patchset [1]. This patchset
implements a new feature that collects hist entries in a hierachical
manner. That means lower-level entries belong 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.
The patch 01 to 07 are resend and got Ack's from Jiri [2].
* Changes from v3)
- rebased onto the percent limit patchset v2
* Changes from v2)
- check memory allocation failure in hists__hierarchy_insert_entry (Jiri)
- remove unused rb_hierarchy_first() (Arnaldo)
- support callchain percent limit (Andi)
- break TUI context menu cleanup (Arnaldo)
This time I implemented it for every output browser including TUI.
A screenshot on TUI looks like below:
For normal output:
$ perf report --tui
Samples: 3K of event 'cycles:pp', Event count (approx.): 1695979674
Overhead Command Shared Object Symbol
------------------------------------------------------------------------
- 7.57% swapper [kernel.vmlinux] [k] intel_idle
intel_idle
cpuidle_enter_state
cpuidle_enter
call_cpuidle
+ cpu_startup_entry
+ 1.16 firefox firefox [.] 0x00000000000019433
+ 0.97% firefox libpthread-2.22.so [.] pthread_mutex_lock
...
With hierarchy view,
$ perf report --tui --hierarchy
Samples: 3K of event 'cycles:pp', Event count (approx.): 1695979674
Overhead Command / Shared Object / Symbol
-------------------------------------------------------------------
+ 76.30% firefox
- 9.95% swapper
- 9.51% [kernel.vmlinux]
- 7.57 [k] intel_idle
intel_idle
cpuidle_enter_state
cpuidle_enter
call_cpuidle
+ cpu_startup_entry
+ 0.15% [k] __schedule
+ 0.12% [k] menu_select
...
+ 0.34% [sdhci]
+ 0.06% [e1000e]
...
+ 5.65% Xorg
+ 5.42% Socket Thread
...
As you can see, overhead of an upper level entry is the sum of
overhead of lower level entries. The entries are aligned by its order
of matching sort keys.
This is available from 'perf/hierarchy-v4' branch in my tree:
git://git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git
Any comments are welcome, thanks!
Namhyung
Cc: Don Zickus <dzickus@redhat.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Moinuddin Quadri <moin18@gmail.com>
[1] https://lkml.org/lkml/2013/5/21/24
[2] http://lkml.iu.edu/hypermail/linux/kernel/1601.2/04245.html
Namhyung Kim (21):
perf callchain: Check return value of add_child()
perf callchain: Check return value of fill_node()
perf callchain: Add enum match_result for match_chain()
perf callchain: Check return value of split_add_child()
perf callchain: Check return value of append_chain_children()
perf hists: Return error from hists__collapse_resort()
perf report: Check error during report__collapse_hists()
perf hists: Basic support of hierarchical report view
perf hists: Resort hist entries with hierarchy
perf hists: Add helper functions for hierarchy mode
perf hists: Support filtering in hierarchy mode
perf ui/stdio: Implement hierarchy output mode
perf ui/stdio: Align column header for hierarchy output
perf hists browser: Count number of hierarchy entries
perf hists browser: Support collapsing/expanding whole entries in
hierarchy
perf hists browser: Implement hierarchy output
perf hists browser: Align column header in hierarchy mode
perf ui/gtk: Implement hierarchy output mode
perf report: Add --hierarchy option
perf hists: Support decaying in hierarchy mode
perf top: Add --hierarchy option
tools/perf/Documentation/perf-report.txt | 3 +
tools/perf/Documentation/perf-top.txt | 3 +
tools/perf/Documentation/tips.txt | 1 +
tools/perf/builtin-report.c | 31 +-
tools/perf/builtin-top.c | 15 +
tools/perf/ui/browsers/hists.c | 470 ++++++++++++++++++++++++++++---
tools/perf/ui/gtk/hists.c | 161 ++++++++++-
tools/perf/ui/hist.c | 14 +
tools/perf/ui/stdio/hist.c | 184 +++++++++++-
tools/perf/util/callchain.c | 102 +++++--
tools/perf/util/hist.c | 421 +++++++++++++++++++++++++--
tools/perf/util/hist.h | 14 +-
tools/perf/util/sort.c | 113 ++++++++
tools/perf/util/sort.h | 14 +-
tools/perf/util/symbol.h | 3 +-
15 files changed, 1444 insertions(+), 105 deletions(-)
--
2.6.4
[toc] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 04/21] perf callchain: Check return value of split_add_child() |
| Message-ID | <qXKrh-4kd-51@gated-at.bofh.it> |
| In reply to | #1324158 |
Now create_child() and add_child() return errors so check and pass it
to the caller.
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/callchain.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index dab2c1f1e86b..5259379892e1 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -508,7 +508,7 @@ static enum match_result match_chain(struct callchain_cursor_node *node,
* give a part of its callchain to the created child.
* Then create another child to host the given callchain of new branch
*/
-static void
+static int
split_add_child(struct callchain_node *parent,
struct callchain_cursor *cursor,
struct callchain_list *to_split,
@@ -520,6 +520,8 @@ split_add_child(struct callchain_node *parent,
/* split */
new = create_child(parent, true);
+ if (new == NULL)
+ return -1;
/* split the callchain and move a part to the new child */
old_tail = parent->val.prev;
@@ -554,7 +556,7 @@ split_add_child(struct callchain_node *parent,
node = callchain_cursor_current(cursor);
new = add_child(parent, cursor, period);
if (new == NULL)
- return;
+ return -1;
/*
* This is second child since we moved parent's children
@@ -576,6 +578,7 @@ split_add_child(struct callchain_node *parent,
parent->hit = period;
parent->count = 1;
}
+ return 0;
}
static enum match_result
@@ -670,7 +673,10 @@ append_chain(struct callchain_node *root,
/* we match only a part of the node. Split it and add the new chain */
if (matches < root->val_nr) {
- split_add_child(root, cursor, cnode, start, matches, period);
+ if (split_add_child(root, cursor, cnode, start, matches,
+ period) < 0)
+ return MATCH_ERROR;
+
return MATCH_EQ;
}
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 10/21] perf hists: Add helper functions for hierarchy mode |
| Message-ID | <qXKri-4kd-53@gated-at.bofh.it> |
| In reply to | #1324158 |
The rb_hierarchy_{next,prev,last} functions are to traverse all
hist entries in a hierarchy. They will be used by various function
which supports hierarchy output.
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/hist.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/hist.h | 8 ++++++++
2 files changed, 53 insertions(+)
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index b0840f9bfe98..3d9709473b0c 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1416,6 +1416,51 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)
}
}
+struct rb_node *rb_hierarchy_last(struct rb_node *node)
+{
+ struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
+
+ while (he->unfolded && !he->leaf) {
+ node = rb_last(&he->hroot_out);
+ he = rb_entry(node, struct hist_entry, rb_node);
+ }
+ return node;
+}
+
+struct rb_node *rb_hierarchy_next(struct rb_node *node)
+{
+ struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
+
+ if (!he->leaf && he->unfolded)
+ node = rb_first(&he->hroot_out);
+ else
+ node = rb_next(node);
+
+ while (node == NULL) {
+ he = he->parent_he;
+ if (he == NULL)
+ break;
+
+ node = rb_next(&he->rb_node);
+ }
+ return node;
+}
+
+struct rb_node *rb_hierarchy_prev(struct rb_node *node)
+{
+ struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
+
+ node = rb_prev(node);
+ if (node)
+ return rb_hierarchy_last(node);
+
+ he = he->parent_he;
+ if (he == NULL)
+ return NULL;
+
+ return &he->rb_node;
+}
+
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
enum hist_filter filter)
{
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 9c72f8331d1c..487cb677d48e 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -381,4 +381,12 @@ int parse_filter_percentage(const struct option *opt __maybe_unused,
const char *arg, int unset __maybe_unused);
int perf_hist_config(const char *var, const char *value);
+#define HIERARCHY_INDENT 3
+
+int perf_hpp__count_sort_keys(void);
+
+struct rb_node *rb_hierarchy_last(struct rb_node *node);
+struct rb_node *rb_hierarchy_next(struct rb_node *node);
+struct rb_node *rb_hierarchy_prev(struct rb_node *node);
+
#endif /* __PERF_HIST_H */
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 15/21] perf hists browser: Support collapsing/expanding whole entries in hierarchy |
| Message-ID | <qXKri-4kd-57@gated-at.bofh.it> |
| In reply to | #1324158 |
The 'C' and 'E' keys are to collapse/expand all hist entries. Update
nr_hierarchy_entries properly in this case.
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/ui/browsers/hists.c | 63 +++++++++++++++++++++++++++++++++++-------
1 file changed, 53 insertions(+), 10 deletions(-)
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 4fbdbee4da28..ebb5d23716b6 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -484,13 +484,37 @@ static int callchain__set_folding(struct rb_root *chain, bool unfold)
return n;
}
-static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
+static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
+ bool unfold __maybe_unused)
+{
+ float percent;
+ struct rb_node *nd;
+ struct hist_entry *child;
+ int n = 0;
+
+ for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
+ child = rb_entry(nd, struct hist_entry, rb_node);
+ percent = hist_entry__get_percent_limit(child);
+ if (!child->filtered && percent >= hb->min_pcnt)
+ n++;
+ }
+
+ return n;
+}
+
+static void hist_entry__set_folding(struct hist_entry *he,
+ struct hist_browser *hb, bool unfold)
{
- hist_entry__init_have_children(he);
he->unfolded = unfold ? he->has_children : false;
if (he->has_children) {
- int n = callchain__set_folding(&he->sorted_chain, unfold);
+ int n;
+
+ if (he->leaf)
+ n = callchain__set_folding(&he->sorted_chain, unfold);
+ else
+ n = hierarchy_set_folding(hb, he, unfold);
+
he->nr_rows = unfold ? n : 0;
} else
he->nr_rows = 0;
@@ -500,19 +524,38 @@ static void
__hist_browser__set_folding(struct hist_browser *browser, bool unfold)
{
struct rb_node *nd;
- struct hists *hists = browser->hists;
+ struct hist_entry *he;
+ double percent;
- for (nd = rb_first(&hists->entries);
- (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
- nd = rb_next(nd)) {
- struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
- hist_entry__set_folding(he, unfold);
- browser->nr_callchain_rows += he->nr_rows;
+ nd = rb_first(&browser->hists->entries);
+ while (nd) {
+ he = rb_entry(nd, struct hist_entry, rb_node);
+
+ hist_entry__init_have_children(he);
+
+ /*
+ * Tentatively set unfolded so that the rb_hierarchy_next()
+ * can toggle children of folded entries too.
+ */
+ he->unfolded = he->has_children;
+ nd = rb_hierarchy_next(nd);
+
+ hist_entry__set_folding(he, browser, unfold);
+
+ percent = hist_entry__get_percent_limit(he);
+ if (he->filtered || percent < browser->min_pcnt)
+ continue;
+
+ if (!he->depth || unfold)
+ browser->nr_hierarchy_entries++;
+ if (he->leaf)
+ browser->nr_callchain_rows += he->nr_rows;
}
}
static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
{
+ browser->nr_hierarchy_entries = 0;
browser->nr_callchain_rows = 0;
__hist_browser__set_folding(browser, unfold);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 09/21] perf hists: Resort hist entries with hierarchy |
| Message-ID | <qXKri-4kd-55@gated-at.bofh.it> |
| In reply to | #1324158 |
For hierarchical output, each entries should be sorted in their
rbtree (hroot) properly. Add hists__hierarchy_output_resort() to do the
job. Note that those hierarchy entries share the period counts, it'd be
important to update the hists->stats only once (for leaves).
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/hist.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 7c604b4fc516..b0840f9bfe98 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -1262,6 +1262,74 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h)
hists->stats.total_period += h->stat.period;
}
+static void hierarchy_insert_output_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;
+
+ while (*p != NULL) {
+ parent = *p;
+ iter = rb_entry(parent, struct hist_entry, rb_node);
+
+ if (hist_entry__sort(he, iter) > 0)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+
+ rb_link_node(&he->rb_node, parent, p);
+ rb_insert_color(&he->rb_node, root);
+}
+
+static void hists__hierarchy_output_resort(struct hists *hists,
+ struct ui_progress *prog,
+ struct rb_root *root_in,
+ struct rb_root *root_out,
+ u64 min_callchain_hits,
+ bool use_callchain)
+{
+ struct rb_node *node;
+ struct hist_entry *he;
+
+ *root_out = RB_ROOT;
+ node = rb_first(root_in);
+
+ while (node) {
+ he = rb_entry(node, struct hist_entry, rb_node_in);
+ node = rb_next(node);
+
+ hierarchy_insert_output_entry(root_out, he);
+
+ if (prog)
+ ui_progress__update(prog, 1);
+
+ if (!he->leaf) {
+ hists__hierarchy_output_resort(hists, prog,
+ &he->hroot_in,
+ &he->hroot_out,
+ min_callchain_hits,
+ use_callchain);
+ hists->nr_entries++;
+ if (!he->filtered)
+ hists->nr_non_filtered_entries++;
+
+ continue;
+ }
+
+ /* only update stat for leaf entries to avoid duplication */
+ hists__inc_stats(hists, he);
+ if (!he->filtered)
+ hists__calc_col_len(hists, he);
+
+ if (use_callchain)
+ callchain_param.sort(&he->sorted_chain, he->callchain,
+ min_callchain_hits,
+ &callchain_param);
+ }
+}
+
static void __hists__insert_output_entry(struct rb_root *entries,
struct hist_entry *he,
u64 min_callchain_hits,
@@ -1314,6 +1382,17 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)
min_callchain_hits = hists__total_period(hists) * (callchain_param.min_percent / 100);
+ hists__reset_stats(hists);
+ hists__reset_col_len(hists);
+
+ if (symbol_conf.report_hierarchy) {
+ return hists__hierarchy_output_resort(hists, prog,
+ &hists->entries_collapsed,
+ &hists->entries,
+ min_callchain_hits,
+ use_callchain);
+ }
+
if (sort__need_collapse)
root = &hists->entries_collapsed;
else
@@ -1322,9 +1401,6 @@ void hists__output_resort(struct hists *hists, struct ui_progress *prog)
next = rb_first(root);
hists->entries = RB_ROOT;
- hists__reset_stats(hists);
- hists__reset_col_len(hists);
-
while (next) {
n = rb_entry(next, struct hist_entry, rb_node_in);
next = rb_next(&n->rb_node_in);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 12/21] perf ui/stdio: Implement hierarchy output mode |
| Message-ID | <qXKri-4kd-59@gated-at.bofh.it> |
| In reply to | #1324158 |
The hierarchy output mode is to group entries for each level so that
user can see higher level picture more easily. It also helps to find
out which component is most costly. The output will look like below:
15.11% swapper
14.97% [kernel.vmlinux]
0.09% [libahci]
0.05% [iwlwifi]
10.29% irq/33-iwlwifi
6.45% [kernel.vmlinux]
1.41% [mac80211]
1.15% [iwldvm]
1.14% [iwlwifi]
0.14% [cfg80211]
4.81% firefox
3.92% libxul.so
0.34% [kernel.vmlinux]
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/ui/hist.c | 14 +++++++++
tools/perf/ui/stdio/hist.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index bf2a66e254ea..86c0f3d0ae04 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -679,3 +679,17 @@ void perf_hpp__set_user_width(const char *width_list_str)
break;
}
}
+
+int perf_hpp__count_sort_keys(void)
+{
+ int nr_sort = 0;
+ struct perf_hpp_fmt *fmt;
+
+ perf_hpp__for_each_format(fmt) {
+ if (perf_hpp__is_sort_entry(fmt) ||
+ perf_hpp__is_dynamic_entry(fmt))
+ nr_sort++;
+ }
+
+ return nr_sort;
+}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index 691e52ce7510..1d0aba6389eb 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -409,6 +409,71 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp)
return hpp->buf - start;
}
+static int hist_entry__hierarchy_fprintf(struct hist_entry *he,
+ struct perf_hpp *hpp,
+ int nr_sort_key, struct hists *hists,
+ FILE *fp)
+{
+ const char *sep = symbol_conf.field_sep;
+ struct perf_hpp_fmt *fmt;
+ char *buf = hpp->buf;
+ int ret, printed = 0;
+ bool first = true;
+
+ if (symbol_conf.exclude_other && !he->parent)
+ return 0;
+
+ ret = scnprintf(hpp->buf, hpp->size, "%*s", he->depth * HIERARCHY_INDENT, "");
+ advance_hpp(hpp, ret);
+
+ perf_hpp__for_each_format(fmt) {
+ if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
+ break;
+
+ /*
+ * If there's no field_sep, we still need
+ * to display initial ' '.
+ */
+ if (!sep || !first) {
+ ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
+ advance_hpp(hpp, ret);
+ } else
+ first = false;
+
+ if (perf_hpp__use_color() && fmt->color)
+ ret = fmt->color(fmt, hpp, he);
+ else
+ ret = fmt->entry(fmt, hpp, he);
+
+ advance_hpp(hpp, ret);
+ }
+
+ if (sep)
+ ret = scnprintf(hpp->buf, hpp->size, "%s", sep);
+ else
+ ret = scnprintf(hpp->buf, hpp->size, "%*s",
+ (nr_sort_key - 1) * HIERARCHY_INDENT + 2, "");
+ advance_hpp(hpp, ret);
+
+ fmt = he->fmt;
+ if (perf_hpp__use_color() && fmt->color)
+ fmt->color(fmt, hpp, he);
+ else
+ fmt->entry(fmt, hpp, he);
+
+ printed += fprintf(fp, "%s\n", buf);
+
+ if (symbol_conf.use_callchain && he->leaf) {
+ u64 total = hists__total_period(hists);
+
+ printed += hist_entry_callchain__fprintf(he, total, 0, fp);
+ goto out;
+ }
+
+out:
+ return printed;
+}
+
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
struct hists *hists,
char *bf, size_t bfsz, FILE *fp)
@@ -423,6 +488,13 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size,
if (size == 0 || size > bfsz)
size = hpp.size = bfsz;
+ if (symbol_conf.report_hierarchy) {
+ int nr_sort = perf_hpp__count_sort_keys();
+
+ return hist_entry__hierarchy_fprintf(he, &hpp, nr_sort,
+ hists, fp);
+ }
+
hist_entry__snprintf(he, &hpp);
ret = fprintf(fp, "%s\n", bf);
@@ -521,7 +593,7 @@ print_entries:
goto out;
}
- for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ for (nd = rb_first(&hists->entries); nd; nd = rb_hierarchy_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
float percent;
@@ -542,6 +614,9 @@ print_entries:
MAP__FUNCTION, fp);
fprintf(fp, "%.10s end\n", graph_dotted_line);
}
+
+ if (symbol_conf.report_hierarchy)
+ h->unfolded = true;
}
free(line);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 05/21] perf callchain: Check return value of append_chain_children() |
| Message-ID | <qXKri-4kd-61@gated-at.bofh.it> |
| In reply to | #1324158 |
Now it can check the error case, so check and pass it to the caller.
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/callchain.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 5259379892e1..24b4bd0d7754 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -586,7 +586,7 @@ append_chain(struct callchain_node *root,
struct callchain_cursor *cursor,
u64 period);
-static void
+static int
append_chain_children(struct callchain_node *root,
struct callchain_cursor *cursor,
u64 period)
@@ -598,7 +598,7 @@ append_chain_children(struct callchain_node *root,
node = callchain_cursor_current(cursor);
if (!node)
- return;
+ return -1;
/* lookup in childrens */
while (*p) {
@@ -611,6 +611,8 @@ append_chain_children(struct callchain_node *root,
ret = append_chain(rnode, cursor, period);
if (ret == MATCH_EQ)
goto inc_children_hit;
+ if (ret == MATCH_ERROR)
+ return -1;
if (ret == MATCH_LT)
p = &parent->rb_left;
@@ -620,7 +622,7 @@ append_chain_children(struct callchain_node *root,
/* nothing in children, add to the current node */
rnode = add_child(root, cursor, period);
if (rnode == NULL)
- return;
+ return -1;
rb_link_node(&rnode->rb_node_in, parent, p);
rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);
@@ -628,6 +630,7 @@ append_chain_children(struct callchain_node *root,
inc_children_hit:
root->children_hit += period;
root->children_count++;
+ return 0;
}
static enum match_result
@@ -688,7 +691,8 @@ append_chain(struct callchain_node *root,
}
/* We match the node and still have a part remaining */
- append_chain_children(root, cursor, period);
+ if (append_chain_children(root, cursor, period) < 0)
+ return MATCH_ERROR;
return MATCH_EQ;
}
@@ -702,7 +706,8 @@ int callchain_append(struct callchain_root *root,
callchain_cursor_commit(cursor);
- append_chain_children(&root->node, cursor, period);
+ if (append_chain_children(&root->node, cursor, period) < 0)
+ return -1;
if (cursor->nr > root->max_depth)
root->max_depth = cursor->nr;
@@ -730,7 +735,8 @@ merge_chain_branch(struct callchain_cursor *cursor,
if (src->hit) {
callchain_cursor_commit(cursor);
- append_chain_children(dst, cursor, src->hit);
+ if (append_chain_children(dst, cursor, src->hit) < 0)
+ return -1;
}
n = rb_first(&src->rb_root_in);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 18/21] perf ui/gtk: Implement hierarchy output mode |
| Message-ID | <qXKri-4kd-63@gated-at.bofh.it> |
| In reply to | #1324158 |
The hierarchy output mode is to group entries for each level so that
user can see higher level picture more easily.
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/ui/gtk/hists.c | 161 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 0f8dcfdfb10f..f33df340fc45 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -396,6 +396,162 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
gtk_container_add(GTK_CONTAINER(window), view);
}
+static void perf_gtk__add_hierarchy_entries(struct hists *hists,
+ struct rb_root *root,
+ GtkTreeStore *store,
+ GtkTreeIter *parent,
+ struct perf_hpp *hpp,
+ float min_pcnt)
+{
+ int col_idx = 0;
+ struct rb_node *node;
+ struct hist_entry *he;
+ struct perf_hpp_fmt *fmt;
+ u64 total = hists__total_period(hists);
+
+ for (node = rb_first(root); node; node = rb_next(node)) {
+ GtkTreeIter iter;
+ float percent;
+
+ he = rb_entry(node, struct hist_entry, rb_node);
+ if (he->filtered)
+ continue;
+
+ percent = hist_entry__get_percent_limit(he);
+ if (percent < min_pcnt)
+ continue;
+
+ gtk_tree_store_append(store, &iter, parent);
+
+ col_idx = 0;
+ perf_hpp__for_each_format(fmt) {
+ if (perf_hpp__is_sort_entry(fmt) ||
+ perf_hpp__is_dynamic_entry(fmt))
+ break;
+
+ if (fmt->color)
+ fmt->color(fmt, hpp, he);
+ else
+ fmt->entry(fmt, hpp, he);
+
+ gtk_tree_store_set(store, &iter, col_idx++, hpp->buf, -1);
+ }
+
+ fmt = he->fmt;
+ if (fmt->color)
+ fmt->color(fmt, hpp, he);
+ else
+ fmt->entry(fmt, hpp, he);
+
+ gtk_tree_store_set(store, &iter, col_idx, rtrim(hpp->buf), -1);
+
+ if (!he->leaf) {
+ perf_gtk__add_hierarchy_entries(hists, &he->hroot_out,
+ store, &iter, hpp,
+ min_pcnt);
+ }
+
+ if (symbol_conf.use_callchain && he->leaf) {
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ total = symbol_conf.cumulate_callchain ?
+ he->stat_acc->period : he->stat.period;
+
+ perf_gtk__add_callchain(&he->sorted_chain, store, &iter,
+ col_idx, total);
+ }
+ }
+
+}
+
+static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
+ float min_pcnt)
+{
+ struct perf_hpp_fmt *fmt;
+ GType col_types[MAX_COLUMNS];
+ GtkCellRenderer *renderer;
+ GtkTreeStore *store;
+ GtkWidget *view;
+ int col_idx;
+ int nr_cols = 0;
+ char s[512];
+ char buf[512];
+ bool first = true;
+ struct perf_hpp hpp = {
+ .buf = s,
+ .size = sizeof(s),
+ };
+
+ perf_hpp__for_each_format(fmt) {
+ if (perf_hpp__is_sort_entry(fmt) ||
+ perf_hpp__is_dynamic_entry(fmt))
+ break;
+
+ col_types[nr_cols++] = G_TYPE_STRING;
+ }
+ col_types[nr_cols++] = G_TYPE_STRING;
+
+ store = gtk_tree_store_newv(nr_cols, col_types);
+ view = gtk_tree_view_new();
+ renderer = gtk_cell_renderer_text_new();
+
+ col_idx = 0;
+ perf_hpp__for_each_format(fmt) {
+ if (perf_hpp__is_sort_entry(fmt) ||
+ perf_hpp__is_dynamic_entry(fmt))
+ break;
+
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, fmt->name,
+ renderer, "markup",
+ col_idx++, NULL);
+ }
+
+ /* construct merged column header since sort keys share single column */
+ buf[0] = '\0';
+ perf_hpp__for_each_format(fmt) {
+ if (!perf_hpp__is_sort_entry(fmt) &&
+ !perf_hpp__is_dynamic_entry(fmt))
+ continue;
+
+ if (first)
+ first = false;
+ else
+ strcat(buf, " / ");
+
+ fmt->header(fmt, &hpp, hists_to_evsel(hists));
+ strcat(buf, rtrim(hpp.buf));
+ }
+
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, buf,
+ renderer, "markup",
+ col_idx++, NULL);
+
+ for (col_idx = 0; col_idx < nr_cols; col_idx++) {
+ GtkTreeViewColumn *column;
+
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+
+ if (col_idx == 0) {
+ gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
+ column);
+ }
+ }
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+ g_object_unref(GTK_TREE_MODEL(store));
+
+ perf_gtk__add_hierarchy_entries(hists, &hists->entries, store,
+ NULL, &hpp, min_pcnt);
+
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
+
+ g_signal_connect(view, "row-activated",
+ G_CALLBACK(on_row_activated), NULL);
+ gtk_container_add(GTK_CONTAINER(window), view);
+}
+
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *help,
struct hist_browser_timer *hbt __maybe_unused,
@@ -463,7 +619,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
- perf_gtk__show_hists(scrolled_window, hists, min_pcnt);
+ if (symbol_conf.report_hierarchy)
+ perf_gtk__show_hierarchy(scrolled_window, hists, min_pcnt);
+ else
+ perf_gtk__show_hists(scrolled_window, hists, min_pcnt);
tab_label = gtk_label_new(evname);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 17/21] perf hists browser: Align column header in hierarchy mode |
| Message-ID | <qXKri-4kd-67@gated-at.bofh.it> |
| In reply to | #1324158 |
Like in stdio, fit column header to hierarchy output. Merge column
headers with "/" as a separator.
Overhead Command / Shared Object / Symbol
...
+ 0.09% dwm
+ 0.06% emacs
- 0.05% perf
- 0.05% [kernel.vmlinux]
+ 0.03% [k] memcpy_orig
+ 0.01% [k] unmap_single_vma
+ 0.01% [k] smp_call_function_single
+ 0.00% [k] native_irq_return_iret
+ 0.00% [k] arch_trigger_all_cpu_backtrace_handler
+ 0.00% [k] native_write_msr_safe
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/ui/browsers/hists.c | 69 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 54fe59aea53e..f6ab8ec1bd96 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -1427,11 +1427,78 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *
return ret;
}
+static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
+{
+ struct hists *hists = browser->hists;
+ struct perf_hpp dummy_hpp = {
+ .buf = buf,
+ .size = size,
+ };
+ struct perf_hpp_fmt *fmt;
+ size_t ret = 0;
+ int column = 0;
+ int nr_sort_keys = perf_hpp__count_sort_keys();
+ bool first = true;
+
+ ret = scnprintf(buf, size, " ");
+ if (advance_hpp_check(&dummy_hpp, ret))
+ return ret;
+
+ perf_hpp__for_each_format(fmt) {
+ if (column++ < browser->b.horiz_scroll)
+ continue;
+
+ if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
+ break;
+
+ ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
+ if (advance_hpp_check(&dummy_hpp, ret))
+ break;
+
+ ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
+ if (advance_hpp_check(&dummy_hpp, ret))
+ break;
+ }
+
+ ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
+ (nr_sort_keys - 1) * HIERARCHY_INDENT, "");
+ if (advance_hpp_check(&dummy_hpp, ret))
+ return ret;
+
+ perf_hpp__for_each_format(fmt) {
+ if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt))
+ continue;
+
+ if (first) {
+ first = false;
+ } else {
+ ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
+ if (advance_hpp_check(&dummy_hpp, ret))
+ break;
+ }
+
+ ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
+ dummy_hpp.buf[ret] = '\0';
+ rtrim(dummy_hpp.buf);
+
+ ret = strlen(dummy_hpp.buf);
+ if (advance_hpp_check(&dummy_hpp, ret))
+ break;
+ }
+
+ return ret;
+}
+
static void hist_browser__show_headers(struct hist_browser *browser)
{
char headers[1024];
- hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
+ if (symbol_conf.report_hierarchy)
+ hists_browser__scnprintf_hierarchy_headers(browser, headers,
+ sizeof(headers));
+ else
+ hists_browser__scnprintf_headers(browser, headers,
+ sizeof(headers));
ui_browser__gotorc(&browser->b, 0, 0);
ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 21/21] perf top: Add --hierarchy option |
| Message-ID | <qXKri-4kd-71@gated-at.bofh.it> |
| In reply to | #1324158 |
Support hierarchy output for perf-top using --hierarchy option.
Acked-by: Pekka Enberg <penberg@kernel.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/Documentation/perf-top.txt | 3 +++
tools/perf/builtin-top.c | 15 +++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index b0e60e17db38..19f046f027cd 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -233,6 +233,9 @@ Default is to monitor all CPUS.
--raw-trace::
When displaying traceevent output, do not use print fmt or plugins.
+--hierarchy::
+ Enable hierarchy output.
+
INTERACTIVE PROMPTING KEYS
--------------------------
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index bf01cbb0ef23..5da5b098f141 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1212,6 +1212,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
parse_branch_stack),
OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace,
"Show raw trace event output (do not use print fmt or plugins)"),
+ OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy,
+ "Show entries in a hierarchy"),
OPT_END()
};
const char * const top_usage[] = {
@@ -1239,6 +1241,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
goto out_delete_evlist;
}
+ if (symbol_conf.report_hierarchy) {
+ /* disable incompatible options */
+ symbol_conf.event_group = false;
+ symbol_conf.cumulate_callchain = false;
+
+ if (field_order) {
+ pr_err("Error: --hierarchy and --fields options cannot be used together\n");
+ parse_options_usage(top_usage, options, "fields", 0);
+ parse_options_usage(NULL, options, "hierarchy", 0);
+ goto out_delete_evlist;
+ }
+ }
+
sort__mode = SORT_MODE__TOP;
/* display thread wants entries to be collapsed in a different tree */
sort__need_collapse = 1;
--
2.6.4
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2016-02-02 15:50 +0100 |
| Subject | [PATCH 02/21] perf callchain: Check return value of fill_node() |
| Message-ID | <qXKri-4kd-73@gated-at.bofh.it> |
| In reply to | #1324158 |
Memory allocation in the fill_node() can fail so change its return type
to int and check it in add_child() too.
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/callchain.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 134d88b33fc1..a82ea6f6fc0f 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -416,7 +416,7 @@ create_child(struct callchain_node *parent, bool inherit_children)
/*
* Fill the node with callchain values
*/
-static void
+static int
fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
{
struct callchain_cursor_node *cursor_node;
@@ -433,7 +433,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
call = zalloc(sizeof(*call));
if (!call) {
perror("not enough memory for the code path tree");
- return;
+ return -1;
}
call->ip = cursor_node->ip;
call->ms.sym = cursor_node->sym;
@@ -443,6 +443,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
callchain_cursor_advance(cursor);
cursor_node = callchain_cursor_current(cursor);
}
+ return 0;
}
static struct callchain_node *
@@ -456,7 +457,16 @@ add_child(struct callchain_node *parent,
if (new == NULL)
return NULL;
- fill_node(new, cursor);
+ if (fill_node(new, cursor) < 0) {
+ struct callchain_list *call, *tmp;
+
+ list_for_each_entry_safe(call, tmp, &new->val, list) {
+ list_del(&call->list);
+ free(call);
+ }
+ free(new);
+ return NULL;
+ }
new->children_hit = 0;
new->hit = period;
--
2.6.4
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web