Skip to contentSkip to conversation

perf tools: Various fixes

PATCH In review Jiri Olsa 4 participants 11 messages Jul 4, 2016 – Jul 5, 2016 perf events

lore
Share & tools
lore

Jiri Olsa

author reviewer
#0 lore raw

v2 cover perf tools: Various fixes

hi,
this is v2 for original post:
http://marc.info/?t=146711345400003&r=1&w=2

v2 changes:
- keep nr_numa_nodes in patch 1
- added patch 2,3,4

thanks,
jirka

---
Jiri Olsa (4):
perf tools: Transform nodes string info to struct perf tests: Fix hist accumulation test
perf tools: Add initialized arg into unwind__prepare_access perf tools: Call unwind__prepare_access for forked thread

 tools/perf/tests/hists_cumulate.c  |  4 ++++
 tools/perf/util/callchain.h        |  1 +
 tools/perf/util/env.c              |  5 ++++-
 tools/perf/util/env.h              | 10 +++++++++-
 tools/perf/util/header.c           | 76 ++++++++++++++++++++++++++--------------------------------------------------
 tools/perf/util/map.c              |  9 ++++++++-
 tools/perf/util/map.h              |  2 +-
 tools/perf/util/thread.c           | 39 ++++++++++++++++++++++++++++++++++++---
 tools/perf/util/unwind-libunwind.c | 11 +++++++++--
 tools/perf/util/unwind.h           |  9 ++++++---
 tools/perf/util/util.c             | 19 +++++++++++++------
 11 files changed, 117 insertions(+), 68 deletions(-)
author reviewer
#1 lore raw

PATCH v2 1/4 perf tools: Transform nodes string info to struct

Storing NUMA info within struct numa_node instead of strings. This way it's usable in future patches.

Also it turned out it's slightly less code involved than using strings.

---
 tools/perf/util/env.c    |  5 +++-
 tools/perf/util/env.h    | 10 ++++++-
 tools/perf/util/header.c | 76 +++++++++++++++++-------------------------------
 3 files changed, 39 insertions(+), 52 deletions(-)
tools/perf/util/env.c +4 -1
index 49a11d9d8b8f..bb964e86b09d 100644
@@ -18,10 +18,13 @@ void perf_env__exit(struct perf_env *env)
 	zfree(&env->cmdline_argv);
 	zfree(&env->sibling_cores);
 	zfree(&env->sibling_threads);
-	zfree(&env->numa_nodes);
 	zfree(&env->pmu_mappings);
 	zfree(&env->cpu);
 
+	for (i = 0; i < env->nr_numa_nodes; i++)
+		cpu_map__put(env->numa_nodes[i].map);
+	zfree(&env->numa_nodes);
+
 	for (i = 0; i < env->caches_cnt; i++)
 		cpu_cache_level__free(&env->caches[i]);
 	zfree(&env->caches);
tools/perf/util/env.h +9 -1
index 56cffb60a0b4..b164dfd2dcbf 100644
@@ -2,6 +2,7 @@
 #define __PERF_ENV_H
 
 #include <linux/types.h>
+#include "cpumap.h"
 
 struct cpu_topology_map {
 	int	socket_id;
@@ -18,6 +19,13 @@ struct cpu_cache_level {
 	char	*map;
 };
 
+struct numa_node {
+	u32		 node;
+	u64		 mem_total;
+	u64		 mem_free;
+	struct cpu_map	*map;
+};
+
 struct perf_env {
 	char			*hostname;
 	char			*os_release;
@@ -40,11 +48,11 @@ struct perf_env {
 	const char		**cmdline_argv;
 	char			*sibling_cores;
 	char			*sibling_threads;
-	char			*numa_nodes;
 	char			*pmu_mappings;
 	struct cpu_topology_map	*cpu;
 	struct cpu_cache_level	*caches;
 	int			 caches_cnt;
+	struct numa_node	*numa_nodes;
 };
 
 extern struct perf_env perf_env;
tools/perf/util/header.c +26 -50

126 lines not on this page · raw message

Signature · 1 line
2.4.11
#2 lore raw

[tip:perf/core] perf header: Transform nodes string info to struct

perf header: Transform nodes string info to struct
Commit-ID:  c60da22aca8755b77b7f4d4caf57ada8654db939
Gitweb:     http://git.kernel.org/tip/c60da22aca8755b77b7f4d4caf57ada8654db939
Author:     Jiri Olsa <[email protected]>
AuthorDate: Mon, 4 Jul 2016 14:16:20 +0200
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 4 Jul 2016 19:39:01 -0300

perf header: Transform nodes string info to struct

Storing NUMA info within struct numa_node instead of strings. This way it's usable in future patches.

Also it turned out it's slightly less code involved than using strings.

---
 tools/perf/util/env.c    |  5 +++-
 tools/perf/util/env.h    | 10 ++++++-
 tools/perf/util/header.c | 76 +++++++++++++++++-------------------------------
 3 files changed, 39 insertions(+), 52 deletions(-)
tools/perf/util/env.c +4 -1
index 49a11d9..bb964e8 100644
@@ -18,10 +18,13 @@ void perf_env__exit(struct perf_env *env)
 	zfree(&env->cmdline_argv);
 	zfree(&env->sibling_cores);
 	zfree(&env->sibling_threads);
-	zfree(&env->numa_nodes);
 	zfree(&env->pmu_mappings);
 	zfree(&env->cpu);
 
+	for (i = 0; i < env->nr_numa_nodes; i++)
+		cpu_map__put(env->numa_nodes[i].map);
+	zfree(&env->numa_nodes);
+
 	for (i = 0; i < env->caches_cnt; i++)
 		cpu_cache_level__free(&env->caches[i]);
 	zfree(&env->caches);
tools/perf/util/env.h +9 -1
index 56cffb6..b164dfd 100644
@@ -2,6 +2,7 @@
 #define __PERF_ENV_H
 
 #include <linux/types.h>
+#include "cpumap.h"
 
 struct cpu_topology_map {
 	int	socket_id;
@@ -18,6 +19,13 @@ struct cpu_cache_level {
 	char	*map;
 };
 
+struct numa_node {
+	u32		 node;
+	u64		 mem_total;
+	u64		 mem_free;
+	struct cpu_map	*map;
+};
+
 struct perf_env {
 	char			*hostname;
 	char			*os_release;
@@ -40,11 +48,11 @@ struct perf_env {
 	const char		**cmdline_argv;
 	char			*sibling_cores;
 	char			*sibling_threads;
-	char			*numa_nodes;
 	char			*pmu_mappings;
 	struct cpu_topology_map	*cpu;
 	struct cpu_cache_level	*caches;
 	int			 caches_cnt;
+	struct numa_node	*numa_nodes;
 };
 
 extern struct perf_env perf_env;
tools/perf/util/header.c +26 -50

126 lines not on this page · raw message

author reviewer
#3 lore raw

PATCH v2 2/4 perf tests: Fix hist accumulation test

In reply to Jiri Olsa

User's values from .perfconfig could overload the default callchain setup and cause this test to fail. Making sure the test is using default callchain_param values.

---
 tools/perf/tests/hists_cumulate.c |  4 ++++
 tools/perf/util/callchain.h       |  1 +
 tools/perf/util/util.c            | 19 +++++++++++++------
 3 files changed, 18 insertions(+), 6 deletions(-)
tools/perf/tests/hists_cumulate.c +4 -0
index a9e3db3afac4..9fd54b79a788 100644
@@ -216,6 +216,8 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
 
 		/* check callchain entries */
 		root = &he->callchain->node.rb_root;
+
+		TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
 		cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
 
 		c = 0;
@@ -666,6 +668,8 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
 	perf_evsel__set_sample_bit(evsel, CALLCHAIN);
 
 	setup_sorting(NULL);
+
+	callchain_param = callchain_param_default;
 	callchain_register_param(&callchain_param);
 
 	err = add_hist_entries(hists, machine);
tools/perf/util/callchain.h +1 -0
index a70f6b54eb92..13e75549c440 100644
@@ -106,6 +106,7 @@ struct callchain_param {
 };
 
 extern struct callchain_param callchain_param;
+extern struct callchain_param callchain_param_default;
 
 struct callchain_list {
 	u64			ip;
tools/perf/util/util.c +13 -6
index e08b9a092a23..5f44a21955cd 100644
@@ -19,12 +19,19 @@
 #include "callchain.h"
 #include "strlist.h"
 
-struct callchain_param	callchain_param = {
-	.mode	= CHAIN_GRAPH_ABS,
-	.min_percent = 0.5,
-	.order  = ORDER_CALLEE,
-	.key	= CCKEY_FUNCTION,
-	.value	= CCVAL_PERCENT,
+#define CALLCHAIN_PARAM_DEFAULT			\
+	.mode		= CHAIN_GRAPH_ABS,	\
+	.min_percent	= 0.5,			\
+	.order		= ORDER_CALLEE,		\
+	.key		= CCKEY_FUNCTION,	\
+	.value		= CCVAL_PERCENT,	\
+
+struct callchain_param callchain_param = {
+	CALLCHAIN_PARAM_DEFAULT
+};
+
+struct callchain_param callchain_param_default = {
+	CALLCHAIN_PARAM_DEFAULT
 };
 
 /*
Signature · 1 line
2.4.11
#4 lore raw

[tip:perf/core] perf tests: Fix hist accumulation test

perf tests: Fix hist accumulation test
Commit-ID:  347ca878062d5fb0e16db1fae81b0994f2457efd
Gitweb:     http://git.kernel.org/tip/347ca878062d5fb0e16db1fae81b0994f2457efd
Author:     Jiri Olsa <[email protected]>
AuthorDate: Mon, 4 Jul 2016 14:16:21 +0200
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 4 Jul 2016 19:39:01 -0300

perf tests: Fix hist accumulation test

User's values from .perfconfig could overload the default callchain setup and cause this test to fail. Making sure the test is using default callchain_param values.

---
 tools/perf/tests/hists_cumulate.c |  4 ++++
 tools/perf/util/callchain.h       |  1 +
 tools/perf/util/util.c            | 19 +++++++++++++------
 3 files changed, 18 insertions(+), 6 deletions(-)
tools/perf/tests/hists_cumulate.c +4 -0
index a9e3db3..9fd54b7 100644
@@ -216,6 +216,8 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
 
 		/* check callchain entries */
 		root = &he->callchain->node.rb_root;
+
+		TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
 		cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
 
 		c = 0;
@@ -666,6 +668,8 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
 	perf_evsel__set_sample_bit(evsel, CALLCHAIN);
 
 	setup_sorting(NULL);
+
+	callchain_param = callchain_param_default;
 	callchain_register_param(&callchain_param);
 
 	err = add_hist_entries(hists, machine);
tools/perf/util/callchain.h +1 -0
index a70f6b5..13e7554 100644
@@ -106,6 +106,7 @@ struct callchain_param {
 };
 
 extern struct callchain_param callchain_param;
+extern struct callchain_param callchain_param_default;
 
 struct callchain_list {
 	u64			ip;
tools/perf/util/util.c +13 -6
index e08b9a0..5f44a21 100644
@@ -19,12 +19,19 @@
 #include "callchain.h"
 #include "strlist.h"
 
-struct callchain_param	callchain_param = {
-	.mode	= CHAIN_GRAPH_ABS,
-	.min_percent = 0.5,
-	.order  = ORDER_CALLEE,
-	.key	= CCKEY_FUNCTION,
-	.value	= CCVAL_PERCENT,
+#define CALLCHAIN_PARAM_DEFAULT			\
+	.mode		= CHAIN_GRAPH_ABS,	\
+	.min_percent	= 0.5,			\
+	.order		= ORDER_CALLEE,		\
+	.key		= CCKEY_FUNCTION,	\
+	.value		= CCVAL_PERCENT,	\
+
+struct callchain_param callchain_param = {
+	CALLCHAIN_PARAM_DEFAULT
+};
+
+struct callchain_param callchain_param_default = {
+	CALLCHAIN_PARAM_DEFAULT
 };
 
 /*
author reviewer
#5 lore raw

PATCH v2 3/4 perf tools: Add initialized arg into unwind__prepare_access

In reply to Jiri Olsa

Adding initialized arg into unwind__prepare_access to get feedback about the initialization state.

It's not possible to get it from error code, because we return 0 even in case we don't recognize dso, which is valid.

The 'initialized' value is used in following patch to speedup unwind__prepare_access calls logic in fork path.

---
 tools/perf/util/thread.c           |  2 +-
 tools/perf/util/unwind-libunwind.c | 11 +++++++++--
 tools/perf/util/unwind.h           |  9 ++++++---
 3 files changed, 16 insertions(+), 6 deletions(-)
tools/perf/util/thread.c +1 -1
index f30f9566fddc..2439b122a4e4 100644
@@ -202,7 +202,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
 {
 	int ret;
 
-	ret = unwind__prepare_access(thread, map);
+	ret = unwind__prepare_access(thread, map, NULL);
 	if (ret)
 		return ret;
 
tools/perf/util/unwind-libunwind.c +9 -2
index 854711966cad..6d542a4e0648 100644
@@ -14,15 +14,19 @@ static void unwind__register_ops(struct thread *thread,
 	thread->unwind_libunwind_ops = ops;
 }
 
-int unwind__prepare_access(struct thread *thread, struct map *map)
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized)
 {
 	const char *arch;
 	enum dso_type dso_type;
 	struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+	int err;
 
 	if (thread->addr_space) {
 		pr_debug("unwind: thread map already set, dso=%s\n",
 			 map->dso->name);
+		if (initialized)
+			*initialized = true;
 		return 0;
 	}
 
@@ -51,7 +55,10 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
 out_register:
 	unwind__register_ops(thread, ops);
 
-	return thread->unwind_libunwind_ops->prepare_access(thread);
+	err = thread->unwind_libunwind_ops->prepare_access(thread);
+	if (initialized)
+		*initialized = err ? false : true;
+	return err;
 }
 
 void unwind__flush_access(struct thread *thread)
tools/perf/util/unwind.h +6 -3
index 84c6d44d52f9..17ea1f928f13 100644
@@ -42,12 +42,14 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 #endif
 
 int LIBUNWIND__ARCH_REG_ID(int regnum);
-int unwind__prepare_access(struct thread *thread, struct map *map);
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized);
 void unwind__flush_access(struct thread *thread);
 void unwind__finish_access(struct thread *thread);
 #else
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused);
 {
 	return 0;
 }
@@ -67,7 +69,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
 }
 
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused);
 {
 	return 0;
 }
Signature · 1 line
2.4.11
#6 lore raw

Re: [PATCH 3/4] perf tools: Add initialized arg into unwind__prepare_access

Em Mon, Jul 04, 2016 at 02:16:22PM +0200, Jiri Olsa escreveu:

Quoted 92 lines

Adding initialized arg into unwind__prepare_access to get feedback about the initialization state.

It's not possible to get it from error code, because we return 0 even in case we don't recognize dso, which is valid.

The 'initialized' value is used in following patch to speedup unwind__prepare_access calls logic in fork path.

---
 tools/perf/util/thread.c           |  2 +-
 tools/perf/util/unwind-libunwind.c | 11 +++++++++--
 tools/perf/util/unwind.h           |  9 ++++++---
 3 files changed, 16 insertions(+), 6 deletions(-)
tools/perf/util/thread.c +1 -1
index f30f9566fddc..2439b122a4e4 100644
@@ -202,7 +202,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
 {
 	int ret;
 
-	ret = unwind__prepare_access(thread, map);
+	ret = unwind__prepare_access(thread, map, NULL);
 	if (ret)
 		return ret;
 
tools/perf/util/unwind-libunwind.c +9 -2
index 854711966cad..6d542a4e0648 100644
@@ -14,15 +14,19 @@ static void unwind__register_ops(struct thread *thread,
 	thread->unwind_libunwind_ops = ops;
 }
 
-int unwind__prepare_access(struct thread *thread, struct map *map)
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized)
 {
 	const char *arch;
 	enum dso_type dso_type;
 	struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+	int err;
 
 	if (thread->addr_space) {
 		pr_debug("unwind: thread map already set, dso=%s\n",
 			 map->dso->name);
+		if (initialized)
+			*initialized = true;
 		return 0;
 	}
 
@@ -51,7 +55,10 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
 out_register:
 	unwind__register_ops(thread, ops);
 
-	return thread->unwind_libunwind_ops->prepare_access(thread);
+	err = thread->unwind_libunwind_ops->prepare_access(thread);
+	if (initialized)
+		*initialized = err ? false : true;
+	return err;
 }
 
 void unwind__flush_access(struct thread *thread)
tools/perf/util/unwind.h +4 -2
index 84c6d44d52f9..17ea1f928f13 100644
@@ -42,12 +42,14 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 #endif
 
 int LIBUNWIND__ARCH_REG_ID(int regnum);
-int unwind__prepare_access(struct thread *thread, struct map *map);
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized);
 void unwind__flush_access(struct thread *thread);
 void unwind__finish_access(struct thread *thread);
 #else
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused);
 {
 	return 0;
 }

tools/perf/util/unwind.h unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, · line 76

Quoted 9 lines
tools/perf/util/unwind.h +2 -1
@@ -67,7 +69,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
 }
 
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused);
 {
 	return 0;
 }

That extra ; breaks the build on centos5 (i.e. on an arch where the above code gets compiled:

CC /tmp/build/perf/bench/sched-pipe.o
In file included from util/machine.c:14:
util/unwind.h:74: error: expected identifier or '(' before '{' token
MKDIR /tmp/build/perf/tests/
CC /tmp/build/perf/tests/parse-events.o
mv: cannot stat `/tmp/build/perf/util/.machine.o.tmp': No such file or directory make[3]: *** [/tmp/build/perf/util/machine.o] Error 1
make[3]: *** Waiting for unfinished jobs....
MKDIR /tmp/build/perf/tests/
CC /tmp/build/perf/tests/dso-data.o
make[2]: *** [util] Error 2
make[1]: *** [/tmp/build/perf/libperf-in.o] Error 2
make[1]: *** Waiting for unfinished jobs....
MKDIR /tmp/build/perf/tests/

Fixing it.

Quoted 2 lines
Signature · 1 line
2.4.11
#7 lore raw

Re: [PATCH 3/4] perf tools: Add initialized arg into unwind__prepare_access

On Mon, Jul 04, 2016 at 04:25:11PM -0300, Arnaldo Carvalho de Melo wrote:

SNIP

Quoted 31 lines

tools/perf/util/unwind.h unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, · line 76

Quoted 11 lines

}

tools/perf/util/unwind.h +2 -1
@@ -67,7 +69,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
 }
 
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused);
 {
 	return 0;
 }

That extra ; breaks the build on centos5 (i.e. on an arch where the above code gets compiled:

CC /tmp/build/perf/bench/sched-pipe.o
In file included from util/machine.c:14:
util/unwind.h:74: error: expected identifier or '(' before '{' token
MKDIR /tmp/build/perf/tests/
CC /tmp/build/perf/tests/parse-events.o
mv: cannot stat `/tmp/build/perf/util/.machine.o.tmp': No such file or directory make[3]: *** [/tmp/build/perf/util/machine.o] Error 1
make[3]: *** Waiting for unfinished jobs....
MKDIR /tmp/build/perf/tests/
CC /tmp/build/perf/tests/dso-data.o
make[2]: *** [util] Error 2
make[1]: *** [/tmp/build/perf/libperf-in.o] Error 2
make[1]: *** Waiting for unfinished jobs....
MKDIR /tmp/build/perf/tests/

Quoted 1 line

Fixing it.

oops, thanks

jirka

#8 lore raw

[tip:perf/core] perf unwind: Add initialized arg into unwind__prepare_access

In reply to Jiri Olsa
perf unwind: Add initialized arg into unwind__prepare_access
Commit-ID:  a2873325ffb21cecca8032673eb698cb4d778dc6
Gitweb:     http://git.kernel.org/tip/a2873325ffb21cecca8032673eb698cb4d778dc6
Author:     Jiri Olsa <[email protected]>
AuthorDate: Mon, 4 Jul 2016 14:16:22 +0200
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 4 Jul 2016 20:27:12 -0300

perf unwind: Add initialized arg into unwind__prepare_access

Adding initialized arg into unwind__prepare_access to get feedback about the initialization state.

It's not possible to get it from error code, because we return 0 even in case we don't recognize dso, which is valid.

The 'initialized' value is used in following patch to speedup unwind__prepare_access calls logic in fork path.

Cc David Ahern <[email protected]>
Cc He Kuang <[email protected]>
Cc Namhyung Kim <[email protected]>
Cc Peter Zijlstra <[email protected]>
[ Remove ; after static inline function signatures, fixes build break ]
---
 tools/perf/util/thread.c           |  2 +-
 tools/perf/util/unwind-libunwind.c | 11 +++++++++--
 tools/perf/util/unwind.h           |  9 ++++++---
 3 files changed, 16 insertions(+), 6 deletions(-)
tools/perf/util/thread.c +1 -1
index f30f956..2439b12 100644
@@ -202,7 +202,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
 {
 	int ret;
 
-	ret = unwind__prepare_access(thread, map);
+	ret = unwind__prepare_access(thread, map, NULL);
 	if (ret)
 		return ret;
 
tools/perf/util/unwind-libunwind.c +9 -2
index 8547119..6d542a4 100644
@@ -14,15 +14,19 @@ static void unwind__register_ops(struct thread *thread,
 	thread->unwind_libunwind_ops = ops;
 }
 
-int unwind__prepare_access(struct thread *thread, struct map *map)
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized)
 {
 	const char *arch;
 	enum dso_type dso_type;
 	struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
+	int err;
 
 	if (thread->addr_space) {
 		pr_debug("unwind: thread map already set, dso=%s\n",
 			 map->dso->name);
+		if (initialized)
+			*initialized = true;
 		return 0;
 	}
 
@@ -51,7 +55,10 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
 out_register:
 	unwind__register_ops(thread, ops);
 
-	return thread->unwind_libunwind_ops->prepare_access(thread);
+	err = thread->unwind_libunwind_ops->prepare_access(thread);
+	if (initialized)
+		*initialized = err ? false : true;
+	return err;
 }
 
 void unwind__flush_access(struct thread *thread)
tools/perf/util/unwind.h +6 -3
index 84c6d44..61fb1e9 100644
@@ -42,12 +42,14 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
 #endif
 
 int LIBUNWIND__ARCH_REG_ID(int regnum);
-int unwind__prepare_access(struct thread *thread, struct map *map);
+int unwind__prepare_access(struct thread *thread, struct map *map,
+			   bool *initialized);
 void unwind__flush_access(struct thread *thread);
 void unwind__finish_access(struct thread *thread);
 #else
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused)
 {
 	return 0;
 }
@@ -67,7 +69,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
 }
 
 static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
-					 struct map *map __maybe_unused)
+					 struct map *map __maybe_unused,
+					 bool *initialized __maybe_unused)
 {
 	return 0;
 }
author reviewer
#9 lore raw

PATCH v2 4/4 perf tools: Call unwind__prepare_access for forked thread

In reply to Jiri Olsa

Currently we call unwind__prepare_access for map event. In case we report fork event the thread inherits its parent's maps and unwind__prepare_access is never called for the thread.

This causes unwind__get_entries seeing uninitialized unwind_libunwind_ops and thus returning no callchain.

Adding unwind__prepare_access calls for fork even processing.

---
 tools/perf/util/map.c    |  9 ++++++++-
 tools/perf/util/map.h    |  2 +-
 tools/perf/util/thread.c | 37 +++++++++++++++++++++++++++++++++++--
 3 files changed, 44 insertions(+), 4 deletions(-)
tools/perf/util/map.c +8 -1
index b19bcd3b7128..b39b12a1208d 100644
@@ -15,6 +15,7 @@
 #include "debug.h"
 #include "machine.h"
 #include <linux/string.h>
+#include "unwind.h"
 
 static void __maps__insert(struct maps *maps, struct map *map);
 
@@ -744,9 +745,10 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 /*
  * XXX This should not really _copy_ te maps, but refcount them.
  */
-int map_groups__clone(struct map_groups *mg,
+int map_groups__clone(struct thread *thread,
 		      struct map_groups *parent, enum map_type type)
 {
+	struct map_groups *mg = thread->mg;
 	int err = -ENOMEM;
 	struct map *map;
 	struct maps *maps = &parent->maps[type];
@@ -757,6 +759,11 @@ int map_groups__clone(struct map_groups *mg,
 		struct map *new = map__clone(map);
 		if (new == NULL)
 			goto out_unlock;
+
+		err = unwind__prepare_access(thread, new, NULL);
+		if (err)
+			goto out_unlock;
+
 		map_groups__insert(mg, new);
 		map__put(new);
 	}
tools/perf/util/map.h +1 -1
index 7309d64ce39e..d83396ceecba 100644
@@ -194,7 +194,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
                                          struct map **mapp, symbol_filter_t filter);
 void map_groups__init(struct map_groups *mg, struct machine *machine);
 void map_groups__exit(struct map_groups *mg);
-int map_groups__clone(struct map_groups *mg,
+int map_groups__clone(struct thread *thread,
 		      struct map_groups *parent, enum map_type type);
 size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
 
tools/perf/util/thread.c +35 -2
index 2439b122a4e4..8b10a55410a2 100644
@@ -212,6 +212,39 @@ int thread__insert_map(struct thread *thread, struct map *map)
 	return 0;
 }
 
+static int __thread__prepare_access(struct thread *thread)
+{
+	bool initialized = false;
+	int i, err = 0;
+
+	for (i = 0; i < MAP__NR_TYPES; ++i) {
+		struct maps *maps = &thread->mg->maps[i];
+		struct map *map;
+
+		pthread_rwlock_rdlock(&maps->lock);
+
+		for (map = maps__first(maps); map; map = map__next(map)) {
+			err = unwind__prepare_access(thread, map, &initialized);
+			if (err || initialized)
+				break;
+		}
+
+		pthread_rwlock_unlock(&maps->lock);
+	}
+
+	return err;
+}
+
+static int thread__prepare_access(struct thread *thread)
+{
+	int err = 0;
+
+	if (symbol_conf.use_callchain)
+		err = __thread__prepare_access(thread);
+
+	return err;
+}
+
 static int thread__clone_map_groups(struct thread *thread,
 				    struct thread *parent)
 {
@@ -219,7 +252,7 @@ static int thread__clone_map_groups(struct thread *thread,
 
 	/* This is new thread, we share map groups for process. */
 	if (thread->pid_ == parent->pid_)
-		return 0;
+		return thread__prepare_access(thread);
 
 	if (thread->mg == parent->mg) {
 		pr_debug("broken map groups on thread %d/%d parent %d/%d\n",
@@ -229,7 +262,7 @@ static int thread__clone_map_groups(struct thread *thread,
 
 	/* But this one is new process, copy maps. */
 	for (i = 0; i < MAP__NR_TYPES; ++i)
-		if (map_groups__clone(thread->mg, parent->mg, i) < 0)
+		if (map_groups__clone(thread, parent->mg, i) < 0)
 			return -ENOMEM;
 
 	return 0;
Signature · 1 line
2.4.11
#10 lore raw

[tip:perf/core] perf unwind: Call unwind__prepare_access for forked thread

perf unwind: Call unwind__prepare_access for forked thread
Commit-ID:  6c502584438bda63fc1a67606854fb0b300465cd
Gitweb:     http://git.kernel.org/tip/6c502584438bda63fc1a67606854fb0b300465cd
Author:     Jiri Olsa <[email protected]>
AuthorDate: Mon, 4 Jul 2016 14:16:23 +0200
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Mon, 4 Jul 2016 20:27:25 -0300

perf unwind: Call unwind__prepare_access for forked thread

Currently we call unwind__prepare_access for map event. In case we report fork event the thread inherits its parent's maps and unwind__prepare_access is never called for the thread.

This causes unwind__get_entries seeing uninitialized unwind_libunwind_ops and thus returning no callchain.

Adding unwind__prepare_access calls for fork even processing.

---
 tools/perf/util/map.c    |  9 ++++++++-
 tools/perf/util/map.h    |  2 +-
 tools/perf/util/thread.c | 37 +++++++++++++++++++++++++++++++++++--
 3 files changed, 44 insertions(+), 4 deletions(-)
tools/perf/util/map.c +8 -1
index b19bcd3..b39b12a 100644
@@ -15,6 +15,7 @@
 #include "debug.h"
 #include "machine.h"
 #include <linux/string.h>
+#include "unwind.h"
 
 static void __maps__insert(struct maps *maps, struct map *map);
 
@@ -744,9 +745,10 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
 /*
  * XXX This should not really _copy_ te maps, but refcount them.
  */
-int map_groups__clone(struct map_groups *mg,
+int map_groups__clone(struct thread *thread,
 		      struct map_groups *parent, enum map_type type)
 {
+	struct map_groups *mg = thread->mg;
 	int err = -ENOMEM;
 	struct map *map;
 	struct maps *maps = &parent->maps[type];
@@ -757,6 +759,11 @@ int map_groups__clone(struct map_groups *mg,
 		struct map *new = map__clone(map);
 		if (new == NULL)
 			goto out_unlock;
+
+		err = unwind__prepare_access(thread, new, NULL);
+		if (err)
+			goto out_unlock;
+
 		map_groups__insert(mg, new);
 		map__put(new);
 	}
tools/perf/util/map.h +1 -1
index 7309d64..d83396c 100644
@@ -194,7 +194,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
                                          struct map **mapp, symbol_filter_t filter);
 void map_groups__init(struct map_groups *mg, struct machine *machine);
 void map_groups__exit(struct map_groups *mg);
-int map_groups__clone(struct map_groups *mg,
+int map_groups__clone(struct thread *thread,
 		      struct map_groups *parent, enum map_type type);
 size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
 
tools/perf/util/thread.c +35 -2
index 2439b12..8b10a55 100644
@@ -212,6 +212,39 @@ int thread__insert_map(struct thread *thread, struct map *map)
 	return 0;
 }
 
+static int __thread__prepare_access(struct thread *thread)
+{
+	bool initialized = false;
+	int i, err = 0;
+
+	for (i = 0; i < MAP__NR_TYPES; ++i) {
+		struct maps *maps = &thread->mg->maps[i];
+		struct map *map;
+
+		pthread_rwlock_rdlock(&maps->lock);
+
+		for (map = maps__first(maps); map; map = map__next(map)) {
+			err = unwind__prepare_access(thread, map, &initialized);
+			if (err || initialized)
+				break;
+		}
+
+		pthread_rwlock_unlock(&maps->lock);
+	}
+
+	return err;
+}
+
+static int thread__prepare_access(struct thread *thread)
+{
+	int err = 0;
+
+	if (symbol_conf.use_callchain)
+		err = __thread__prepare_access(thread);
+
+	return err;
+}
+
 static int thread__clone_map_groups(struct thread *thread,
 				    struct thread *parent)
 {
@@ -219,7 +252,7 @@ static int thread__clone_map_groups(struct thread *thread,
 
 	/* This is new thread, we share map groups for process. */
 	if (thread->pid_ == parent->pid_)
-		return 0;
+		return thread__prepare_access(thread);
 
 	if (thread->mg == parent->mg) {
 		pr_debug("broken map groups on thread %d/%d parent %d/%d\n",
@@ -229,7 +262,7 @@ static int thread__clone_map_groups(struct thread *thread,
 
 	/* But this one is new process, copy maps. */
 	for (i = 0; i < MAP__NR_TYPES; ++i)
-		if (map_groups__clone(thread->mg, parent->mg, i) < 0)
+		if (map_groups__clone(thread, parent->mg, i) < 0)
 			return -ENOMEM;
 
 	return 0;