linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/6] perf probe: Attempt to improve C++ probing
@ 2018-05-14  4:19 Holger Freyther
  2018-05-14  4:19 ` [RFC 1/6] perf probe: Do not exclude mangled C++ funcs Holger Freyther
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

Currently perf probe -x app --funcs will list and demangle C++ functions
but the other probe actions can't work with them. When asking probe to not
demangle it will not list any of the application symbols creating the
impression that there are no symbols at all.

Make --funcs --no-demangle list all C++ functions and modify the handling
for listing code, variables and adding the uprobe work with the demangled
C++ function name.

I tried to keep this as minimal as possible but having to keep the dso in
the debuginfo and passing it everywhere to be able to demangle the linkage
name isn't pretty (and for C++ demangling the struct dso is not of much
use. Maybe having a static "empty" dso could avoid a lot of the changes).

Maybe the easiest first patch is to default to --no-demangle and change
the DEFAULT_FUNC_FILTER to not include mangled C++ symbols. The remaining
tooling would work then.

This has seen very little testing outside the following commands.

My test set includes:

 ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at"
 ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at:2-3"

 ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at"
 ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at:2"
 ./perf probe -x . -V "std::vector<int, std::allocator<int> >::size%return"


Holger Hans Peter Freyther (6):
  perf probe: Do not exclude mangled C++ funcs
  perf probe: Parse linerange for C++ functions
  perf probe: Make listing of C++ functions work
  perf probe: Show variables for C++ functions
  perf probe: Make listing of variables work for C++ functions
  perf probe: Make it possible to add a C++ uprobe

 tools/perf/builtin-probe.c     |   2 +-
 tools/perf/util/probe-event.c  |  77 ++++++++++++++++++++-
 tools/perf/util/probe-finder.c | 152 ++++++++++++++++++++++++++++++-----------
 tools/perf/util/probe-finder.h |   3 +
 tools/perf/util/string.c       |  57 ++++++++++++++++
 tools/perf/util/string2.h      |   1 +
 6 files changed, 247 insertions(+), 45 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [RFC 1/6] perf probe: Do not exclude mangled C++ funcs
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14  4:19 ` [RFC 2/6] perf probe: Parse linerange for C++ functions Holger Freyther
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

Using --funcs --no-demangle on a C++ binary does not list any of the C++
functions. Change the default filter to not exclude the Common C++ ABI
symbols.

 $ ./perf probe -x ./cxx-example --funcs --no-demangle
 ...
 _ZN9__gnu_cxx13new_allocatorIiEC1Ev
 ...

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/builtin-probe.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c006592..d69f679 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -43,7 +43,7 @@
 #include "util/probe-file.h"
 
 #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
-#define DEFAULT_FUNC_FILTER "!_*"
+#define DEFAULT_FUNC_FILTER "!_* | _Z*"
 #define DEFAULT_LIST_FILTER "*"
 
 /* Session management structure */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC 2/6] perf probe: Parse linerange for C++ functions
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
  2018-05-14  4:19 ` [RFC 1/6] perf probe: Do not exclude mangled C++ funcs Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14  4:19 ` [RFC 3/6] perf probe: Make listing of C++ functions work Holger Freyther
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

perf probe --funcs will demangle C++ symbols by default but these
functions can not be used for listing sourcecode. Modify the scanner
to start searching for a line number only after a single ':'.

./perf probe -x ./cxx-example -L \
	"std::vector<int, std::allocator<int> >::at:1"

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/util/probe-event.c | 57 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e1dbc98..39a2d47 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1237,6 +1237,59 @@ static bool is_c_func_name(const char *name)
 	return true;
 }
 
+/* Symbols in demangled CXX function names */
+static inline bool is_cxx_symbol(const char symbol)
+{
+	switch (symbol) {
+	case '_':
+	case ' ':
+	case '&':
+	case '*':
+	case '@':
+	case ',':
+	case ':':
+	case '<':
+	case '>':
+	case '(':
+	case ')':
+		return true;
+	default:
+		return false;
+	}
+}
+
+/* Is name a C++ name? */
+static bool is_cxx_func_name(const char *name)
+{
+	/* C name or a mangled name */
+	if (is_c_func_name(name))
+		return true;
+	while (*++name != '\0') {
+		if (!isalpha(*name) && !isdigit(*name) && !is_cxx_symbol(*name))
+			return false;
+	}
+	return true;
+}
+
+/*
+ * Find the first ':' that isn't part of a C++ namespace or class
+ * name.
+ */
+static char *first_non_cxx_ns(char *name)
+{
+	while (*name) {
+		char cur = *name, nxt = *(name + 1);
+
+		if (cur == ':' && nxt == ':')
+			name += 2;
+		else if (cur == ':')
+			return name;
+
+		name += 1;
+	}
+	return NULL;
+}
+
 /*
  * Stuff 'lr' according to the line range described by 'arg'.
  * The line range syntax is described by:
@@ -1255,7 +1308,7 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
 	lr->start = 0;
 	lr->end = INT_MAX;
 
-	range = strchr(name, ':');
+	range = first_non_cxx_ns(name);
 	if (range) {
 		*range++ = '\0';
 
@@ -1309,6 +1362,8 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
 		lr->file = name;
 	else if (is_c_func_name(name))/* We reuse it for checking funcname */
 		lr->function = name;
+	else if (is_cxx_func_name(name))
+		lr->function = name;
 	else {	/* Invalid name */
 		semantic_error("'%s' is not a valid function name.\n", name);
 		err = -EINVAL;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC 3/6] perf probe: Make listing of C++ functions work
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
  2018-05-14  4:19 ` [RFC 1/6] perf probe: Do not exclude mangled C++ funcs Holger Freyther
  2018-05-14  4:19 ` [RFC 2/6] perf probe: Parse linerange for C++ functions Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14  4:19 ` [RFC 4/6] perf probe: Show variables for C++ functions Holger Freyther
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

If die_match_name does not match, attempt to demangle the linkage name.
To use the generic demangling API we require to have a struct dso. Store
it inside the debuginfo and pass it to the relevant callbacks.

./perf probe -x ./foo -L \
	"std::vector<int, std::allocator<int> >::at:2-3"
<...::at@/usr/include/c++/5/bits/stl_vector.h:2>
      2         _M_range_check(__n);
      3         return (*this)[__n];

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/util/probe-finder.c | 55 ++++++++++++++++++++++++++++++++++--------
 tools/perf/util/probe-finder.h |  3 +++
 2 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c37fbef..c73dccc 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -96,7 +96,7 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
 	return -ENOENT;
 }
 
-static struct debuginfo *__debuginfo__new(const char *path)
+static struct debuginfo *__debuginfo__new(const char *path, struct dso *dso)
 {
 	struct debuginfo *dbg = zalloc(sizeof(*dbg));
 	if (!dbg)
@@ -104,8 +104,10 @@ static struct debuginfo *__debuginfo__new(const char *path)
 
 	if (debuginfo__init_offline_dwarf(dbg, path) < 0)
 		zfree(&dbg);
-	if (dbg)
+	if (dbg) {
 		pr_debug("Open Debuginfo file: %s\n", path);
+		dbg->dso = dso__get(dso);
+	}
 	return dbg;
 }
 
@@ -135,13 +137,15 @@ struct debuginfo *debuginfo__new(const char *path)
 		if (dso__read_binary_type_filename(dso, *type, &nil,
 						   buf, PATH_MAX) < 0)
 			continue;
-		dinfo = __debuginfo__new(buf);
+		dinfo = __debuginfo__new(buf, dso);
 	}
-	dso__put(dso);
 
 out:
 	/* if failed to open all distro debuginfo, open given binary */
-	return dinfo ? : __debuginfo__new(path);
+	if (!dinfo)
+		dinfo = __debuginfo__new(path, dso);
+	dso__put(dso);
+	return dinfo;
 }
 
 void debuginfo__delete(struct debuginfo *dbg)
@@ -149,6 +153,7 @@ void debuginfo__delete(struct debuginfo *dbg)
 	if (dbg) {
 		if (dbg->dwfl)
 			dwfl_end(dbg->dwfl);
+		dso__put(dbg->dso);
 		free(dbg);
 	}
 }
@@ -167,6 +172,32 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
 }
 
 /*
+ * Check if the the demangled linkage_name matches the function. E.g. the
+ * linkage name of _ZNKSt6vectorIiSaIiEE4sizeEv matching the c++ function name
+ * of std::vector<int, std::allocator<int> >::size() const.
+ */
+static bool matches_demangled(struct debuginfo *dbg, Dwarf_Die *dw_die,
+			      const char *function)
+{
+	const char *name;
+	char *demangled;
+	bool res;
+
+	name = die_get_linkage_name(dw_die);
+	if (!name)
+		return false;
+
+	demangled = dso__demangle_sym(dbg->dso, 0, name);
+	if (!demangled)
+		return false;
+
+	res = strglobmatch(demangled, function);
+	free(demangled);
+	return res;
+}
+
+
+/*
  * Convert a location into trace_arg.
  * If tvar == NULL, this just checks variable can be converted.
  * If fentry == true and vr_die is a parameter, do huristic search
@@ -975,6 +1006,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
 struct dwarf_callback_param {
 	void *data;
 	int retval;
+	struct debuginfo *dbg;
 };
 
 /* Search function from function name */
@@ -1721,7 +1753,8 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
 		return DWARF_CB_OK;
 
 	if (die_is_func_def(sp_die) &&
-	    die_match_name(sp_die, lr->function)) {
+	    (die_match_name(sp_die, lr->function) ||
+	     matches_demangled(param->dbg, sp_die, lr->function))) {
 		lf->fname = dwarf_decl_file(sp_die);
 		dwarf_decl_line(sp_die, &lr->offset);
 		pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
@@ -1744,9 +1777,11 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
 	return DWARF_CB_OK;
 }
 
-static int find_line_range_by_func(struct line_finder *lf)
+static int find_line_range_by_func(struct debuginfo *dbg,
+				   struct line_finder *lf)
 {
-	struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
+	struct dwarf_callback_param param = {
+		.data = (void *)lf, .retval = 0, .dbg = dbg};
 	dwarf_getfuncs(&lf->cu_die, line_range_search_cb, &param, 0);
 	return param.retval;
 }
@@ -1766,7 +1801,7 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
 			.function = lr->function, .file = lr->file,
 			.cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0};
 		struct dwarf_callback_param line_range_param = {
-			.data = (void *)&lf, .retval = 0};
+			.data = (void *)&lf, .retval = 0, .dbg = dbg};
 
 		dwarf_getpubnames(dbg->dbg, pubname_search_cb,
 				  &pubname_param, 0);
@@ -1796,7 +1831,7 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
 
 		if (!lr->file || lf.fname) {
 			if (lr->function)
-				ret = find_line_range_by_func(&lf);
+				ret = find_line_range_by_func(dbg, &lf);
 			else {
 				lf.lno_s = lr->start;
 				lf.lno_e = lr->end;
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index 1625298..e28acbd 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -14,6 +14,8 @@
 #define PROBE_ARG_VARS		"$vars"
 #define PROBE_ARG_PARAMS	"$params"
 
+struct dso;
+
 static inline int is_c_varname(const char *name)
 {
 	/* TODO */
@@ -32,6 +34,7 @@ struct debuginfo {
 	Dwfl_Module	*mod;
 	Dwfl		*dwfl;
 	Dwarf_Addr	bias;
+	struct dso	*dso;
 };
 
 /* This also tries to open distro debuginfo */
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC 4/6] perf probe: Show variables for C++ functions
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
                   ` (2 preceding siblings ...)
  2018-05-14  4:19 ` [RFC 3/6] perf probe: Make listing of C++ functions work Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14  4:19 ` [RFC 5/6] perf probe: Make listing of variables work " Holger Freyther
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

The demangled C++ function name contains spaces and using the generic
argc_split would split the function in the middle. Create a separate
version that counts the number of opening and closing '<', '>' for
templated functions.

$ ./perf probe -x ./foo -V "std::vector<int, std::allocator<int> >::at"
Available variables at std::vector<int, std::allocator<int> >::at
        @<at+0>
                size_type       __n
                vector<int, std::allocator<int> >*      this

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/util/probe-event.c | 20 +++++++++++++--
 tools/perf/util/string.c      | 57 +++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/string2.h     |  1 +
 3 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 39a2d47..97d6b6a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1407,6 +1407,22 @@ static int parse_perf_probe_event_name(char **arg, struct perf_probe_event *pev)
 	return 0;
 }
 
+/* Split the function name from  @file, :line, %return but be C++ aware */
+static char *split_func_name(char *arg)
+{
+	char *ptr = arg;
+
+	while ((ptr = strpbrk_esc(ptr, ";:+@%"))) {
+		if (ptr[0] == ':' && ptr[1] == ':') {
+			ptr += 2;
+			continue;
+		}
+		return ptr;
+	}
+
+	return NULL;
+}
+
 /* Parse probepoint definition. */
 static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 {
@@ -1486,7 +1502,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 			file_spec = true;
 	}
 
-	ptr = strpbrk_esc(arg, ";:+@%");
+	ptr = split_func_name(arg);
 	if (ptr) {
 		nc = *ptr;
 		*ptr++ = '\0';
@@ -1726,7 +1742,7 @@ int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
 	char **argv;
 	int argc, i, ret = 0;
 
-	argv = argv_split(cmd, &argc);
+	argv = argv_split_cxx(cmd, &argc);
 	if (!argv) {
 		pr_debug("Failed to split arguments.\n");
 		return -ENOMEM;
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index d8bfd0c..bb96fe2 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -80,6 +80,23 @@ static const char *skip_arg(const char *cp)
 	return cp;
 }
 
+static const char *skip_arg_cxx(const char *cp)
+{
+	int tmpl = 0;
+
+	while (*cp) {
+		if (tmpl == 0 && isspace(*cp))
+			break;
+		if (*cp == '<')
+			tmpl += 1;
+		if (*cp == '>')
+			tmpl -= 1;
+		cp++;
+	}
+
+	return cp;
+}
+
 static int count_argc(const char *str)
 {
 	int count = 0;
@@ -163,6 +180,46 @@ char **argv_split(const char *str, int *argcp)
 	return NULL;
 }
 
+char **argv_split_cxx(const char *str, int *argcp)
+{
+	int argc = count_argc(str);
+	char **argv = calloc(argc + 1, sizeof(*argv));
+	char **argvp;
+
+	if (argv == NULL)
+		goto out;
+
+	argvp = argv;
+
+	while (*str) {
+		str = skip_sep(str);
+
+		if (*str) {
+			const char *p = str;
+			char *t;
+
+			str = skip_arg_cxx(str);
+
+			t = strndup(p, str-p);
+			if (t == NULL)
+				goto fail;
+			*argvp++ = t;
+		}
+	}
+	if (argcp)
+		*argcp = argvp - argv;
+	*argvp = NULL;
+
+out:
+	return argv;
+
+fail:
+	if (argcp)
+		*argcp = 0;
+	argv_free(argv);
+	return NULL;
+}
+
 /* Character class matching */
 static bool __match_charclass(const char *pat, char c, const char **npat)
 {
diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h
index 4c68a09..d32de6f 100644
--- a/tools/perf/util/string2.h
+++ b/tools/perf/util/string2.h
@@ -8,6 +8,7 @@
 
 s64 perf_atoll(const char *str);
 char **argv_split(const char *str, int *argcp);
+char **argv_split_cxx(const char *str, int *argvcp);
 void argv_free(char **argv);
 bool strglobmatch(const char *str, const char *pat);
 bool strglobmatch_nocase(const char *str, const char *pat);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC 5/6] perf probe: Make listing of variables work for C++ functions
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
                   ` (3 preceding siblings ...)
  2018-05-14  4:19 ` [RFC 4/6] perf probe: Show variables for C++ functions Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14  4:19 ` [RFC 6/6] perf probe: Make it possible to add a C++ uprobe Holger Freyther
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

Update call sites with die_match_name to call matches_demangled as well.
This requires to pass the struct debuginfo/struct dso to the callbacks
and modifies the closure/void *data parameter. For most functions this
will change the parameter from struct probe_finder to the generic struct
dwarf_callback_param.

$ ./perf probe -x ./foo -V "std::vector<int, std::allocator<int> >::at"
Available variables at std::vector<int, std::allocator<int> >::at
        @<at+0>
                size_type       __n
                vector<int, std::allocator<int> >*      this

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/util/probe-finder.c | 88 +++++++++++++++++++++++++++---------------
 1 file changed, 56 insertions(+), 32 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index c73dccc..4ba4b18 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -760,6 +760,7 @@ struct find_scope_param {
 	int line;
 	int diff;
 	Dwarf_Die *die_mem;
+	struct debuginfo *dbg;
 	bool found;
 };
 
@@ -777,7 +778,8 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
 	}
 	/* If the function name is given, that's what user expects */
 	if (fsp->function) {
-		if (die_match_name(fn_die, fsp->function)) {
+		if (die_match_name(fn_die, fsp->function) ||
+			matches_demangled(fsp->dbg, fn_die, fsp->function)) {
 			memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
 			fsp->found = true;
 			return 1;
@@ -795,8 +797,16 @@ static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
 	return 0;
 }
 
+/* Callback parameter with return value for libdw */
+struct dwarf_callback_param {
+	void *data;
+	int retval;
+	struct debuginfo *dbg;
+};
+
 /* Find an appropriate scope fits to given conditions */
-static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
+static Dwarf_Die *find_best_scope(struct debuginfo *dbg,
+				  struct probe_finder *pf, Dwarf_Die *die_mem)
 {
 	struct find_scope_param fsp = {
 		.function = pf->pev->point.function,
@@ -804,6 +814,7 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
 		.line = pf->lno,
 		.diff = INT_MAX,
 		.die_mem = die_mem,
+		.dbg = dbg,
 		.found = false,
 	};
 
@@ -815,7 +826,8 @@ static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
 static int probe_point_line_walker(const char *fname, int lineno,
 				   Dwarf_Addr addr, void *data)
 {
-	struct probe_finder *pf = data;
+	struct dwarf_callback_param *param = data;
+	struct probe_finder *pf = param->data;
 	Dwarf_Die *sc_die, die_mem;
 	int ret;
 
@@ -823,7 +835,7 @@ static int probe_point_line_walker(const char *fname, int lineno,
 		return 0;
 
 	pf->addr = addr;
-	sc_die = find_best_scope(pf, &die_mem);
+	sc_die = find_best_scope(param->dbg, pf, &die_mem);
 	if (!sc_die) {
 		pr_warning("Failed to find scope of probe point.\n");
 		return -ENOENT;
@@ -836,9 +848,12 @@ static int probe_point_line_walker(const char *fname, int lineno,
 }
 
 /* Find probe point from its line number */
-static int find_probe_point_by_line(struct probe_finder *pf)
+static int find_probe_point_by_line(struct debuginfo *dbg,
+				    struct probe_finder *pf)
 {
-	return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
+	struct dwarf_callback_param param = {
+		.data = (void *)pf, .dbg = dbg, .retval = 0};
+	return die_walk_lines(&pf->cu_die, probe_point_line_walker, &param);
 }
 
 /* Find lines which match lazy pattern */
@@ -884,7 +899,8 @@ static int find_lazy_match_lines(struct intlist *list,
 static int probe_point_lazy_walker(const char *fname, int lineno,
 				   Dwarf_Addr addr, void *data)
 {
-	struct probe_finder *pf = data;
+	struct dwarf_callback_param *param = data;
+	struct probe_finder *pf = param->data;
 	Dwarf_Die *sc_die, die_mem;
 	int ret;
 
@@ -896,7 +912,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
 		 lineno, (unsigned long long)addr);
 	pf->addr = addr;
 	pf->lno = lineno;
-	sc_die = find_best_scope(pf, &die_mem);
+	sc_die = find_best_scope(param->dbg, pf, &die_mem);
 	if (!sc_die) {
 		pr_warning("Failed to find scope of probe point.\n");
 		return -ENOENT;
@@ -912,8 +928,10 @@ static int probe_point_lazy_walker(const char *fname, int lineno,
 }
 
 /* Find probe points from lazy pattern  */
-static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
+static int find_probe_point_lazy(Dwarf_Die *sp_die,
+				 struct dwarf_callback_param *param)
 {
+	struct probe_finder *pf = param->data;
 	int ret = 0;
 	char *fpath;
 
@@ -935,7 +953,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
 			return ret;
 	}
 
-	return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
+	return die_walk_lines(sp_die, probe_point_lazy_walker, param);
 }
 
 static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf)
@@ -972,13 +990,14 @@ static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf)
 
 static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
 {
-	struct probe_finder *pf = data;
+	struct dwarf_callback_param *param = data;
+	struct probe_finder *pf = param->data;
 	struct perf_probe_point *pp = &pf->pev->point;
 	Dwarf_Addr addr;
 	int ret;
 
 	if (pp->lazy_line)
-		ret = find_probe_point_lazy(in_die, pf);
+		ret = find_probe_point_lazy(in_die, param);
 	else {
 		/* Get probe address */
 		if (dwarf_entrypc(in_die, &addr) != 0) {
@@ -1002,13 +1021,6 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
 	return ret;
 }
 
-/* Callback parameter with return value for libdw */
-struct dwarf_callback_param {
-	void *data;
-	int retval;
-	struct debuginfo *dbg;
-};
-
 /* Search function from function name */
 static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 {
@@ -1018,7 +1030,8 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 
 	/* Check tag and diename */
 	if (!die_is_func_def(sp_die) ||
-	    !die_match_name(sp_die, pp->function))
+	    (!die_match_name(sp_die, pp->function) &&
+	     !matches_demangled(param->dbg, sp_die, pp->function)))
 		return DWARF_CB_OK;
 
 	/* Check declared file */
@@ -1031,7 +1044,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 	if (pp->line) { /* Function relative line */
 		dwarf_decl_line(sp_die, &pf->lno);
 		pf->lno += pp->line;
-		param->retval = find_probe_point_by_line(pf);
+		param->retval = find_probe_point_by_line(param->dbg, pf);
 	} else if (die_is_func_instance(sp_die)) {
 		/* Instances always have the entry address */
 		dwarf_entrypc(sp_die, &pf->addr);
@@ -1042,7 +1055,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 			param->retval = 0;
 		/* Real function */
 		} else if (pp->lazy_line)
-			param->retval = find_probe_point_lazy(sp_die, pf);
+			param->retval = find_probe_point_lazy(sp_die, param);
 		else {
 			skip_prologue(sp_die, pf);
 			pf->addr += pp->offset;
@@ -1052,7 +1065,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 	} else if (!probe_conf.no_inlines) {
 		/* Inlined function: search instances */
 		param->retval = die_walk_instances(sp_die,
-					probe_point_inline_cb, (void *)pf);
+					probe_point_inline_cb, data);
 		/* This could be a non-existed inline definition */
 		if (param->retval == -ENOENT)
 			param->retval = 0;
@@ -1067,9 +1080,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
 	return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
 }
 
-static int find_probe_point_by_func(struct probe_finder *pf)
+static int find_probe_point_by_func(struct debuginfo *dbg,
+				    struct probe_finder *pf)
 {
-	struct dwarf_callback_param _param = {.data = (void *)pf,
+	struct dwarf_callback_param _param = {.data = (void *)pf, .dbg = dbg,
 					      .retval = 0};
 	dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
 	return _param.retval;
@@ -1080,6 +1094,7 @@ struct pubname_callback_param {
 	char *file;
 	Dwarf_Die *cu_die;
 	Dwarf_Die *sp_die;
+	struct debuginfo *dbg;
 	int found;
 };
 
@@ -1091,7 +1106,9 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
 		if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
 			return DWARF_CB_OK;
 
-		if (die_match_name(param->sp_die, param->function)) {
+		if (die_match_name(param->sp_die, param->function) ||
+			matches_demangled(param->dbg, param->sp_die,
+					  param->function)) {
 			if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
 				return DWARF_CB_OK;
 
@@ -1128,10 +1145,12 @@ static int debuginfo__find_probe_location(struct debuginfo *dbg,
 			.file	  = pp->file,
 			.cu_die	  = &pf->cu_die,
 			.sp_die	  = &pf->sp_die,
+			.dbg      = dbg,
 			.found	  = 0,
 		};
 		struct dwarf_callback_param probe_param = {
 			.data = pf,
+			.dbg  = dbg,
 		};
 
 		dwarf_getpubnames(dbg->dbg, pubname_search_cb,
@@ -1158,12 +1177,17 @@ static int debuginfo__find_probe_location(struct debuginfo *dbg,
 
 		if (!pp->file || pf->fname) {
 			if (pp->function)
-				ret = find_probe_point_by_func(pf);
-			else if (pp->lazy_line)
-				ret = find_probe_point_lazy(&pf->cu_die, pf);
-			else {
+				ret = find_probe_point_by_func(dbg, pf);
+			else if (pp->lazy_line) {
+				struct dwarf_callback_param probe_param = {
+					.data = pf,
+					.dbg  = dbg,
+				};
+				ret = find_probe_point_lazy(&pf->cu_die,
+							    &probe_param);
+			} else {
 				pf->lno = pp->line;
-				ret = find_probe_point_by_line(pf);
+				ret = find_probe_point_by_line(dbg, pf);
 			}
 			if (ret < 0)
 				break;
@@ -1798,7 +1822,7 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
 	/* Fastpath: lookup by function name from .debug_pubnames section */
 	if (lr->function) {
 		struct pubname_callback_param pubname_param = {
-			.function = lr->function, .file = lr->file,
+			.function = lr->function, .file = lr->file, .dbg = dbg,
 			.cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0};
 		struct dwarf_callback_param line_range_param = {
 			.data = (void *)&lf, .retval = 0, .dbg = dbg};
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [RFC 6/6] perf probe: Make it possible to add a C++ uprobe
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
                   ` (4 preceding siblings ...)
  2018-05-14  4:19 ` [RFC 5/6] perf probe: Make listing of variables work " Holger Freyther
@ 2018-05-14  4:19 ` Holger Freyther
  2018-05-14 13:08 ` [RFC 0/6] perf probe: Attempt to improve C++ probing Masami Hiramatsu
  2018-05-14 13:31 ` Masami Hiramatsu
  7 siblings, 0 replies; 10+ messages in thread
From: Holger Freyther @ 2018-05-14  4:19 UTC (permalink / raw)
  To: linux-kernel; +Cc: Holger Hans Peter Freyther

From: Holger Hans Peter Freyther <holgar+kernel@google.com>

If the linkage name looks like a common C++ ABI name use it instead of
the original function name. This makes adding a uprobe for a C++ symbol
possible.

./perf probe -x ./cxx-example  "std::vector<int, std::allocator<int> >::at"
Added new event:
  probe_foo:_ZNSt6vectorIiSaIiEE2atEm (on _ZN... in /cxx-example)

You can now use it in all perf tools, such as:

	perf record -e probe_foo:_ZNSt6vectorIiSaIiEE2atEm -aR sleep 1

Signed-off-by: Holger Hans Peter Freyther <holgar+kernel@google.com>
---
 tools/perf/util/probe-finder.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 4ba4b18..4cfa3de 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1317,6 +1317,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
 	struct perf_probe_point *pp = &pf->pev->point;
 	struct probe_trace_event *tev;
 	struct perf_probe_arg *args = NULL;
+	const char *linkage_name;
 	int ret, i;
 
 	/* Check number of tevs */
@@ -1333,6 +1334,16 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
 	if (ret < 0)
 		goto end;
 
+	/*
+	 * Adding a C++ name like std::vector<int, std::allocator<int> >::at
+	 * will fail. Check if we want to use the linkage name instead.
+	 */
+	linkage_name = die_get_linkage_name(&pf->sp_die);
+	if (linkage_name && strncmp(linkage_name, "_Z", 2) == 0) {
+		free(pp->function);
+		pp->function = strdup(linkage_name);
+	}
+
 	tev->point.realname = strdup(dwarf_diename(sc_die));
 	if (!tev->point.realname) {
 		ret = -ENOMEM;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [RFC 0/6] perf probe: Attempt to improve C++ probing
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
                   ` (5 preceding siblings ...)
  2018-05-14  4:19 ` [RFC 6/6] perf probe: Make it possible to add a C++ uprobe Holger Freyther
@ 2018-05-14 13:08 ` Masami Hiramatsu
  2018-05-14 13:31 ` Masami Hiramatsu
  7 siblings, 0 replies; 10+ messages in thread
From: Masami Hiramatsu @ 2018-05-14 13:08 UTC (permalink / raw)
  To: Holger Freyther; +Cc: linux-kernel, Holger Hans Peter Freyther

Hi Holger,

Thank you for your great work!

On Mon, 14 May 2018 12:19:34 +0800
Holger Freyther <automatic+kernel@freyther.de> wrote:

> From: Holger Hans Peter Freyther <holgar+kernel@google.com>
> 
> Currently perf probe -x app --funcs will list and demangle C++ functions
> but the other probe actions can't work with them. When asking probe to not
> demangle it will not list any of the application symbols creating the
> impression that there are no symbols at all.
> 
> Make --funcs --no-demangle list all C++ functions and modify the handling
> for listing code, variables and adding the uprobe work with the demangled
> C++ function name.
> 
> I tried to keep this as minimal as possible but having to keep the dso in
> the debuginfo and passing it everywhere to be able to demangle the linkage
> name isn't pretty (and for C++ demangling the struct dso is not of much
> use. Maybe having a static "empty" dso could avoid a lot of the changes).
> 
> Maybe the easiest first patch is to default to --no-demangle and change
> the DEFAULT_FUNC_FILTER to not include mangled C++ symbols. The remaining
> tooling would work then.
> 
> This has seen very little testing outside the following commands.
> 
> My test set includes:
> 
>  ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at"
>  ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at:2-3"
> 
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at"
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at:2"
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::size%return"

OK, let me review it.

> 
> 
> Holger Hans Peter Freyther (6):
>   perf probe: Do not exclude mangled C++ funcs
>   perf probe: Parse linerange for C++ functions
>   perf probe: Make listing of C++ functions work
>   perf probe: Show variables for C++ functions
>   perf probe: Make listing of variables work for C++ functions
>   perf probe: Make it possible to add a C++ uprobe
> 
>  tools/perf/builtin-probe.c     |   2 +-
>  tools/perf/util/probe-event.c  |  77 ++++++++++++++++++++-
>  tools/perf/util/probe-finder.c | 152 ++++++++++++++++++++++++++++++-----------
>  tools/perf/util/probe-finder.h |   3 +
>  tools/perf/util/string.c       |  57 ++++++++++++++++
>  tools/perf/util/string2.h      |   1 +
>  6 files changed, 247 insertions(+), 45 deletions(-)
> 
> -- 
> 2.7.4
> 


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC 0/6] perf probe: Attempt to improve C++ probing
  2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
                   ` (6 preceding siblings ...)
  2018-05-14 13:08 ` [RFC 0/6] perf probe: Attempt to improve C++ probing Masami Hiramatsu
@ 2018-05-14 13:31 ` Masami Hiramatsu
  2018-05-15 14:10   ` Arnaldo Carvalho de Melo
  7 siblings, 1 reply; 10+ messages in thread
From: Masami Hiramatsu @ 2018-05-14 13:31 UTC (permalink / raw)
  To: Holger Freyther, Arnaldo Carvalho de Melo
  Cc: linux-kernel, Holger Hans Peter Freyther

On Mon, 14 May 2018 12:19:34 +0800
Holger Freyther <automatic+kernel@freyther.de> wrote:

> From: Holger Hans Peter Freyther <holgar+kernel@google.com>
> 
> Currently perf probe -x app --funcs will list and demangle C++ functions
> but the other probe actions can't work with them. When asking probe to not
> demangle it will not list any of the application symbols creating the
> impression that there are no symbols at all.
> 
> Make --funcs --no-demangle list all C++ functions and modify the handling
> for listing code, variables and adding the uprobe work with the demangled
> C++ function name.
> 
> I tried to keep this as minimal as possible but having to keep the dso in
> the debuginfo and passing it everywhere to be able to demangle the linkage
> name isn't pretty (and for C++ demangling the struct dso is not of much
> use. Maybe having a static "empty" dso could avoid a lot of the changes).
> 
> Maybe the easiest first patch is to default to --no-demangle and change
> the DEFAULT_FUNC_FILTER to not include mangled C++ symbols. The remaining
> tooling would work then.
> 
> This has seen very little testing outside the following commands.
> 
> My test set includes:
> 
>  ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at"
>  ./perf probe -x . -L "std::vector<int, std::allocator<int> >::at:2-3"
> 
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at"
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::at:2"
>  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::size%return"

OK, this series looks good to me :)

Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
for this series.

Arnaldo, could you pull this series?

Thank you!


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [RFC 0/6] perf probe: Attempt to improve C++ probing
  2018-05-14 13:31 ` Masami Hiramatsu
@ 2018-05-15 14:10   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 10+ messages in thread
From: Arnaldo Carvalho de Melo @ 2018-05-15 14:10 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Holger Freyther, linux-kernel, Holger Hans Peter Freyther

Em Mon, May 14, 2018 at 10:31:07PM +0900, Masami Hiramatsu escreveu:
> On Mon, 14 May 2018 12:19:34 +0800 Holger Freyther <automatic+kernel@freyther.de> wrote:
> >  ./perf probe -x . -V "std::vector<int, std::allocator<int> >::size%return"
 
> OK, this series looks good to me :)
 
> Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
> for this series.
 
> Arnaldo, could you pull this series?

Sure, I'm just waiting for Holger to comment on Namhyung points,

Thanks,

- Arnaldo

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2018-05-15 14:10 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-14  4:19 [RFC 0/6] perf probe: Attempt to improve C++ probing Holger Freyther
2018-05-14  4:19 ` [RFC 1/6] perf probe: Do not exclude mangled C++ funcs Holger Freyther
2018-05-14  4:19 ` [RFC 2/6] perf probe: Parse linerange for C++ functions Holger Freyther
2018-05-14  4:19 ` [RFC 3/6] perf probe: Make listing of C++ functions work Holger Freyther
2018-05-14  4:19 ` [RFC 4/6] perf probe: Show variables for C++ functions Holger Freyther
2018-05-14  4:19 ` [RFC 5/6] perf probe: Make listing of variables work " Holger Freyther
2018-05-14  4:19 ` [RFC 6/6] perf probe: Make it possible to add a C++ uprobe Holger Freyther
2018-05-14 13:08 ` [RFC 0/6] perf probe: Attempt to improve C++ probing Masami Hiramatsu
2018-05-14 13:31 ` Masami Hiramatsu
2018-05-15 14:10   ` Arnaldo Carvalho de Melo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).