Skip to contentSkip to conversation

perf/core improvements and fixes

GIT PULL New Arnaldo Carvalho de Melo 2 participants 20 messages Aug 22, 2014 – Aug 24, 2014 perf events

lore
Share & tools
lore

Arnaldo Carvalho de Melo

author maintainer
#0 lore raw

Hi Ingo,

Please consider pulling,

- Arnaldo

The following changes since commit af924aa35129edf338ebc8da6a4eae08cf7cb297:

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2014-08-18 10:14:43 +0200)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo

for you to fetch changes up to 141da74d2eff8de134c5a5ffaf2800a8a1b27517:

perf hists browser: Consolidate callchain print functions in TUI (2014-08-22 13:12:15 -0300)

----------------------------------------------------------------
perf/core improvements and fixes:

User visible:

o Don't try to find DSOs in SYSV maps (Don Zickus)

o Fallback to MAP__FUNCTION if daddr maps are NULL, i.e. addresses get looked upon more maps (Don Zickus)

o Kernel fix to properly handle exited tasks, by returning POLLHUP values on perf event file descriptors. Tooling changes will come next, but were tested with this kernel fix. (Jiri Olsa)

o Add +field argument support for --field option, so that one can add fields to the default list of fields to show, i.e. now one can just do:

   perf report --fields +pid

And the pid will appear in addition to the default fields. (Jiri Olsa)

Developer stuff:

o More Intel PT prep stuff, including:
- Add a 'perf test' for tracking with sched_switch
- Add 'flush' callback to scripting API

o hists browser (used in top and report) refactorings, getting rid of unused variables and reducing source code size by handling similar cases in a fewer functions (Namhyung Kim).

o Explicitly include util/debug.h for powerpc, was being indirectly included, broke the build when some change made it stop being included. (Sukadev Bhattiprolu)

----------------------------------------------------------------
Adrian Hunter (4):
perf tests: Add a test for tracking with sched_switch perf scripting: Add 'flush' callback to scripting API perf machine: Rename machine__get_kernel_start_addr() method perf tools: Add machine__kernel_ip()

Don Zickus (2):
perf symbols: Don't try to find DSOs in SYSV maps perf machine: Fallback to MAP__FUNCTION if daddr maps are NULL

Jiri Olsa (4):
perf: Fix perf_poll to return proper POLLHUP value
perf: Add PERF_EVENT_STATE_EXIT state for events with exited task perf top: Use set_term_quiet() instead of open coded equivalent perf tools: Add +field argument support for --field option

Namhyung Kim (5):
perf hists browser: Get rid of unused 'remaining' variable perf hists browser: Fix children overhead dump
perf hists browser: Factor out hist_browser__show_callchain_entry() perf hists browser: Cleanup callchain print functions perf hists browser: Consolidate callchain print functions in TUI

Sukadev Bhattiprolu (1):
perf tools powerpc: Explicitly include util/debug.h

 include/linux/perf_event.h                         |   1 +
 kernel/events/core.c                               |  12 +-
 tools/perf/Makefile.perf                           |   1 +
 tools/perf/arch/powerpc/util/skip-callchain-idx.c  |   1 +
 tools/perf/builtin-script.c                        |  13 +
 tools/perf/builtin-top.c                           |   9 +-
 tools/perf/tests/builtin-test.c                    |   4 +
 tools/perf/tests/switch-tracking.c                 | 572 +++++++++++++++++++++
 tools/perf/tests/tests.h                           |   1 +
 tools/perf/ui/browsers/hists.c                     | 343 +++++-------
 tools/perf/ui/hist.c                               |   6 +-
 tools/perf/util/event.c                            |   6 +-
 tools/perf/util/machine.c                          |  41 +-
 tools/perf/util/machine.h                          |  17 +
 tools/perf/util/map.c                              |   1 +
 .../perf/util/scripting-engines/trace-event-perl.c |   6 +
 .../util/scripting-engines/trace-event-python.c    |   6 +
 tools/perf/util/sort.c                             |  24 +-
 tools/perf/util/sort.h                             |   1 +
 tools/perf/util/trace-event-scripting.c            |   7 +
 tools/perf/util/trace-event.h                      |   1 +
 21 files changed, 827 insertions(+), 246 deletions(-)
 create mode 100644 tools/perf/tests/switch-tracking.c
author maintainer
#1 lore raw

PATCH 1/16 perf symbols: Don't try to find DSOs in SYSV maps

From: Don Zickus <[email protected]>

We are seeing a lot of the following with regards to SYSV memory

Failed to open /SYSV0000279c, continuing without symbols

We don't believe this memory will have DSO info, so treat it like the heap and stack for now and skip it to prevent the warning.

---
 tools/perf/util/map.c | 1 +
 1 file changed, 1 insertion(+)
tools/perf/util/map.c +1 -0
index 31b8905dd863..b7090596ac50 100644
@@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename)
 static inline int is_no_dso_memory(const char *filename)
 {
 	return !strncmp(filename, "[stack", 6) ||
+	       !strncmp(filename, "/SYSV",5)   ||
 	       !strcmp(filename, "[heap]");
 }
 
Signature · 1 line
1.9.3
author maintainer
#2 lore raw

PATCH 2/16 perf tools powerpc: Explicitly include util/debug.h

In reply to Arnaldo Carvalho de Melo

From: Sukadev Bhattiprolu <[email protected]>

Looks like util/debug.h was indirectly included before and is no longer included now. pr_debug is left undefined and the build of perf tool fails on Powerpc.

Explicitly include util/debug.h.

---
 tools/perf/arch/powerpc/util/skip-callchain-idx.c | 1 +
 1 file changed, 1 insertion(+)
tools/perf/arch/powerpc/util/skip-callchain-idx.c +1 -0
index a7c23a4b3778..d73ef8bb08c7 100644
@@ -15,6 +15,7 @@
 
 #include "util/thread.h"
 #include "util/callchain.h"
+#include "util/debug.h"
 
 /*
  * When saving the callchain on Power, the kernel conservatively saves
Signature · 1 line
1.9.3
author maintainer
#3 lore raw

PATCH 3/16 perf hists browser: Get rid of unused 'remaining' variable

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

It seems that the 'remaining' variable is not used by any real code.

---
 tools/perf/ui/browsers/hists.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)
tools/perf/ui/browsers/hists.c +2 -8
index 4892480e8298..2f34c6b6d5dc 100644
@@ -488,14 +488,13 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 {
 	struct rb_node *node;
 	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
-	u64 new_total, remaining;
+	u64 new_total;
 
 	if (callchain_param.mode == CHAIN_GRAPH_REL)
 		new_total = chain_node->children_hit;
 	else
 		new_total = total;
 
-	remaining = new_total;
 	node = rb_first(&chain_node->rb_root);
 	while (node) {
 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
@@ -506,8 +505,6 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 		int first = true;
 		int extra_offset = 0;
 
-		remaining -= cumul;
-
 		list_for_each_entry(chain, &child->val, list) {
 			char bf[1024], *alloc_str;
 			const char *str;
@@ -1084,7 +1081,7 @@ static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *bro
 {
 	struct rb_node *node;
 	int offset = level * LEVEL_OFFSET_STEP;
-	u64 new_total, remaining;
+	u64 new_total;
 	int printed = 0;
 
 	if (callchain_param.mode == CHAIN_GRAPH_REL)
@@ -1092,7 +1089,6 @@ static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *bro
 	else
 		new_total = total;
 
-	remaining = new_total;
 	node = rb_first(&chain_node->rb_root);
 	while (node) {
 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
@@ -1103,8 +1099,6 @@ static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *bro
 		int first = true;
 		int extra_offset = 0;
 
-		remaining -= cumul;
-
 		list_for_each_entry(chain, &child->val, list) {
 			char bf[1024], *alloc_str;
 			const char *str;
Signature · 1 line
1.9.3
author maintainer
#4 lore raw

PATCH 4/16 perf hists browser: Fix children overhead dump

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

When perf report runs on TUI, 'P' key dumps current screen to a file but it incorrectly displayed children overhead (as same of self overhead). This was because it fetched the value from self stats.

---
 tools/perf/ui/hist.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/perf/ui/hist.c +1 -1
index b5fa7019d2e2..75eb6ac821f8 100644
@@ -304,7 +304,7 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt,				\
 static int hpp__entry_##_type(struct perf_hpp_fmt *fmt,				\
 			      struct perf_hpp *hpp, struct hist_entry *he) 	\
 {										\
-	return hpp__fmt_acc(fmt, hpp, he, he_get_##_field, " %*.2f%%",		\
+	return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%",	\
 			    hpp_entry_scnprintf, true);				\
 }
 
Signature · 1 line
1.9.3
author maintainer
#5 lore raw

PATCH 5/16 perf hists browser: Factor out hist_browser__show_callchain_entry()

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

Factor out duplicate callchain printing code into the hist_browser__ show_callchain_entry().

---
 tools/perf/ui/browsers/hists.c | 64 ++++++++++++++++++++++--------------------
 1 file changed, 33 insertions(+), 31 deletions(-)
tools/perf/ui/browsers/hists.c +33 -31
index 2f34c6b6d5dc..d42d8a8f3810 100644
@@ -477,6 +477,29 @@ static char *callchain_list__sym_name(struct callchain_list *cl,
 	return bf;
 }
 
+static void hist_browser__show_callchain_entry(struct hist_browser *browser,
+					       struct callchain_list *chain,
+					       unsigned short row, int offset,
+					       char folded_sign, const char *str,
+					       bool *is_current_entry)
+{
+	int color, width;
+
+	color = HE_COLORSET_NORMAL;
+	width = browser->b.width - (offset + 2);
+	if (ui_browser__is_current_entry(&browser->b, row)) {
+		browser->selection = &chain->ms;
+		color = HE_COLORSET_SELECTED;
+		*is_current_entry = true;
+	}
+
+	ui_browser__set_color(&browser->b, color);
+	hist_browser__gotorc(browser, row, 0);
+	slsmg_write_nstring(" ", offset);
+	slsmg_printf("%c ", folded_sign);
+	slsmg_write_nstring(str, width);
+}
+
 #define LEVEL_OFFSET_STEP 3
 
 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
@@ -487,7 +510,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 						     bool *is_current_entry)
 {
 	struct rb_node *node;
-	int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
+	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
 	u64 new_total;
 
 	if (callchain_param.mode == CHAIN_GRAPH_REL)
@@ -508,7 +531,6 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 		list_for_each_entry(chain, &child->val, list) {
 			char bf[1024], *alloc_str;
 			const char *str;
-			int color;
 			bool was_first = first;
 
 			if (first)
@@ -534,19 +556,10 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 					str = alloc_str;
 			}
 
-			color = HE_COLORSET_NORMAL;
-			width = browser->b.width - (offset + extra_offset + 2);
-			if (ui_browser__is_current_entry(&browser->b, row)) {
-				browser->selection = &chain->ms;
-				color = HE_COLORSET_SELECTED;
-				*is_current_entry = true;
-			}
-
-			ui_browser__set_color(&browser->b, color);
-			hist_browser__gotorc(browser, row, 0);
-			slsmg_write_nstring(" ", offset + extra_offset);
-			slsmg_printf("%c ", folded_sign);
-			slsmg_write_nstring(str, width);
+			hist_browser__show_callchain_entry(browser, chain, row,
+							   offset + extra_offset,
+							   folded_sign, str,
+							   is_current_entry);
 			free(alloc_str);
 
 			if (++row == browser->b.rows)
@@ -577,14 +590,12 @@ static int hist_browser__show_callchain_node(struct hist_browser *browser,
 					     bool *is_current_entry)
 {
 	struct callchain_list *chain;
-	int first_row = row,
-	     offset = level * LEVEL_OFFSET_STEP,
-	     width = browser->b.width - offset;
+	int first_row = row;
+	int offset = level * LEVEL_OFFSET_STEP;
 	char folded_sign = ' ';
 
 	list_for_each_entry(chain, &node->val, list) {
 		char bf[1024], *s;
-		int color;
 
 		folded_sign = callchain_list__folded(chain);
 
@@ -593,20 +604,11 @@ static int hist_browser__show_callchain_node(struct hist_browser *browser,
 			continue;
 		}
 
-		color = HE_COLORSET_NORMAL;
-		if (ui_browser__is_current_entry(&browser->b, row)) {
-			browser->selection = &chain->ms;
-			color = HE_COLORSET_SELECTED;
-			*is_current_entry = true;
-		}
-
 		s = callchain_list__sym_name(chain, bf, sizeof(bf),
 					     browser->show_dso);
-		hist_browser__gotorc(browser, row, 0);
-		ui_browser__set_color(&browser->b, color);
-		slsmg_write_nstring(" ", offset);
-		slsmg_printf("%c ", folded_sign);
-		slsmg_write_nstring(s, width - 2);
+		hist_browser__show_callchain_entry(browser, chain, row,
+						   offset, folded_sign, s,
+						   is_current_entry);
 
 		if (++row == browser->b.rows)
 			goto out;
Signature · 1 line
1.9.3
author maintainer
#6 lore raw

PATCH 6/16 perf tests: Add a test for tracking with sched_switch

In reply to Arnaldo Carvalho de Melo

From: Adrian Hunter <[email protected]>

Add a test that checks that sched_switch events and tracking events can be recorded for a workload using the evsel->system_wide and evsel->tracking flags (respectively) with other events sometimes enabled or disabled.

Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
[ Fix build on fedora14 by using a designated initializer for the sched_switch variable ]
---
 tools/perf/Makefile.perf           |   1 +
 tools/perf/tests/builtin-test.c    |   4 +
 tools/perf/tests/switch-tracking.c | 572 +++++++++++++++++++++++++++++++++++++
 tools/perf/tests/tests.h           |   1 +
 4 files changed, 578 insertions(+)
 create mode 100644 tools/perf/tests/switch-tracking.c
tools/perf/Makefile.perf +1 -0
index 1ea31e275b4d..95e832b1bc3c 100644
@@ -425,6 +425,7 @@ endif
 endif
 LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
 LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
+LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
 
 BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
 BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
tools/perf/tests/builtin-test.c +4 -0
index 99481361b19f..6a4145e5ad2c 100644
@@ -154,6 +154,10 @@ static struct test {
 		.func = test__hists_cumulate,
 	},
 	{
+		.desc = "Test tracking with sched_switch",
+		.func = test__switch_tracking,
+	},
+	{
 		.func = NULL,
 	},
 };
tools/perf/tests/switch-tracking.c new file +572 -0

572 lines not on this page · raw message

tools/perf/tests/tests.h +1 -0
index ed64790a395f..be8be10e3957 100644
@@ -48,6 +48,7 @@ int test__mmap_thread_lookup(void);
 int test__thread_mg_share(void);
 int test__hists_output(void);
 int test__hists_cumulate(void);
+int test__switch_tracking(void);
 
 #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
Signature · 1 line
1.9.3
author maintainer
#7 lore raw

PATCH 7/16 perf scripting: Add 'flush' callback to scripting API

In reply to Arnaldo Carvalho de Melo

From: Adrian Hunter <[email protected]>

In order to defer some output via the scripting API, there needs to be a callback after session processing but before the session is deleted.

---
 tools/perf/builtin-script.c                            | 13 +++++++++++++
 tools/perf/util/scripting-engines/trace-event-perl.c   |  6 ++++++
 tools/perf/util/scripting-engines/trace-event-python.c |  6 ++++++
 tools/perf/util/trace-event-scripting.c                |  7 +++++++
 tools/perf/util/trace-event.h                          |  1 +
 5 files changed, 33 insertions(+)
tools/perf/builtin-script.c +13 -0
index c1b7029884b1..02dce9295e2c 100644
@@ -485,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused,
 	return 0;
 }
 
+static int default_flush_script(void)
+{
+	return 0;
+}
+
 static int default_stop_script(void)
 {
 	return 0;
@@ -498,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused,
 
 static struct scripting_ops default_scripting_ops = {
 	.start_script		= default_start_script,
+	.flush_script		= default_flush_script,
 	.stop_script		= default_stop_script,
 	.process_event		= process_event,
 	.generate_script	= default_generate_script,
@@ -513,6 +519,11 @@ static void setup_scripting(void)
 	scripting_ops = &default_scripting_ops;
 }
 
+static int flush_scripting(void)
+{
+	return scripting_ops->flush_script();
+}
+
 static int cleanup_scripting(void)
 {
 	pr_debug("\nperf script stopped\n");
@@ -1813,6 +1824,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
 
 	err = __cmd_script(&script);
 
+	flush_scripting();
+
 out_delete:
 	perf_session__delete(session);
 
tools/perf/util/scripting-engines/trace-event-perl.c +6 -0
index b2dba9c0a3a1..0a01bac4ce02 100644
@@ -432,6 +432,11 @@ error:
 	return err;
 }
 
+static int perl_flush_script(void)
+{
+	return 0;
+}
+
 /*
  * Stop trace script
  */
@@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
 struct scripting_ops perl_scripting_ops = {
 	.name = "Perl",
 	.start_script = perl_start_script,
+	.flush_script = perl_flush_script,
 	.stop_script = perl_stop_script,
 	.process_event = perl_process_event,
 	.generate_script = perl_generate_script,
tools/perf/util/scripting-engines/trace-event-python.c +6 -0
index 26e5f14239ed..56ba07cce549 100644
@@ -639,6 +639,11 @@ error:
 	return err;
 }
 
+static int python_flush_script(void)
+{
+	return 0;
+}
+
 /*
  * Stop trace script
  */
@@ -823,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
 struct scripting_ops python_scripting_ops = {
 	.name = "Python",
 	.start_script = python_start_script,
+	.flush_script = python_flush_script,
 	.stop_script = python_stop_script,
 	.process_event = python_process_event,
 	.generate_script = python_generate_script,
tools/perf/util/trace-event-scripting.c +7 -0
index 57aaccc1692e..5c9bdd1591a9 100644
@@ -30,6 +30,11 @@
 
 struct scripting_context *scripting_context;
 
+static int flush_script_unsupported(void)
+{
+	return 0;
+}
+
 static int stop_script_unsupported(void)
 {
 	return 0;
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent
 struct scripting_ops python_scripting_unsupported_ops = {
 	.name = "Python",
 	.start_script = python_start_script_unsupported,
+	.flush_script = flush_script_unsupported,
 	.stop_script = stop_script_unsupported,
 	.process_event = process_event_unsupported,
 	.generate_script = python_generate_script_unsupported,
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent
 struct scripting_ops perl_scripting_unsupported_ops = {
 	.name = "Perl",
 	.start_script = perl_start_script_unsupported,
+	.flush_script = flush_script_unsupported,
 	.stop_script = stop_script_unsupported,
 	.process_event = process_event_unsupported,
 	.generate_script = perl_generate_script_unsupported,
tools/perf/util/trace-event.h +1 -0
index 7b6d68688327..52aaa19e1eb1 100644
@@ -64,6 +64,7 @@ struct perf_session;
 struct scripting_ops {
 	const char *name;
 	int (*start_script) (const char *script, int argc, const char **argv);
+	int (*flush_script) (void);
 	int (*stop_script) (void);
 	void (*process_event) (union perf_event *event,
 			       struct perf_sample *sample,
Signature · 1 line
1.9.3
author maintainer
#8 lore raw

PATCH 8/16 perf machine: Rename machine__get_kernel_start_addr() method

In reply to Arnaldo Carvalho de Melo

From: Adrian Hunter <[email protected]>

Rename machine__get_kernel_start_addr() to machine__get_running_kernel_start() so that a new function, with a similar name to the original name, can be added that gets the kernel start address from the kernel map.

---
 tools/perf/util/machine.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
tools/perf/util/machine.c +4 -4
index b093b93607fb..37f8dc557ec0 100644
@@ -593,8 +593,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
  * Returns the name of the start symbol in *symbol_name. Pass in NULL as
  * symbol_name if it's not that important.
  */
-static u64 machine__get_kernel_start_addr(struct machine *machine,
-					  const char **symbol_name)
+static u64 machine__get_running_kernel_start(struct machine *machine,
+					     const char **symbol_name)
 {
 	char filename[PATH_MAX];
 	int i;
@@ -621,7 +621,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine,
 int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
 {
 	enum map_type type;
-	u64 start = machine__get_kernel_start_addr(machine, NULL);
+	u64 start = machine__get_running_kernel_start(machine, NULL);
 
 	for (type = 0; type < MAP__NR_TYPES; ++type) {
 		struct kmap *kmap;
@@ -940,7 +940,7 @@ int machine__create_kernel_maps(struct machine *machine)
 {
 	struct dso *kernel = machine__get_kernel(machine);
 	const char *name;
-	u64 addr = machine__get_kernel_start_addr(machine, &name);
+	u64 addr = machine__get_running_kernel_start(machine, &name);
 	if (!addr)
 		return -1;
 
Signature · 1 line
1.9.3
author maintainer
#9 lore raw

PATCH 9/16 perf tools: Add machine__kernel_ip()

In reply to Arnaldo Carvalho de Melo

From: Adrian Hunter <[email protected]>

Add a function to determine if an address is in the kernel. This is based on the kernel function kernel_ip().

---
 tools/perf/util/event.c   |  6 +++---
 tools/perf/util/machine.c | 23 +++++++++++++++++++++++
 tools/perf/util/machine.h | 17 +++++++++++++++++
 3 files changed, 43 insertions(+), 3 deletions(-)
tools/perf/util/event.c +3 -3
index 1398c83d896d..ed558191c0b3 100644
@@ -784,9 +784,9 @@ try_again:
 		 * "[vdso]" dso, but for now lets use the old trick of looking
 		 * in the whole kernel symbol list.
 		 */
-		if ((long long)al->addr < 0 &&
-		    cpumode == PERF_RECORD_MISC_USER &&
-		    machine && mg != &machine->kmaps) {
+		if (cpumode == PERF_RECORD_MISC_USER && machine &&
+		    mg != &machine->kmaps &&
+		    machine__kernel_ip(machine, al->addr)) {
 			mg = &machine->kmaps;
 			load_map = true;
 			goto try_again;
tools/perf/util/machine.c +23 -0
index 37f8dc557ec0..e00daf0d2bde 100644
@@ -32,6 +32,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
 	machine->symbol_filter = NULL;
 	machine->id_hdr_size = 0;
 	machine->comm_exec = false;
+	machine->kernel_start = 0;
 
 	machine->root_dir = strdup(root_dir);
 	if (machine->root_dir == NULL)
@@ -1559,3 +1560,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
 
 	return 0;
 }
+
+int machine__get_kernel_start(struct machine *machine)
+{
+	struct map *map = machine__kernel_map(machine, MAP__FUNCTION);
+	int err = 0;
+
+	/*
+	 * The only addresses above 2^63 are kernel addresses of a 64-bit
+	 * kernel.  Note that addresses are unsigned so that on a 32-bit system
+	 * all addresses including kernel addresses are less than 2^32.  In
+	 * that case (32-bit system), if the kernel mapping is unknown, all
+	 * addresses will be assumed to be in user space - see
+	 * machine__kernel_ip().
+	 */
+	machine->kernel_start = 1ULL << 63;
+	if (map) {
+		err = map__load(map, machine->symbol_filter);
+		if (map->start)
+			machine->kernel_start = map->start;
+	}
+	return err;
+}
tools/perf/util/machine.h +17 -0
index 61216e028319..6a6bcc1cff54 100644
@@ -36,6 +36,7 @@ struct machine {
 	struct list_head  kernel_dsos;
 	struct map_groups kmaps;
 	struct map	  *vmlinux_maps[MAP__NR_TYPES];
+	u64		  kernel_start;
 	symbol_filter_t	  symbol_filter;
 	pid_t		  *current_tid;
 };
@@ -46,6 +47,22 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
 	return machine->vmlinux_maps[type];
 }
 
+int machine__get_kernel_start(struct machine *machine);
+
+static inline u64 machine__kernel_start(struct machine *machine)
+{
+	if (!machine->kernel_start)
+		machine__get_kernel_start(machine);
+	return machine->kernel_start;
+}
+
+static inline bool machine__kernel_ip(struct machine *machine, u64 ip)
+{
+	u64 kernel_start = machine__kernel_start(machine);
+
+	return ip >= kernel_start;
+}
+
 struct thread *machine__find_thread(struct machine *machine, pid_t pid,
 				    pid_t tid);
 struct comm *machine__thread_exec_comm(struct machine *machine,
Signature · 1 line
1.9.3
author maintainer
#10 lore raw

PATCH 10/16 perf hists browser: Cleanup callchain print functions

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

The hist_browser__show_callchain() and friends don't need to be that complex. They're splitted in 3 pieces - one for traversing top-level tree, other one for special casing first chains in the top-level entries, and last one for recursive traversing inner trees. It led to code duplication and unnecessary complexity IMHO.

Simplify the function and consolidate the logic into a single function
- it can recursively call itself. A little difference in printing callchains in top-level tree can be handled with a small change.

It should have no functional change.

---
 tools/perf/ui/browsers/hists.c | 112 +++++++++++------------------------------
 1 file changed, 29 insertions(+), 83 deletions(-)
tools/perf/ui/browsers/hists.c +29 -83
index d42d8a8f3810..519353d9f5fb 100644
@@ -502,23 +502,16 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 
 #define LEVEL_OFFSET_STEP 3
 
-static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
-						     struct callchain_node *chain_node,
-						     u64 total, int level,
-						     unsigned short row,
-						     off_t *row_offset,
-						     bool *is_current_entry)
+static int hist_browser__show_callchain(struct hist_browser *browser,
+					struct rb_root *root, int level,
+					unsigned short row, off_t *row_offset,
+					u64 total, bool *is_current_entry)
 {
 	struct rb_node *node;
 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
 	u64 new_total;
 
-	if (callchain_param.mode == CHAIN_GRAPH_REL)
-		new_total = chain_node->children_hit;
-	else
-		new_total = total;
-
-	node = rb_first(&chain_node->rb_root);
+	node = rb_first(root);
 	while (node) {
 		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
 		struct rb_node *next = rb_next(node);
@@ -535,7 +528,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 
 			if (first)
 				first = false;
-			else
+			else if (level > 1)
 				extra_offset = LEVEL_OFFSET_STEP;
 
 			folded_sign = callchain_list__folded(chain);
@@ -547,8 +540,9 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
 			alloc_str = NULL;
 			str = callchain_list__sym_name(chain, bf, sizeof(bf),
 						       browser->show_dso);
-			if (was_first) {
-				double percent = cumul * 100.0 / new_total;
+
+			if (was_first && level > 1) {
+				double percent = cumul * 100.0 / total;
 
 				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
 					str = "Not enough memory!";
@@ -571,78 +565,23 @@ do_next:
 
 		if (folded_sign == '-') {
 			const int new_level = level + (extra_offset ? 2 : 1);
-			row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
-									 new_level, row, row_offset,
-									 is_current_entry);
-		}
-		if (row == browser->b.rows)
-			goto out;
-		node = next;
-	}
-out:
-	return row - first_row;
-}
-
-static int hist_browser__show_callchain_node(struct hist_browser *browser,
-					     struct callchain_node *node,
-					     int level, unsigned short row,
-					     off_t *row_offset,
-					     bool *is_current_entry)
-{
-	struct callchain_list *chain;
-	int first_row = row;
-	int offset = level * LEVEL_OFFSET_STEP;
-	char folded_sign = ' ';
-
-	list_for_each_entry(chain, &node->val, list) {
-		char bf[1024], *s;
 
-		folded_sign = callchain_list__folded(chain);
+			if (callchain_param.mode == CHAIN_GRAPH_REL)
+				new_total = child->children_hit;
+			else
+				new_total = total;
 
-		if (*row_offset != 0) {
-			--*row_offset;
-			continue;
+			row += hist_browser__show_callchain(browser, &child->rb_root,
+							    new_level,
+							    row, row_offset,
+							    new_total,
+							    is_current_entry);
 		}
-
-		s = callchain_list__sym_name(chain, bf, sizeof(bf),
-					     browser->show_dso);
-		hist_browser__show_callchain_entry(browser, chain, row,
-						   offset, folded_sign, s,
-						   is_current_entry);
-
-		if (++row == browser->b.rows)
-			goto out;
-	}
-
-	if (folded_sign == '-')
-		row += hist_browser__show_callchain_node_rb_tree(browser, node,
-								 browser->hists->stats.total_period,
-								 level + 1, row,
-								 row_offset,
-								 is_current_entry);
-out:
-	return row - first_row;
-}
-
-static int hist_browser__show_callchain(struct hist_browser *browser,
-					struct rb_root *chain,
-					int level, unsigned short row,
-					off_t *row_offset,
-					bool *is_current_entry)
-{
-	struct rb_node *nd;
-	int first_row = row;
-
-	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
-		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
-
-		row += hist_browser__show_callchain_node(browser, node, level,
-							 row, row_offset,
-							 is_current_entry);
 		if (row == browser->b.rows)
 			break;
+		node = next;
 	}
-
+out:
 	return row - first_row;
 }
 
@@ -817,9 +756,16 @@ static int hist_browser__show_entry(struct hist_browser *browser,
 		--row_offset;
 
 	if (folded_sign == '-' && row != browser->b.rows) {
-		printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
+		u64 total = hists__total_period(entry->hists);
+
+		if (symbol_conf.cumulate_callchain)
+			total = entry->stat_acc->period;
+
+		printed += hist_browser__show_callchain(browser,
+							&entry->sorted_chain,
 							1, row, &row_offset,
-							&current_entry);
+							total, &current_entry);
+
 		if (current_entry)
 			browser->he_selection = entry;
 	}
Signature · 1 line
1.9.3
author maintainer
#11 lore raw

PATCH 11/16 perf machine: Fallback to MAP__FUNCTION if daddr maps are NULL

In reply to Arnaldo Carvalho de Melo

From: Don Zickus <[email protected]>

As we run "perf c2c" on more applications, we noticed we're missing significant samples from a common customer's application. Looking at the /proc/<pid>/maps file for the app, we see "rwxs" and "rwxp" permissions on many of the shared memory & heap regions, and on all the thread stacks.

Because those regions have the "x" bit set, perf marks them with a MAP_FUNCTION type. Hence ip_resolve_data() never finds load or store events coming from them.

We fixed this by re-calling thread__find_addr_location with MAP__FUNCTION in the case where map is NULL as a last ditch effort to map the sample before giving up and dropping it.

---
 tools/perf/util/machine.c | 10 ++++++++++
 1 file changed, 10 insertions(+)
tools/perf/util/machine.c +10 -0
index e00daf0d2bde..b2ec38bf211e 100644
@@ -1314,6 +1314,16 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,
 
 	thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
 				   &al);
+	if (al.map == NULL) {
+		/*
+		 * some shared data regions have execute bit set which puts
+		 * their mapping in the MAP__FUNCTION type array.
+		 * Check there as a fallback option before dropping the sample.
+		 */
+		thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr,
+					   &al);
+	}
+
 	ams->addr = addr;
 	ams->al_addr = al.addr;
 	ams->sym = al.sym;
Signature · 1 line
1.9.3
author maintainer
#12 lore raw

PATCH 12/16 perf: Fix perf_poll to return proper POLLHUP value

In reply to Arnaldo Carvalho de Melo

From: Jiri Olsa <[email protected]>

Currently perf_poll returns POLL_HUP in case of error, which is wrong, because poll syscall expects POLLHUP. The POLL_HUP is meant to be used for SIGIO state.

---
 kernel/events/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/events/core.c +1 -1
index 2d7363adf678..4575dd6e59ea 100644
@@ -3627,7 +3627,7 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
 {
 	struct perf_event *event = file->private_data;
 	struct ring_buffer *rb;
-	unsigned int events = POLL_HUP;
+	unsigned int events = POLLHUP;
 
 	poll_wait(file, &event->waitq, wait);
 	/*
Signature · 1 line
1.9.3
author maintainer
#13 lore raw

PATCH 13/16 perf: Add PERF_EVENT_STATE_EXIT state for events with exited task

In reply to Arnaldo Carvalho de Melo

From: Jiri Olsa <[email protected]>

Adding new perf event state to indicate that the monitored task has exited. In this case the event stays alive until the owner task exits or close the event fd while providing the last data through the read syscall and ring buffer.

Instead it needs to propagate the error info (monitored task has died) via poll and read syscalls by returning POLLHUP and 0 respectively.

---
 include/linux/perf_event.h |  1 +
 kernel/events/core.c       | 10 +++++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)
include/linux/perf_event.h +1 -0
index f0a1036b1911..893a0d07986f 100644
@@ -269,6 +269,7 @@ struct pmu {
  * enum perf_event_active_state - the states of a event
  */
 enum perf_event_active_state {
+	PERF_EVENT_STATE_EXIT		= -3,
 	PERF_EVENT_STATE_ERROR		= -2,
 	PERF_EVENT_STATE_OFF		= -1,
 	PERF_EVENT_STATE_INACTIVE	=  0,
kernel/events/core.c +9 -1
index 4575dd6e59ea..d8cb4d21a346 100644
@@ -3600,7 +3600,8 @@ perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
 	 * error state (i.e. because it was pinned but it couldn't be
 	 * scheduled on to the CPU at some point).
 	 */
-	if (event->state == PERF_EVENT_STATE_ERROR)
+	if ((event->state == PERF_EVENT_STATE_ERROR) ||
+	    (event->state == PERF_EVENT_STATE_EXIT))
 		return 0;
 
 	if (count < event->read_size)
@@ -3630,6 +3631,10 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
 	unsigned int events = POLLHUP;
 
 	poll_wait(file, &event->waitq, wait);
+
+	if (event->state == PERF_EVENT_STATE_EXIT)
+		return events;
+
 	/*
 	 * Pin the event->rb by taking event->mmap_mutex; otherwise
 	 * perf_event_set_output() can swizzle our rb and make us miss wakeups.
@@ -7588,6 +7593,9 @@ __perf_event_exit_task(struct perf_event *child_event,
 	if (child_event->parent) {
 		sync_child_event(child_event, child);
 		free_event(child_event);
+	} else {
+		child_event->state = PERF_EVENT_STATE_EXIT;
+		perf_event_wakeup(child_event);
 	}
 }
 
Signature · 1 line
1.9.3
author maintainer
#14 lore raw

PATCH 14/16 perf top: Use set_term_quiet() instead of open coded equivalent

In reply to Arnaldo Carvalho de Melo

From: Jiri Olsa <[email protected]>

Cc Adrian Hunter <[email protected]>
Cc Corey Ashford <[email protected]>
Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Jean Pihet <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
[ Yanked this out of a patch containing this and some other change ]
---
 tools/perf/builtin-top.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
tools/perf/builtin-top.c +2 -7
index a77ff6ca5fbd..9848e270b92c 100644
@@ -433,18 +433,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
 
 	if (!perf_top__key_mapped(top, c)) {
 		struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
-		struct termios tc, save;
+		struct termios save;
 
 		perf_top__print_mapped_keys(top);
 		fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
 		fflush(stdout);
 
-		tcgetattr(0, &save);
-		tc = save;
-		tc.c_lflag &= ~(ICANON | ECHO);
-		tc.c_cc[VMIN] = 0;
-		tc.c_cc[VTIME] = 0;
-		tcsetattr(0, TCSANOW, &tc);
+		set_term_quiet_input(&save);
 
 		poll(&stdin_poll, 1, -1);
 		c = getc(stdin);
Signature · 1 line
1.9.3
author maintainer
#15 lore raw

PATCH 15/16 perf tools: Add +field argument support for --field option

In reply to Arnaldo Carvalho de Melo

From: Jiri Olsa <[email protected]>

Adding support to add field(s) to default field order via using the '+' prefix, like for report:

  $ perf report
  Samples: 10  of event 'cycles', Event count (approx.): 4463799
  Overhead  Command  Shared Object      Symbol
    32.40%  ls       [kernel.kallsyms]  [k] filemap_fault
    28.19%  ls       [kernel.kallsyms]  [k] get_page_from_freelist
    23.38%  ls       [kernel.kallsyms]  [k] enqueue_entity
    15.04%  ls       [kernel.kallsyms]  [k] mmap_region
  $ perf report -F +period,sample
  Samples: 10  of event 'cycles', Event count (approx.): 4463799
  Overhead        Period       Samples  Command  Shared Object      Symbol
    32.40%       1446493             1  ls       [kernel.kallsyms]  [k] filemap_fault
    28.19%       1258486             1  ls       [kernel.kallsyms]  [k] get_page_from_freelist
    23.38%       1043754             1  ls       [kernel.kallsyms]  [k] enqueue_entity
    15.04%        671160             1  ls       [kernel.kallsyms]  [k] mmap_region

Works in general for commands using --field option.

Cc Corey Ashford <[email protected]>
Cc David Ahern <[email protected]>
Cc Frederic Weisbecker <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Jean Pihet <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
---
 tools/perf/ui/hist.c   |  4 ++--
 tools/perf/util/sort.c | 24 +++++++++++++++++++-----
 tools/perf/util/sort.h |  1 +
 3 files changed, 22 insertions(+), 7 deletions(-)
tools/perf/ui/hist.c +2 -2
index 75eb6ac821f8..2af18376b077 100644
@@ -452,7 +452,7 @@ void perf_hpp__init(void)
 	/*
 	 * If user specified field order, no need to setup default fields.
 	 */
-	if (field_order)
+	if (is_strict_order(field_order))
 		return;
 
 	if (symbol_conf.cumulate_callchain) {
@@ -519,7 +519,7 @@ void perf_hpp__column_disable(unsigned col)
 
 void perf_hpp__cancel_cumulate(void)
 {
-	if (field_order)
+	if (is_strict_order(field_order))
 		return;
 
 	perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
tools/perf/util/sort.c +19 -5
index b4a805e5e440..1958637cf136 100644
@@ -1453,7 +1453,7 @@ static int __setup_sorting(void)
 	int ret = 0;
 
 	if (sort_keys == NULL) {
-		if (field_order) {
+		if (is_strict_order(field_order)) {
 			/*
 			 * If user specified field order but no sort order,
 			 * we'll honor it and not add default sort orders.
@@ -1639,23 +1639,36 @@ static void reset_dimensions(void)
 		memory_sort_dimensions[i].taken = 0;
 }
 
+bool is_strict_order(const char *order)
+{
+	return order && (*order != '+');
+}
+
 static int __setup_output_field(void)
 {
-	char *tmp, *tok, *str;
-	int ret = 0;
+	char *tmp, *tok, *str, *strp;
+	int ret = -EINVAL;
 
 	if (field_order == NULL)
 		return 0;
 
 	reset_dimensions();
 
-	str = strdup(field_order);
+	strp = str = strdup(field_order);
 	if (str == NULL) {
 		error("Not enough memory to setup output fields");
 		return -ENOMEM;
 	}
 
-	for (tok = strtok_r(str, ", ", &tmp);
+	if (!is_strict_order(field_order))
+		strp++;
+
+	if (!strlen(strp)) {
+		error("Invalid --fields key: `+'");
+		goto out;
+	}
+
+	for (tok = strtok_r(strp, ", ", &tmp);
 			tok; tok = strtok_r(NULL, ", ", &tmp)) {
 		ret = output_field_add(tok);
 		if (ret == -EINVAL) {
@@ -1667,6 +1680,7 @@ static int __setup_output_field(void)
 		}
 	}
 
+out:
 	free(str);
 	return ret;
 }
tools/perf/util/sort.h +1 -0
index 041f0c9cea2b..c03e4ff8beff 100644
@@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide);
 
 int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
 
+bool is_strict_order(const char *order);
 #endif	/* __PERF_SORT_H */
Signature · 1 line
1.9.3
author maintainer
#16 lore raw

PATCH 16/16 perf hists browser: Consolidate callchain print functions in TUI

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

Currently there're two callchain print functions in TUI - one for the hists browser and another for file dump. They do almost same job so it'd be better consolidate the codes.

To do that, provide two callbacks to the generic logic - one for printing and another for checking whether it should stop.

---
 tools/perf/ui/browsers/hists.c | 203 ++++++++++++++++-------------------------
 1 file changed, 80 insertions(+), 123 deletions(-)
tools/perf/ui/browsers/hists.c +80 -123
index 519353d9f5fb..d4cef68176da 100644
@@ -477,20 +477,37 @@ static char *callchain_list__sym_name(struct callchain_list *cl,
 	return bf;
 }
 
+struct callchain_print_arg {
+	/* for hists browser */
+	off_t	row_offset;
+	bool	is_current_entry;
+
+	/* for file dump */
+	FILE	*fp;
+	int	printed;
+};
+
+typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
+					 struct callchain_list *chain,
+					 const char *str, int offset,
+					 unsigned short row,
+					 struct callchain_print_arg *arg);
+
 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 					       struct callchain_list *chain,
-					       unsigned short row, int offset,
-					       char folded_sign, const char *str,
-					       bool *is_current_entry)
+					       const char *str, int offset,
+					       unsigned short row,
+					       struct callchain_print_arg *arg)
 {
 	int color, width;
+	char folded_sign = callchain_list__folded(chain);
 
 	color = HE_COLORSET_NORMAL;
 	width = browser->b.width - (offset + 2);
 	if (ui_browser__is_current_entry(&browser->b, row)) {
 		browser->selection = &chain->ms;
 		color = HE_COLORSET_SELECTED;
-		*is_current_entry = true;
+		arg->is_current_entry = true;
 	}
 
 	ui_browser__set_color(&browser->b, color);
@@ -500,12 +517,41 @@ static void hist_browser__show_callchain_entry(struct hist_browser *browser,
 	slsmg_write_nstring(str, width);
 }
 
+static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
+						  struct callchain_list *chain,
+						  const char *str, int offset,
+						  unsigned short row __maybe_unused,
+						  struct callchain_print_arg *arg)
+{
+	char folded_sign = callchain_list__folded(chain);
+
+	arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
+				folded_sign, str);
+}
+
+typedef bool (*check_output_full_fn)(struct hist_browser *browser,
+				     unsigned short row);
+
+static bool hist_browser__check_output_full(struct hist_browser *browser,
+					    unsigned short row)
+{
+	return browser->b.rows == row;
+}
+
+static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
+					  unsigned short row __maybe_unused)
+{
+	return false;
+}
+
 #define LEVEL_OFFSET_STEP 3
 
 static int hist_browser__show_callchain(struct hist_browser *browser,
 					struct rb_root *root, int level,
-					unsigned short row, off_t *row_offset,
-					u64 total, bool *is_current_entry)
+					unsigned short row, u64 total,
+					print_callchain_entry_fn print,
+					struct callchain_print_arg *arg,
+					check_output_full_fn is_output_full)
 {
 	struct rb_node *node;
 	int first_row = row, offset = level * LEVEL_OFFSET_STEP;
@@ -532,8 +578,8 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
 				extra_offset = LEVEL_OFFSET_STEP;
 
 			folded_sign = callchain_list__folded(chain);
-			if (*row_offset != 0) {
-				--*row_offset;
+			if (arg->row_offset != 0) {
+				arg->row_offset--;
 				goto do_next;
 			}
 
@@ -550,13 +596,11 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
 					str = alloc_str;
 			}
 
-			hist_browser__show_callchain_entry(browser, chain, row,
-							   offset + extra_offset,
-							   folded_sign, str,
-							   is_current_entry);
+			print(browser, chain, str, offset + extra_offset, row, arg);
+
 			free(alloc_str);
 
-			if (++row == browser->b.rows)
+			if (is_output_full(browser, ++row))
 				goto out;
 do_next:
 			if (folded_sign == '+')
@@ -572,12 +616,10 @@ do_next:
 				new_total = total;
 
 			row += hist_browser__show_callchain(browser, &child->rb_root,
-							    new_level,
-							    row, row_offset,
-							    new_total,
-							    is_current_entry);
+							    new_level, row, new_total,
+							    print, arg, is_output_full);
 		}
-		if (row == browser->b.rows)
+		if (is_output_full(browser, row))
 			break;
 		node = next;
 	}
@@ -757,16 +799,20 @@ static int hist_browser__show_entry(struct hist_browser *browser,
 
 	if (folded_sign == '-' && row != browser->b.rows) {
 		u64 total = hists__total_period(entry->hists);
+		struct callchain_print_arg arg = {
+			.row_offset = row_offset,
+			.is_current_entry = current_entry,
+		};
 
 		if (symbol_conf.cumulate_callchain)
 			total = entry->stat_acc->period;
 
 		printed += hist_browser__show_callchain(browser,
-							&entry->sorted_chain,
-							1, row, &row_offset,
-							total, &current_entry);
+					&entry->sorted_chain, 1, row, total,
+					hist_browser__show_callchain_entry, &arg,
+					hist_browser__check_output_full);
 
-		if (current_entry)
+		if (arg.is_current_entry)
 			browser->he_selection = entry;
 	}
 
@@ -1022,110 +1068,21 @@ do_offset:
 	}
 }
 
-static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
-							struct callchain_node *chain_node,
-							u64 total, int level,
-							FILE *fp)
-{
-	struct rb_node *node;
-	int offset = level * LEVEL_OFFSET_STEP;
-	u64 new_total;
-	int printed = 0;
-
-	if (callchain_param.mode == CHAIN_GRAPH_REL)
-		new_total = chain_node->children_hit;
-	else
-		new_total = total;
-
-	node = rb_first(&chain_node->rb_root);
-	while (node) {
-		struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
-		struct rb_node *next = rb_next(node);
-		u64 cumul = callchain_cumul_hits(child);
-		struct callchain_list *chain;
-		char folded_sign = ' ';
-		int first = true;
-		int extra_offset = 0;
-
-		list_for_each_entry(chain, &child->val, list) {
-			char bf[1024], *alloc_str;
-			const char *str;
-			bool was_first = first;
-
-			if (first)
-				first = false;
-			else
-				extra_offset = LEVEL_OFFSET_STEP;
-
-			folded_sign = callchain_list__folded(chain);
-
-			alloc_str = NULL;
-			str = callchain_list__sym_name(chain, bf, sizeof(bf),
-						       browser->show_dso);
-			if (was_first) {
-				double percent = cumul * 100.0 / new_total;
-
-				if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
-					str = "Not enough memory!";
-				else
-					str = alloc_str;
-			}
-
-			printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
-			free(alloc_str);
-			if (folded_sign == '+')
-				break;
-		}
-
-		if (folded_sign == '-') {
-			const int new_level = level + (extra_offset ? 2 : 1);
-			printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
-										new_level, fp);
-		}
-
-		node = next;
-	}
-
-	return printed;
-}
-
-static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
-						struct callchain_node *node,
-						int level, FILE *fp)
-{
-	struct callchain_list *chain;
-	int offset = level * LEVEL_OFFSET_STEP;
-	char folded_sign = ' ';
-	int printed = 0;
-
-	list_for_each_entry(chain, &node->val, list) {
-		char bf[1024], *s;
-
-		folded_sign = callchain_list__folded(chain);
-		s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
-		printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
-	}
-
-	if (folded_sign == '-')
-		printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
-									browser->hists->stats.total_period,
-									level + 1,  fp);
-	return printed;
-}
-
 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
-					   struct rb_root *chain, int level, FILE *fp)
+					   struct hist_entry *he, FILE *fp)
 {
-	struct rb_node *nd;
-	int printed = 0;
-
-	for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
-		struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
+	u64 total = hists__total_period(he->hists);
+	struct callchain_print_arg arg  = {
+		.fp = fp,
+	};
 
-		printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
-	}
+	if (symbol_conf.cumulate_callchain)
+		total = he->stat_acc->period;
 
-	return printed;
+	hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
+				     hist_browser__fprintf_callchain_entry, &arg,
+				     hist_browser__check_dump_full);
+	return arg.printed;
 }
 
 static int hist_browser__fprintf_entry(struct hist_browser *browser,
@@ -1164,7 +1121,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
 	printed += fprintf(fp, "%s\n", rtrim(s));
 
 	if (folded_sign == '-')
-		printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
+		printed += hist_browser__fprintf_callchain(browser, he, fp);
 
 	return printed;
 }
Signature · 1 line
1.9.3
#17 lore raw
In reply to Arnaldo Carvalho de Melo
Quoted 24 lines · Arnaldo Carvalho de Melo

Hi Ingo,

Please consider pulling,

- Arnaldo

The following changes since commit af924aa35129edf338ebc8da6a4eae08cf7cb297:

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core (2014-08-18 10:14:43 +0200)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo

for you to fetch changes up to 141da74d2eff8de134c5a5ffaf2800a8a1b27517:

perf hists browser: Consolidate callchain print functions in TUI (2014-08-22 13:12:15 -0300)

----------------------------------------------------------------
perf/core improvements and fixes:

User visible:

Quoted 8 lines

o Don't try to find DSOs in SYSV maps (Don Zickus)

o Fallback to MAP__FUNCTION if daddr maps are NULL, i.e. addresses get looked upon more maps (Don Zickus)

o Kernel fix to properly handle exited tasks, by returning POLLHUP values on perf event file descriptors. Tooling changes will come next, but were tested with this kernel fix. (Jiri Olsa)

So the signoffs are really weird (and invalid) here:

commit ce1d77c667e40631d6673717e65e72f72265bf10

Author: Jiri Olsa <[email protected]>
Date:   Fri Aug 15 16:24:45 2014 -0300

perf top: Use set_term_quiet() instead of open coded equivalent

...

Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
[ Yanked this out of a patch containing this and some other change ]

and in 7713886adc15 and f017bf0a8d92 - results of a rebase gone wrong?

Thanks,

Ingo

author maintainer
#18 lore raw

Em Sun, Aug 24, 2014 at 12:11:24PM +0200, Ingo Molnar escreveu:

Quoted 22 lines · Arnaldo Carvalho de Melo
Quoted 3 lines

o Kernel fix to properly handle exited tasks, by returning POLLHUP values on perf event file descriptors. Tooling changes will come next, but were tested with this kernel fix. (Jiri Olsa)

So the signoffs are really weird (and invalid) here:

commit ce1d77c667e40631d6673717e65e72f72265bf10

Author: Jiri Olsa <[email protected]>
Date:   Fri Aug 15 16:24:45 2014 -0300

perf top: Use set_term_quiet() instead of open coded equivalent

...

Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
[ Yanked this out of a patch containing this and some other change ]
Quoted 1 line

and in 7713886adc15 and f017bf0a8d92 - results of a rebase gone wrong?

Should be ok now, I recreated the perf-core-for-mingo signed tag, please let me know if there are still any problems,

Thanks!

- Arnaldo

#19 lore raw
Quoted 23 lines · Arnaldo Carvalho de Melo

Em Sun, Aug 24, 2014 at 12:11:24PM +0200, Ingo Molnar escreveu:

Quoted 21 lines · Arnaldo Carvalho de Melo
Quoted 3 lines

o Kernel fix to properly handle exited tasks, by returning POLLHUP values on perf event file descriptors. Tooling changes will come next, but were tested with this kernel fix. (Jiri Olsa)

So the signoffs are really weird (and invalid) here:

commit ce1d77c667e40631d6673717e65e72f72265bf10

Author: Jiri Olsa <[email protected]>
Date:   Fri Aug 15 16:24:45 2014 -0300

perf top: Use set_term_quiet() instead of open coded equivalent

...

Cc Jiri Olsa <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Peter Zijlstra <[email protected]>
[ Yanked this out of a patch containing this and some other change ]
Quoted 4 lines
Quoted 1 line

and in 7713886adc15 and f017bf0a8d92 - results of a rebase gone wrong?

Should be ok now, I recreated the perf-core-for-mingo signed tag, please let me know if there are still any problems,

Looks good now. Pulled, thanks a lot Arnaldo!

Ingo