Skip to contentSkip to conversation

perf-probe --cache and SDT support

PATCH Superseded Masami Hiramatsu 4 participants 41 messages Jun 8, 2016 – Jun 16, 2016 perf events

v10 of 13 Superseded Latest is v13 →

lore
Share & tools
lore
#0 lore raw

v10 cover perf-probe --cache and SDT support

Hi,

Here is the 10th version of the patchset for probe-cache and initial SDT support.

This version fixes a bug and split big patches into small subsets according to Namhyung's comment.
Also I've added a testcase for SDT. In this version, the number of patches are increased, but basically, only 3 patches (bugfix, and adding test) are new.

Changes in v10:
- [1/23] Fix a bug in rm_rf().
- [2/23,] Separate a big patch into 3 sub patches.
- [3/23 - 7/23] separate a big patch into 5 sub patches.
- [13/23, 14/23] Update documents for SDT explanations.
- [22/23, 23/23] Add a test case for SDT and update build config to detect sys/sdt.h.

Thank you,

---

Hemant Kumar (1):
perf/sdt: ELF support for SDT

Masami Hiramatsu (22):
perf: util: Fix rm_rf() to handle non-regular files correctly
perf-probe: Fix to add NULL check for strndup
perf-buildid: Rename and export build_id_cache__cachedir() perf probe: Add perf_probe_event__copy()
perf probe: Recover and export synthesize_perf_probe_point() perf probe-file: Introduce perf_cache interfaces
perf probe: Add --cache option to cache the probe definitions perf probe: Use cache entry if possible
perf probe: Show all cached probes
perf probe: Remove caches when --cache is given
perf probe: Add group name support
perf buildid-cache: Scan and import user SDT events to probe cache perf probe: Accept %sdt and %cached event name
perf-list: Show SDT and pre-cached events
perf-list: Skip SDTs placed in invalid binaries
perf: probe-cache: Add for_each_probe_cache_entry() wrapper perf probe: Allow wildcard for cached events
perf probe: Search SDT/cached event from all probe caches perf probe: Support @BUILDID or @FILE suffix for SDT events perf probe: Support a special SDT probe format
perf build: Add sdt feature detection
perf-test: Add a test case for SDT event

 tools/perf/Documentation/perf-buildid-cache.txt |    3 
 tools/perf/Documentation/perf-probe.txt         |   32 +
 tools/perf/Makefile.perf                        |    3 
 tools/perf/builtin-list.c                       |    6 
 tools/perf/builtin-probe.c                      |   30 +
 tools/perf/config/Makefile                      |   10 
 tools/perf/tests/Build                          |    1 
 tools/perf/tests/builtin-test.c                 |    4 
 tools/perf/tests/make                           |    3 
 tools/perf/tests/sdt.c                          |  114 +++++
 tools/perf/tests/tests.h                        |    1 
 tools/perf/util/build-id.c                      |  196 ++++++++-
 tools/perf/util/build-id.h                      |    6 
 tools/perf/util/parse-events.c                  |   83 ++++
 tools/perf/util/parse-events.h                  |    2 
 tools/perf/util/probe-event.c                   |  490 ++++++++++++++++++++--
 tools/perf/util/probe-event.h                   |    6 
 tools/perf/util/probe-file.c                    |  504 +++++++++++++++++++++++
 tools/perf/util/probe-file.h                    |   41 ++
 tools/perf/util/symbol-elf.c                    |  252 ++++++++++++
 tools/perf/util/symbol.h                        |   22 +
 tools/perf/util/util.c                          |   13 -
 22 files changed, 1736 insertions(+), 86 deletions(-)
 create mode 100644 tools/perf/tests/sdt.c

--
Masami Hiramatsu

#1 lore raw

PATCH v10 1/23 perf: util: Fix rm_rf() to handle non-regular files correctly

Fix rm_rf() to handle non-regular files correctly. This fix includes two changes;

- Fix to use lstat(3) instead of stat(3) since if the target file is a symbolic link, rm_rf() should unlink the symbolic link itself, not the file which pointed by the symlink.
- Fix to unlink non-regular files (except for directory), including symlink.

Even though the first one fixes to stat symlink itself, without second fix, it still failed because the symlink is not a regular file.

---
 tools/perf/util/util.c |   13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)
tools/perf/util/util.c +5 -8
index 23504ad..e08b9a0 100644
@@ -97,20 +97,17 @@ int rm_rf(char *path)
 		scnprintf(namebuf, sizeof(namebuf), "%s/%s",
 			  path, d->d_name);
 
-		ret = stat(namebuf, &statbuf);
+		/* We have to check symbolic link itself */
+		ret = lstat(namebuf, &statbuf);
 		if (ret < 0) {
 			pr_debug("stat failed: %s\n", namebuf);
 			break;
 		}
 
-		if (S_ISREG(statbuf.st_mode))
-			ret = unlink(namebuf);
-		else if (S_ISDIR(statbuf.st_mode))
+		if (S_ISDIR(statbuf.st_mode))
 			ret = rm_rf(namebuf);
-		else {
-			pr_debug("unknown file: %s\n", namebuf);
-			ret = -1;
-		}
+		else
+			ret = unlink(namebuf);
 	}
 	closedir(dir);
 
#2 lore raw

[tip:perf/core] perf tools: Fix rm_rf() to handle non-regular files correctly

perf tools: Fix rm_rf() to handle non-regular files correctly
Commit-ID:  2a1ef032cfccd8c92f32b86615a0b0151a7cd86f
Gitweb:     http://git.kernel.org/tip/2a1ef032cfccd8c92f32b86615a0b0151a7cd86f
Author:     Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 8 Jun 2016 18:29:11 +0900
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Tue, 14 Jun 2016 09:29:54 -0300

perf tools: Fix rm_rf() to handle non-regular files correctly

Fix rm_rf() to handle non-regular files correctly. This fix includes two changes;

- Fix to use lstat(3) instead of stat(3) since if the target file is a symbolic link, rm_rf() should unlink the symbolic link itself, not the file which pointed by the symlink.
- Fix to unlink non-regular files (except for directory), including symlink.

Even though the first one fixes to stat symlink itself, without second fix, it still failed because the symlink is not a regular file.

---
 tools/perf/util/util.c | 13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)
tools/perf/util/util.c +5 -8
index 23504ad..e08b9a0 100644
@@ -97,20 +97,17 @@ int rm_rf(char *path)
 		scnprintf(namebuf, sizeof(namebuf), "%s/%s",
 			  path, d->d_name);
 
-		ret = stat(namebuf, &statbuf);
+		/* We have to check symbolic link itself */
+		ret = lstat(namebuf, &statbuf);
 		if (ret < 0) {
 			pr_debug("stat failed: %s\n", namebuf);
 			break;
 		}
 
-		if (S_ISREG(statbuf.st_mode))
-			ret = unlink(namebuf);
-		else if (S_ISDIR(statbuf.st_mode))
+		if (S_ISDIR(statbuf.st_mode))
 			ret = rm_rf(namebuf);
-		else {
-			pr_debug("unknown file: %s\n", namebuf);
-			ret = -1;
-		}
+		else
+			ret = unlink(namebuf);
 	}
 	closedir(dir);
 
#3 lore raw

PATCH v10 2/23 perf-probe: Fix to add NULL check for strndup

In reply to Masami Hiramatsu

Fix to add a NULL check for strndup when parsing
probe trace command.

---
Changes in v10:
- Splited from "perf probe: Allow wildcard for cached events"

---
 tools/perf/util/probe-event.c |    4 ++++
 1 file changed, 4 insertions(+)
tools/perf/util/probe-event.c +4 -0
index 74401a2..7ae3dd1 100644
@@ -1603,6 +1603,10 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
 	p = strchr(argv[1], ':');
 	if (p) {
 		tp->module = strndup(argv[1], p - argv[1]);
+		if (!tp->module) {
+			ret = -ENOMEM;
+			goto out;
+		}
 		p++;
 	} else
 		p = argv[1];
#4 lore raw

[tip:perf/core] perf probe: Fix to add NULL check for strndup

perf probe: Fix to add NULL check for strndup
Commit-ID:  844faa4bcddc5d321311003ea3af9d808371c48e
Gitweb:     http://git.kernel.org/tip/844faa4bcddc5d321311003ea3af9d808371c48e
Author:     Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 8 Jun 2016 18:29:21 +0900
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Tue, 14 Jun 2016 09:29:54 -0300

perf probe: Fix to add NULL check for strndup

Fix to add a NULL check for strndup when parsing probe trace command.

---
 tools/perf/util/probe-event.c | 4 ++++
 1 file changed, 4 insertions(+)
tools/perf/util/probe-event.c +4 -0
index 74401a2..7ae3dd1 100644
@@ -1603,6 +1603,10 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
 	p = strchr(argv[1], ':');
 	if (p) {
 		tp->module = strndup(argv[1], p - argv[1]);
+		if (!tp->module) {
+			ret = -ENOMEM;
+			goto out;
+		}
 		p++;
 	} else
 		p = argv[1];
#5 lore raw

PATCH v10 3/23 perf-buildid: Rename and export build_id_cache__cachedir()

In reply to Masami Hiramatsu

Rename and export build_id_cache__cachedir() for retrieving use of the path of cache directory for given build_id.

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/build-id.c |   12 +++++-------
 tools/perf/util/build-id.h |    2 ++
 2 files changed, 7 insertions(+), 7 deletions(-)
tools/perf/util/build-id.c +5 -7
index 67f986c..ccf0d0d 100644
@@ -390,9 +390,8 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
-static char *build_id_cache__dirname_from_path(const char *name,
-					       bool is_kallsyms, bool is_vdso,
-					       const char *sbuild_id)
+char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
+			       bool is_kallsyms, bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
@@ -420,8 +419,7 @@ int build_id_cache__list_build_ids(const char *pathname,
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__dirname_from_path(pathname, false, false,
-						     NULL);
+	dir_name = build_id_cache__cachedir(NULL, pathname, false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -447,8 +445,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__dirname_from_path(name, is_kallsyms,
-						     is_vdso, sbuild_id);
+	dir_name = build_id_cache__cachedir(sbuild_id, name,
+					    is_kallsyms, is_vdso);
 	if (!dir_name)
 		goto out_free;
 
tools/perf/util/build-id.h +2 -0
index e5435f4..d8c7f2f 100644
@@ -30,6 +30,8 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
 int perf_session__write_buildid_table(struct perf_session *session, int fd);
 int perf_session__cache_build_ids(struct perf_session *session);
 
+char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
+			       bool is_kallsyms, bool is_vdso);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
#6 lore raw

[tip:perf/core] perf buildid: Rename and export build_id_cache__cachedir()

perf buildid: Rename and export build_id_cache__cachedir()
Commit-ID:  4698b8b7572ff74d9d17fdb02d5957b7148c64fe
Gitweb:     http://git.kernel.org/tip/4698b8b7572ff74d9d17fdb02d5957b7148c64fe
Author:     Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 8 Jun 2016 18:29:30 +0900
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Tue, 14 Jun 2016 09:29:54 -0300

perf buildid: Rename and export build_id_cache__cachedir()

Rename and export build_id_cache__cachedir() for retrieving use of the path of cache directory for given build_id.

---
 tools/perf/util/build-id.c | 12 +++++-------
 tools/perf/util/build-id.h |  2 ++
 2 files changed, 7 insertions(+), 7 deletions(-)
tools/perf/util/build-id.c +5 -7
index 20aef90..62b1473 100644
@@ -387,9 +387,8 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
-static char *build_id_cache__dirname_from_path(const char *name,
-					       bool is_kallsyms, bool is_vdso,
-					       const char *sbuild_id)
+char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
+			       bool is_kallsyms, bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
@@ -417,8 +416,7 @@ int build_id_cache__list_build_ids(const char *pathname,
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__dirname_from_path(pathname, false, false,
-						     NULL);
+	dir_name = build_id_cache__cachedir(NULL, pathname, false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -444,8 +442,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__dirname_from_path(name, is_kallsyms,
-						     is_vdso, sbuild_id);
+	dir_name = build_id_cache__cachedir(sbuild_id, name,
+					    is_kallsyms, is_vdso);
 	if (!dir_name)
 		goto out_free;
 
tools/perf/util/build-id.h +2 -0
index e5435f4..d8c7f2f 100644
@@ -30,6 +30,8 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
 int perf_session__write_buildid_table(struct perf_session *session, int fd);
 int perf_session__cache_build_ids(struct perf_session *session);
 
+char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
+			       bool is_kallsyms, bool is_vdso);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
#7 lore raw

PATCH v10 4/23 perf probe: Add perf_probe_event__copy()

In reply to Masami Hiramatsu

Add perf_probe_event__copy() to copy perf_probe_event data structure and sub data structures under given source perf_probe_event.

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/probe-event.c |   76 +++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/probe-event.h |    3 ++
 2 files changed, 76 insertions(+), 3 deletions(-)
tools/perf/util/probe-event.c +73 -3
index 7ae3dd1..84f4b2b3 100644
@@ -2030,6 +2030,79 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
 	memset(pev, 0, sizeof(*pev));
 }
 
+#define strdup_or_goto(str, label)	\
+({ char *__p = NULL; if (str && !(__p = strdup(str))) goto label; __p; })
+
+static int perf_probe_point__copy(struct perf_probe_point *dst,
+				  struct perf_probe_point *src)
+{
+	dst->file = strdup_or_goto(src->file, out_err);
+	dst->function = strdup_or_goto(src->function, out_err);
+	dst->lazy_line = strdup_or_goto(src->lazy_line, out_err);
+	dst->line = src->line;
+	dst->retprobe = src->retprobe;
+	dst->offset = src->offset;
+	return 0;
+
+out_err:
+	clear_perf_probe_point(dst);
+	return -ENOMEM;
+}
+
+static int perf_probe_arg__copy(struct perf_probe_arg *dst,
+				struct perf_probe_arg *src)
+{
+	struct perf_probe_arg_field *field, **ppfield;
+
+	dst->name = strdup_or_goto(src->name, out_err);
+	dst->var = strdup_or_goto(src->var, out_err);
+	dst->type = strdup_or_goto(src->type, out_err);
+
+	field = src->field;
+	ppfield = &(dst->field);
+	while (field) {
+		*ppfield = zalloc(sizeof(*field));
+		if (!*ppfield)
+			goto out_err;
+		(*ppfield)->name = strdup_or_goto(field->name, out_err);
+		(*ppfield)->index = field->index;
+		(*ppfield)->ref = field->ref;
+		field = field->next;
+		ppfield = &((*ppfield)->next);
+	}
+	return 0;
+out_err:
+	return -ENOMEM;
+}
+
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src)
+{
+	int i;
+
+	dst->event = strdup_or_goto(src->event, out_err);
+	dst->group = strdup_or_goto(src->group, out_err);
+	dst->target = strdup_or_goto(src->target, out_err);
+	dst->uprobes = src->uprobes;
+
+	if (perf_probe_point__copy(&dst->point, &src->point) < 0)
+		goto out_err;
+
+	dst->args = zalloc(sizeof(struct perf_probe_arg) * src->nargs);
+	if (!dst->args)
+		goto out_err;
+	dst->nargs = src->nargs;
+
+	for (i = 0; i < src->nargs; i++)
+		if (perf_probe_arg__copy(&dst->args[i], &src->args[i]) < 0)
+			goto out_err;
+	return 0;
+
+out_err:
+	clear_perf_probe_event(dst);
+	return -ENOMEM;
+}
+
 void clear_probe_trace_event(struct probe_trace_event *tev)
 {
 	struct probe_trace_arg_ref *ref, *next;
@@ -2505,9 +2578,6 @@ static int find_probe_functions(struct map *map, char *name,
 	return found;
 }
 
-#define strdup_or_goto(str, label)	\
-	({ char *__p = strdup(str); if (!__p) goto label; __p; })
-
 void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
 				struct probe_trace_event *tev __maybe_unused,
 				struct map *map __maybe_unused,
tools/perf/util/probe-event.h +3 -0
index 5a27eb4..367f886 100644
@@ -122,6 +122,9 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev);
 char *synthesize_probe_trace_command(struct probe_trace_event *tev);
 char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
 
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src);
+
 /* Check the perf_probe_event needs debuginfo */
 bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
 
#8 lore raw

[tip:perf/core] perf probe: Add perf_probe_event__copy()

perf probe: Add perf_probe_event__copy()
Commit-ID:  0542bb9c8da51faa8d8703c394c32e334ac4e9d6
Gitweb:     http://git.kernel.org/tip/0542bb9c8da51faa8d8703c394c32e334ac4e9d6
Author:     Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 8 Jun 2016 18:29:40 +0900
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Tue, 14 Jun 2016 09:29:54 -0300

perf probe: Add perf_probe_event__copy()

Add perf_probe_event__copy() to copy perf_probe_event data structure and sub data structures under given source perf_probe_event.

---
 tools/perf/util/probe-event.c | 76 +++++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/probe-event.h |  3 ++
 2 files changed, 76 insertions(+), 3 deletions(-)
tools/perf/util/probe-event.c +73 -3
index 7ae3dd1..84f4b2b3 100644
@@ -2030,6 +2030,79 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
 	memset(pev, 0, sizeof(*pev));
 }
 
+#define strdup_or_goto(str, label)	\
+({ char *__p = NULL; if (str && !(__p = strdup(str))) goto label; __p; })
+
+static int perf_probe_point__copy(struct perf_probe_point *dst,
+				  struct perf_probe_point *src)
+{
+	dst->file = strdup_or_goto(src->file, out_err);
+	dst->function = strdup_or_goto(src->function, out_err);
+	dst->lazy_line = strdup_or_goto(src->lazy_line, out_err);
+	dst->line = src->line;
+	dst->retprobe = src->retprobe;
+	dst->offset = src->offset;
+	return 0;
+
+out_err:
+	clear_perf_probe_point(dst);
+	return -ENOMEM;
+}
+
+static int perf_probe_arg__copy(struct perf_probe_arg *dst,
+				struct perf_probe_arg *src)
+{
+	struct perf_probe_arg_field *field, **ppfield;
+
+	dst->name = strdup_or_goto(src->name, out_err);
+	dst->var = strdup_or_goto(src->var, out_err);
+	dst->type = strdup_or_goto(src->type, out_err);
+
+	field = src->field;
+	ppfield = &(dst->field);
+	while (field) {
+		*ppfield = zalloc(sizeof(*field));
+		if (!*ppfield)
+			goto out_err;
+		(*ppfield)->name = strdup_or_goto(field->name, out_err);
+		(*ppfield)->index = field->index;
+		(*ppfield)->ref = field->ref;
+		field = field->next;
+		ppfield = &((*ppfield)->next);
+	}
+	return 0;
+out_err:
+	return -ENOMEM;
+}
+
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src)
+{
+	int i;
+
+	dst->event = strdup_or_goto(src->event, out_err);
+	dst->group = strdup_or_goto(src->group, out_err);
+	dst->target = strdup_or_goto(src->target, out_err);
+	dst->uprobes = src->uprobes;
+
+	if (perf_probe_point__copy(&dst->point, &src->point) < 0)
+		goto out_err;
+
+	dst->args = zalloc(sizeof(struct perf_probe_arg) * src->nargs);
+	if (!dst->args)
+		goto out_err;
+	dst->nargs = src->nargs;
+
+	for (i = 0; i < src->nargs; i++)
+		if (perf_probe_arg__copy(&dst->args[i], &src->args[i]) < 0)
+			goto out_err;
+	return 0;
+
+out_err:
+	clear_perf_probe_event(dst);
+	return -ENOMEM;
+}
+
 void clear_probe_trace_event(struct probe_trace_event *tev)
 {
 	struct probe_trace_arg_ref *ref, *next;
@@ -2505,9 +2578,6 @@ static int find_probe_functions(struct map *map, char *name,
 	return found;
 }
 
-#define strdup_or_goto(str, label)	\
-	({ char *__p = strdup(str); if (!__p) goto label; __p; })
-
 void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
 				struct probe_trace_event *tev __maybe_unused,
 				struct map *map __maybe_unused,
tools/perf/util/probe-event.h +3 -0
index 5a27eb4..367f886 100644
@@ -122,6 +122,9 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev);
 char *synthesize_probe_trace_command(struct probe_trace_event *tev);
 char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
 
+int perf_probe_event__copy(struct perf_probe_event *dst,
+			   struct perf_probe_event *src);
+
 /* Check the perf_probe_event needs debuginfo */
 bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
 
#9 lore raw

PATCH v10 5/23 perf probe: Recover and export synthesize_perf_probe_point()

In reply to Masami Hiramatsu

Recover and export synthesize_perf_probe_point() which had once introduced but disabled long time. This renew the code and re-enable it.

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/probe-event.c |   39 ++++++++++++++++++++++-----------------
 tools/perf/util/probe-event.h |    1 +
 2 files changed, 23 insertions(+), 17 deletions(-)
tools/perf/util/probe-event.c +22 -17
index 84f4b2b3..cbc8a8b 100644
@@ -67,7 +67,6 @@ int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 static struct machine *host_machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -1716,7 +1715,7 @@ out:
 }
 
 /* Compose only probe point (not argument) */
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+char *synthesize_perf_probe_point(struct perf_probe_point *pp)
 {
 	struct strbuf buf;
 	char *tmp, *ret = NULL;
@@ -1755,30 +1754,36 @@ out:
 	return ret;
 }
 
-#if 0
 char *synthesize_perf_probe_command(struct perf_probe_event *pev)
 {
-	char *buf;
-	int i, len, ret;
+	struct strbuf buf;
+	char *tmp, *ret = NULL;
+	int i;
 
-	buf = synthesize_perf_probe_point(&pev->point);
-	if (!buf)
+	if (strbuf_init(&buf, 64))
 		return NULL;
+	if (pev->event)
+		if (strbuf_addf(&buf, "%s:%s=", pev->group ?: PERFPROBE_GROUP,
+				pev->event) < 0)
+			goto out;
+
+	tmp = synthesize_perf_probe_point(&pev->point);
+	if (!tmp || strbuf_addstr(&buf, tmp) < 0)
+		goto out;
+	free(tmp);
 
-	len = strlen(buf);
 	for (i = 0; i < pev->nargs; i++) {
-		ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
-				 pev->args[i].name);
-		if (ret <= 0) {
-			free(buf);
-			return NULL;
-		}
-		len += ret;
+		tmp = synthesize_perf_probe_arg(pev->args + i);
+		if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0)
+			goto out;
+		free(tmp);
 	}
 
-	return buf;
+	ret = strbuf_detach(&buf, NULL);
+out:
+	strbuf_release(&buf);
+	return ret;
 }
-#endif
 
 static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
 					    struct strbuf *buf, int depth)
tools/perf/util/probe-event.h +1 -0
index 367f886..0b024ba 100644
@@ -121,6 +121,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev);
 char *synthesize_perf_probe_command(struct perf_probe_event *pev);
 char *synthesize_probe_trace_command(struct probe_trace_event *tev);
 char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
+char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 
 int perf_probe_event__copy(struct perf_probe_event *dst,
 			   struct perf_probe_event *src);
#10 lore raw

[tip:perf/core] perf probe: Uncomment and export synthesize_perf_probe_point()

perf probe: Uncomment and export synthesize_perf_probe_point()
Commit-ID:  c4ff49209bcdc1ef709773f4833a341ac49a26cc
Gitweb:     http://git.kernel.org/tip/c4ff49209bcdc1ef709773f4833a341ac49a26cc
Author:     Masami Hiramatsu <[email protected]>
AuthorDate: Wed, 8 Jun 2016 18:29:50 +0900
Committer:  Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Tue, 14 Jun 2016 09:29:54 -0300

perf probe: Uncomment and export synthesize_perf_probe_point()

Uncomment and export synthesize_perf_probe_point() which had once introduced but has been disabled for a long time. This renews the code and re-enable it.

---
 tools/perf/util/probe-event.c | 39 ++++++++++++++++++++++-----------------
 tools/perf/util/probe-event.h |  1 +
 2 files changed, 23 insertions(+), 17 deletions(-)
tools/perf/util/probe-event.c +22 -17
index 84f4b2b3..cbc8a8b 100644
@@ -67,7 +67,6 @@ int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 static struct machine *host_machine;
 
 /* Initialize symbol maps and path of vmlinux/modules */
@@ -1716,7 +1715,7 @@ out:
 }
 
 /* Compose only probe point (not argument) */
-static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
+char *synthesize_perf_probe_point(struct perf_probe_point *pp)
 {
 	struct strbuf buf;
 	char *tmp, *ret = NULL;
@@ -1755,30 +1754,36 @@ out:
 	return ret;
 }
 
-#if 0
 char *synthesize_perf_probe_command(struct perf_probe_event *pev)
 {
-	char *buf;
-	int i, len, ret;
+	struct strbuf buf;
+	char *tmp, *ret = NULL;
+	int i;
 
-	buf = synthesize_perf_probe_point(&pev->point);
-	if (!buf)
+	if (strbuf_init(&buf, 64))
 		return NULL;
+	if (pev->event)
+		if (strbuf_addf(&buf, "%s:%s=", pev->group ?: PERFPROBE_GROUP,
+				pev->event) < 0)
+			goto out;
+
+	tmp = synthesize_perf_probe_point(&pev->point);
+	if (!tmp || strbuf_addstr(&buf, tmp) < 0)
+		goto out;
+	free(tmp);
 
-	len = strlen(buf);
 	for (i = 0; i < pev->nargs; i++) {
-		ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
-				 pev->args[i].name);
-		if (ret <= 0) {
-			free(buf);
-			return NULL;
-		}
-		len += ret;
+		tmp = synthesize_perf_probe_arg(pev->args + i);
+		if (!tmp || strbuf_addf(&buf, " %s", tmp) < 0)
+			goto out;
+		free(tmp);
 	}
 
-	return buf;
+	ret = strbuf_detach(&buf, NULL);
+out:
+	strbuf_release(&buf);
+	return ret;
 }
-#endif
 
 static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
 					    struct strbuf *buf, int depth)
tools/perf/util/probe-event.h +1 -0
index 367f886..0b024ba 100644
@@ -121,6 +121,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev);
 char *synthesize_perf_probe_command(struct perf_probe_event *pev);
 char *synthesize_probe_trace_command(struct probe_trace_event *tev);
 char *synthesize_perf_probe_arg(struct perf_probe_arg *pa);
+char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 
 int perf_probe_event__copy(struct perf_probe_event *dst,
 			   struct perf_probe_event *src);
#11 lore raw

PATCH v10 6/23 perf probe-file: Introduce perf_cache interfaces

In reply to Masami Hiramatsu

Introduce perf_cache object and interfaces to create, add entry, commit, and delete the object. perf_cache represents a file for the cached perf-probe definitions on one binary file or vmlinux which has its own build id. The probe cache file is located under the build-id cache directory of the target binary, as below;

<perf-debug-dir>/.build-id/<BU>/<ILDID>/probe

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/probe-file.c |  307 ++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-file.h |   19 +++
 2 files changed, 326 insertions(+)
tools/perf/util/probe-file.c +307 -0
index 3fe6214..689d874 100644
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <sys/uio.h>
 #include "util.h"
 #include "event.h"
 #include "strlist.h"
@@ -324,3 +325,309 @@ int probe_file__del_events(int fd, struct strfilter *filter)
 
 	return ret;
 }
+
+static void probe_cache_entry__delete(struct probe_cache_entry *node)
+{
+	if (!list_empty(&node->list))
+		list_del(&node->list);
+	if (node->tevlist)
+		strlist__delete(node->tevlist);
+	clear_perf_probe_event(&node->pev);
+	free(node->spev);
+	free(node);
+}
+
+static struct probe_cache_entry *
+probe_cache_entry__new(struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->tevlist = strlist__new(NULL, NULL);
+		if (!ret->tevlist)
+			zfree(&ret);
+		if (ret && pev) {
+			ret->spev = synthesize_perf_probe_command(pev);
+			if (!ret->spev ||
+			    perf_probe_event__copy(&ret->pev, pev) < 0) {
+				probe_cache_entry__delete(ret);
+				return NULL;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* For the kernel probe caches, pass target = NULL */
+static int probe_cache__open(struct probe_cache *pcache, const char *target)
+{
+	char cpath[PATH_MAX];
+	char sbuildid[SBUILD_ID_SIZE];
+	char *dir_name;
+	bool is_kallsyms = !target;
+	int ret, fd;
+
+	if (target)
+		ret = filename__sprintf_build_id(target, sbuildid);
+	else {
+		target = DSO__NAME_KALLSYMS;
+		ret = sysfs__sprintf_build_id("/", sbuildid);
+	}
+	if (ret < 0) {
+		pr_debug("Failed to get build-id from %s.\n", target);
+		return ret;
+	}
+
+	/* If we have no buildid cache, make it */
+	if (!build_id_cache__cached(sbuildid)) {
+		ret = build_id_cache__add_s(sbuildid, target,
+					    is_kallsyms, NULL);
+		if (ret < 0) {
+			pr_debug("Failed to add build-id cache: %s\n", target);
+			return ret;
+		}
+	}
+
+	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+					    false);
+	if (!dir_name)
+		return -ENOMEM;
+
+	snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
+	fd = open(cpath, O_CREAT | O_RDWR, 0644);
+	if (fd < 0)
+		pr_debug("Failed to open cache(%d): %s\n", fd, cpath);
+	free(dir_name);
+	pcache->fd = fd;
+
+	return fd;
+}
+
+static int probe_cache__load(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry = NULL;
+	char buf[MAX_CMDLEN], *p;
+	int ret = 0;
+	FILE *fp;
+
+	fp = fdopen(dup(pcache->fd), "r");
+	while (!feof(fp)) {
+		if (!fgets(buf, MAX_CMDLEN, fp))
+			break;
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+		if (buf[0] == '#') {	/* #perf_probe_event */
+			entry = probe_cache_entry__new(NULL);
+			if (!entry) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			entry->spev = strdup(buf + 1);
+			if (entry->spev)
+				ret = parse_perf_probe_command(buf + 1,
+								&entry->pev);
+			else
+				ret = -ENOMEM;
+			if (ret < 0) {
+				probe_cache_entry__delete(entry);
+				goto out;
+			}
+			list_add_tail(&entry->list, &pcache->list);
+		} else {	/* trace_probe_event */
+			if (!entry) {
+				ret = -EINVAL;
+				goto out;
+			}
+			strlist__add(entry->tevlist, buf);
+		}
+	}
+out:
+	fclose(fp);
+	return ret;
+}
+
+static struct probe_cache *probe_cache__alloc(void)
+{
+	struct probe_cache *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->fd = -EINVAL;
+	}
+	return ret;
+}
+
+void probe_cache__delete(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+
+	if (!pcache)
+		return;
+
+	while (!list_empty(&pcache->list)) {
+		entry = list_first_entry(&pcache->list, typeof(*entry), list);
+		probe_cache_entry__delete(entry);
+	}
+	if (pcache->fd > 0)
+		close(pcache->fd);
+	free(pcache);
+}
+
+struct probe_cache *probe_cache__new(const char *target)
+{
+	struct probe_cache *pcache = probe_cache__alloc();
+	int ret;
+
+	if (!pcache)
+		return NULL;
+
+	ret = probe_cache__open(pcache, target);
+	if (ret < 0) {
+		pr_debug("Cache open error: %d\n", ret);
+		goto out_err;
+	}
+
+	ret = probe_cache__load(pcache);
+	if (ret < 0) {
+		pr_debug("Cache read error: %d\n", ret);
+		goto out_err;
+	}
+
+	return pcache;
+
+out_err:
+	probe_cache__delete(pcache);
+	return NULL;
+}
+
+static bool streql(const char *a, const char *b)
+{
+	if (a == b)
+		return true;
+
+	if (!a || !b)
+		return false;
+
+	return !strcmp(a, b);
+}
+
+static struct probe_cache_entry *
+probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *cmd = NULL;
+
+	cmd = synthesize_perf_probe_command(pev);
+	if (!cmd)
+		return NULL;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		/* Hit if same event name or same command-string */
+		if ((pev->event &&
+		     (streql(entry->pev.group, pev->group) &&
+		      streql(entry->pev.event, pev->event))) ||
+		    (!strcmp(entry->spev, cmd)))
+			goto found;
+	}
+	entry = NULL;
+
+found:
+	free(cmd);
+	return entry;
+}
+
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *command;
+	int i, ret = 0;
+
+	if (!pcache || !pev || !tevs || ntevs <= 0) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Remove old cache entry */
+	entry = probe_cache__find(pcache, pev);
+	if (entry)
+		probe_cache_entry__delete(entry);
+
+	ret = -ENOMEM;
+	entry = probe_cache_entry__new(pev);
+	if (!entry)
+		goto out_err;
+
+	for (i = 0; i < ntevs; i++) {
+		if (!tevs[i].point.symbol)
+			continue;
+
+		command = synthesize_probe_trace_command(&tevs[i]);
+		if (!command)
+			goto out_err;
+		strlist__add(entry->tevlist, command);
+		free(command);
+	}
+	list_add_tail(&entry->list, &pcache->list);
+	pr_debug("Added probe cache: %d\n", ntevs);
+	return 0;
+
+out_err:
+	pr_debug("Failed to add probe caches\n");
+	if (entry)
+		probe_cache_entry__delete(entry);
+	return ret;
+}
+
+static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
+{
+	struct str_node *snode;
+	struct iovec iov[3];
+	int ret;
+
+	pr_debug("Writing cache: #%s\n", entry->spev);
+	iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
+	iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
+	iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
+	ret = writev(fd, iov, 3);
+	if (ret < 0)
+		return ret;
+
+	strlist__for_each(snode, entry->tevlist) {
+		iov[0].iov_base = (void *)snode->s;
+		iov[0].iov_len = strlen(snode->s);
+		iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1;
+		ret = writev(fd, iov, 2);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+int probe_cache__commit(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+	int ret = 0;
+
+	/* TBD: if we do not update existing entries, skip it */
+	ret = lseek(pcache->fd, 0, SEEK_SET);
+	if (ret < 0)
+		goto out;
+
+	ret = ftruncate(pcache->fd, 0);
+	if (ret < 0)
+		goto out;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		ret = probe_cache_entry__write(entry, pcache->fd);
+		pr_debug("Cache committed: %d\n", ret);
+		if (ret < 0)
+			break;
+	}
+out:
+	return ret;
+}
tools/perf/util/probe-file.h +19 -0
index 18ac9cf..d2b8791d 100644
@@ -5,6 +5,19 @@
 #include "strfilter.h"
 #include "probe-event.h"
 
+/* Cache of probe definitions */
+struct probe_cache_entry {
+	struct list_head	list;
+	struct perf_probe_event pev;
+	char			*spev;
+	struct strlist		*tevlist;
+};
+
+struct probe_cache {
+	int	fd;
+	struct list_head list;
+};
+
 #define PF_FL_UPROBE	1
 #define PF_FL_RW	2
 
@@ -18,5 +31,11 @@ int probe_file__get_events(int fd, struct strfilter *filter,
 				  struct strlist *plist);
 int probe_file__del_strlist(int fd, struct strlist *namelist);
 
+struct probe_cache *probe_cache__new(const char *target);
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs);
+int probe_cache__commit(struct probe_cache *pcache);
+void probe_cache__delete(struct probe_cache *pcache);
 
 #endif
#12 lore raw

Re: [PATCH perf/core v10 06/23] perf probe-file: Introduce perf_cache interfaces

Em Wed, Jun 08, 2016 at 06:29:59PM +0900, Masami Hiramatsu escreveu:

Quoted 31 lines

Introduce perf_cache object and interfaces to create, add entry, commit, and delete the object. perf_cache represents a file for the cached perf-probe definitions on one binary file or vmlinux which has its own build id. The probe cache file is located under the build-id cache directory of the target binary, as below;

<perf-debug-dir>/.build-id/<BU>/<ILDID>/probe

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/probe-file.c |  307 ++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-file.h |   19 +++
 2 files changed, 326 insertions(+)
tools/perf/util/probe-file.c +1 -0
index 3fe6214..689d874 100644
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <sys/uio.h>
 #include "util.h"
 #include "event.h"
 #include "strlist.h"

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 332

Quoted 8 lines
tools/perf/util/probe-file.c +5 -0
@@ -324,3 +325,309 @@ int probe_file__del_events(int fd, struct strfilter *filter)
 
 	return ret;
 }
+
+static void probe_cache_entry__delete(struct probe_cache_entry *node)
+{
+	if (!list_empty(&node->list))
+		list_del(&node->list);

Humm, shouldn't this be something like:

BUG_ON(!list_empty(&node->list)

?

I.e. whoever inserted this on a list should take care of removing it before deleting it, taking locks, whatever is needed to keep the integrity of such list.

You may not be using this stuff in a multithreaded app now, but lets not make it difficult to :-)

I noticed why you do it this way and have a suggestion to use a more usual model, see below.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 334

Quoted 2 lines
tools/perf/util/probe-file.c +2 -0
+	if (node->tevlist)
+		strlist__delete(node->tevlist);

No checking, destructors generally follows the free() model, i.e. they eat NULL for breakfast. Lemme see if strlist does that... Yes, they do.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 336

Quoted 2 lines
tools/perf/util/probe-file.c +2 -0
+	clear_perf_probe_event(&node->pev);
+	free(node->spev);

Here you may want to use:

	zfree(&node->spev);

To free and set node->spev to NULL, to help in debugging when this node may be still referenced even having being deleted.

Quoted 69 lines
tools/perf/util/probe-file.c +69 -0
+	free(node);
+}
+
+static struct probe_cache_entry *
+probe_cache_entry__new(struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->tevlist = strlist__new(NULL, NULL);
+		if (!ret->tevlist)
+			zfree(&ret);
+		if (ret && pev) {
+			ret->spev = synthesize_perf_probe_command(pev);
+			if (!ret->spev ||
+			    perf_probe_event__copy(&ret->pev, pev) < 0) {
+				probe_cache_entry__delete(ret);
+				return NULL;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* For the kernel probe caches, pass target = NULL */
+static int probe_cache__open(struct probe_cache *pcache, const char *target)
+{
+	char cpath[PATH_MAX];
+	char sbuildid[SBUILD_ID_SIZE];
+	char *dir_name;
+	bool is_kallsyms = !target;
+	int ret, fd;
+
+	if (target)
+		ret = filename__sprintf_build_id(target, sbuildid);
+	else {
+		target = DSO__NAME_KALLSYMS;
+		ret = sysfs__sprintf_build_id("/", sbuildid);
+	}
+	if (ret < 0) {
+		pr_debug("Failed to get build-id from %s.\n", target);
+		return ret;
+	}
+
+	/* If we have no buildid cache, make it */
+	if (!build_id_cache__cached(sbuildid)) {
+		ret = build_id_cache__add_s(sbuildid, target,
+					    is_kallsyms, NULL);
+		if (ret < 0) {
+			pr_debug("Failed to add build-id cache: %s\n", target);
+			return ret;
+		}
+	}
+
+	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+					    false);
+	if (!dir_name)
+		return -ENOMEM;
+
+	snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
+	fd = open(cpath, O_CREAT | O_RDWR, 0644);
+	if (fd < 0)
+		pr_debug("Failed to open cache(%d): %s\n", fd, cpath);
+	free(dir_name);
+	pcache->fd = fd;
+
+	return fd;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 415

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+}
+
+static int probe_cache__load(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry = NULL;
+	char buf[MAX_CMDLEN], *p;
+	int ret = 0;
+	FILE *fp;
+
+	fp = fdopen(dup(pcache->fd), "r");

fdopen may return NULL, a check is needed.

Quoted 43 lines
tools/perf/util/probe-file.c +43 -0
+	while (!feof(fp)) {
+		if (!fgets(buf, MAX_CMDLEN, fp))
+			break;
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+		if (buf[0] == '#') {	/* #perf_probe_event */
+			entry = probe_cache_entry__new(NULL);
+			if (!entry) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			entry->spev = strdup(buf + 1);
+			if (entry->spev)
+				ret = parse_perf_probe_command(buf + 1,
+								&entry->pev);
+			else
+				ret = -ENOMEM;
+			if (ret < 0) {
+				probe_cache_entry__delete(entry);
+				goto out;
+			}
+			list_add_tail(&entry->list, &pcache->list);
+		} else {	/* trace_probe_event */
+			if (!entry) {
+				ret = -EINVAL;
+				goto out;
+			}
+			strlist__add(entry->tevlist, buf);
+		}
+	}
+out:
+	fclose(fp);
+	return ret;
+}
+
+static struct probe_cache *probe_cache__alloc(void)
+{
+	struct probe_cache *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->fd = -EINVAL;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 468

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	}
+	return ret;
+}
+
+void probe_cache__delete(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+
+	if (!pcache)
+		return;

see, a good destructor, accepts NULL, does nothing with it.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 473

Quoted 5 lines
tools/perf/util/probe-file.c +5 -0
+
+	while (!list_empty(&pcache->list)) {
+		entry = list_first_entry(&pcache->list, typeof(*entry), list);
+		probe_cache_entry__delete(entry);
+	}

the above while is the definition of a "purge()" operation, that may be useful outside of a delete operation, please consider adding it, like:

void probe_cache__purge(struct probe_cache *pcache)

{
	struct probe_cache_entry *entry, *n;
	list_for_each_entry_safe(entry, n, &pcache->list, node)
		probe_cache_entry__delete(entry);
}

And please use 'list' for lists and 'node' for entries in a list, i.e. you have, at the end of this patch:

Quoted 2 lines
struct probe_cache_entry {
	struct list_head	list;

This one should be 'node', not list, this way we know that this is an entry in a list, not a list of some other structs.

Quoted 8 lines
	struct perf_probe_event pev;
	char			*spev;
	struct strlist		*tevlist;
};
struct probe_cache {
	int	fd;
	struct list_head list;

This one is ok, but then if you rename it from the generic name 'list' to something more informative, for instance 'cache_entries', I think it will help people reading your code to grasp what it is doing more quickly.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 482

Quoted 11 lines

};

tools/perf/util/probe-file.c +9 -0
+	if (pcache->fd > 0)
+		close(pcache->fd);
+	free(pcache);
+}
+
+struct probe_cache *probe_cache__new(const char *target)
+{
+	struct probe_cache *pcache = probe_cache__alloc();
+	int ret;

This is odd, what you call "probe_cache__alloc() looks like what a "new()" does, if you think that the constructor for 'probe_cache' has to always open and load it, then why not just do the zalloc() here and then call probe_cache__init() on it?

Quoted 31 lines
tools/perf/util/probe-file.c +31 -0
+
+	if (!pcache)
+		return NULL;
+
+	ret = probe_cache__open(pcache, target);
+	if (ret < 0) {
+		pr_debug("Cache open error: %d\n", ret);
+		goto out_err;
+	}
+
+	ret = probe_cache__load(pcache);
+	if (ret < 0) {
+		pr_debug("Cache read error: %d\n", ret);
+		goto out_err;
+	}
+
+	return pcache;
+
+out_err:
+	probe_cache__delete(pcache);
+	return NULL;
+}
+
+static bool streql(const char *a, const char *b)
+{
+	if (a == b)
+		return true;
+
+	if (!a || !b)
+		return false;
+

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 523

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	return !strcmp(a, b);
+}
+
+static struct probe_cache_entry *
+probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *cmd = NULL;
+
+	cmd = synthesize_perf_probe_command(pev);

Why init it to NULL only to immediately init it again to something else? Perhaps:

	char *cmd = synthesize_perf_probe_command(pev);

instead?

Quoted 25 lines
tools/perf/util/probe-file.c +25 -0
+	if (!cmd)
+		return NULL;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		/* Hit if same event name or same command-string */
+		if ((pev->event &&
+		     (streql(entry->pev.group, pev->group) &&
+		      streql(entry->pev.event, pev->event))) ||
+		    (!strcmp(entry->spev, cmd)))
+			goto found;
+	}
+	entry = NULL;
+
+found:
+	free(cmd);
+	return entry;
+}
+
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *command;
+	int i, ret = 0;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 558

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+
+	if (!pcache || !pev || !tevs || ntevs <= 0) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Remove old cache entry */
+	entry = probe_cache__find(pcache, pev);
+	if (entry)
+		probe_cache_entry__delete(entry);

Here you could be more compact with:

	probe_cache_entry__delete(probe_cache__find(pcache, pev));

Because delete() functions accept NULL?

Quoted 14 lines
tools/perf/util/probe-file.c +14 -0
+
+	ret = -ENOMEM;
+	entry = probe_cache_entry__new(pev);
+	if (!entry)
+		goto out_err;
+
+	for (i = 0; i < ntevs; i++) {
+		if (!tevs[i].point.symbol)
+			continue;
+
+		command = synthesize_probe_trace_command(&tevs[i]);
+		if (!command)
+			goto out_err;
+		strlist__add(entry->tevlist, command);

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 582

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+		free(command);
+	}
+	list_add_tail(&entry->list, &pcache->list);
+	pr_debug("Added probe cache: %d\n", ntevs);
+	return 0;
+
+out_err:
+	pr_debug("Failed to add probe caches\n");
+	if (entry)
+		probe_cache_entry__delete(entry);

No need to check for NULL, call the destructor directly.

Quoted 6 lines
tools/perf/util/probe-file.c +6 -0
+	return ret;
+}
+
+static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
+{
+	struct str_node *snode;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 598

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	struct iovec iov[3];
+	int ret;
+
+	pr_debug("Writing cache: #%s\n", entry->spev);
+	iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
+	iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
+	iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
+	ret = writev(fd, iov, 3);
+	if (ret < 0)
+		return ret;

Shouldn't we check short writes? writev() returns the number of bytes written, isn't it possible to return less than what you asked for?

Quoted 71 lines
tools/perf/util/probe-file.c +35 -0
+
+	strlist__for_each(snode, entry->tevlist) {
+		iov[0].iov_base = (void *)snode->s;
+		iov[0].iov_len = strlen(snode->s);
+		iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1;
+		ret = writev(fd, iov, 2);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+int probe_cache__commit(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+	int ret = 0;
+
+	/* TBD: if we do not update existing entries, skip it */
+	ret = lseek(pcache->fd, 0, SEEK_SET);
+	if (ret < 0)
+		goto out;
+
+	ret = ftruncate(pcache->fd, 0);
+	if (ret < 0)
+		goto out;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		ret = probe_cache_entry__write(entry, pcache->fd);
+		pr_debug("Cache committed: %d\n", ret);
+		if (ret < 0)
+			break;
+	}
+out:
+	return ret;
+}
tools/perf/util/probe-file.h +19 -0
index 18ac9cf..d2b8791d 100644
@@ -5,6 +5,19 @@
 #include "strfilter.h"
 #include "probe-event.h"
 
+/* Cache of probe definitions */
+struct probe_cache_entry {
+	struct list_head	list;
+	struct perf_probe_event pev;
+	char			*spev;
+	struct strlist		*tevlist;
+};
+
+struct probe_cache {
+	int	fd;
+	struct list_head list;
+};
+
 #define PF_FL_UPROBE	1
 #define PF_FL_RW	2
 
@@ -18,5 +31,11 @@ int probe_file__get_events(int fd, struct strfilter *filter,
 				  struct strlist *plist);
 int probe_file__del_strlist(int fd, struct strlist *namelist);
 
+struct probe_cache *probe_cache__new(const char *target);
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs);
+int probe_cache__commit(struct probe_cache *pcache);
+void probe_cache__delete(struct probe_cache *pcache);
 
 #endif
#13 lore raw

Re: [PATCH perf/core v10 06/23] perf probe-file: Introduce perf_cache interfaces

Quoted 43 lines · On Thu, 9 Jun 2016 11:16:31 -0300 Arnaldo Carvalho de Melo <[email protected]>…

Em Wed, Jun 08, 2016 at 06:29:59PM +0900, Masami Hiramatsu escreveu:

Quoted 31 lines

Introduce perf_cache object and interfaces to create, add entry, commit, and delete the object. perf_cache represents a file for the cached perf-probe definitions on one binary file or vmlinux which has its own build id. The probe cache file is located under the build-id cache directory of the target binary, as below;

<perf-debug-dir>/.build-id/<BU>/<ILDID>/probe

---
Changes in v10:
- Splited from "Add --cache option to cache the probe definitions"

---
 tools/perf/util/probe-file.c |  307 ++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-file.h |   19 +++
 2 files changed, 326 insertions(+)
tools/perf/util/probe-file.c +1 -0
index 3fe6214..689d874 100644
@@ -14,6 +14,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <sys/uio.h>
 #include "util.h"
 #include "event.h"
 #include "strlist.h"

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 332

Quoted 8 lines
tools/perf/util/probe-file.c +5 -0
@@ -324,3 +325,309 @@ int probe_file__del_events(int fd, struct strfilter *filter)
 
 	return ret;
 }
+
+static void probe_cache_entry__delete(struct probe_cache_entry *node)
+{
+	if (!list_empty(&node->list))
+		list_del(&node->list);

Humm, shouldn't this be something like:

Quoted 3 lines

BUG_ON(!list_empty(&node->list)

?

Would you mean assert? (it seems similart to die()...)

Quoted 4 lines

I.e. whoever inserted this on a list should take care of removing it before deleting it, taking locks, whatever is needed to keep the integrity of such list.

Ah, I see.

Quoted 4 lines

You may not be using this stuff in a multithreaded app now, but lets not make it difficult to :-)

Quoted 8 lines

I noticed why you do it this way and have a suggestion to use a more usual model, see below.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 334

Quoted 2 lines
tools/perf/util/probe-file.c +2 -0
+	if (node->tevlist)
+		strlist__delete(node->tevlist);

No checking, destructors generally follows the free() model, i.e. they eat NULL for breakfast. Lemme see if strlist does that... Yes, they do.

For the strlist, I should clear the pointer. OK.

Quoted 10 lines

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 336

Quoted 3 lines
tools/perf/util/probe-file.c +2 -0
+	clear_perf_probe_event(&node->pev);
+	free(node->spev);

Here you may want to use:

	zfree(&node->spev);

To free and set node->spev to NULL, to help in debugging when this node may be still referenced even having being deleted.

OK, I'll clear that.

Quoted 74 lines
Quoted 74 lines
tools/perf/util/probe-file.c +73 -0
+	free(node);
+}
+
+static struct probe_cache_entry *
+probe_cache_entry__new(struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->tevlist = strlist__new(NULL, NULL);
+		if (!ret->tevlist)
+			zfree(&ret);
+		if (ret && pev) {
+			ret->spev = synthesize_perf_probe_command(pev);
+			if (!ret->spev ||
+			    perf_probe_event__copy(&ret->pev, pev) < 0) {
+				probe_cache_entry__delete(ret);
+				return NULL;
+			}
+		}
+	}
+
+	return ret;
+}
+
+/* For the kernel probe caches, pass target = NULL */
+static int probe_cache__open(struct probe_cache *pcache, const char *target)
+{
+	char cpath[PATH_MAX];
+	char sbuildid[SBUILD_ID_SIZE];
+	char *dir_name;
+	bool is_kallsyms = !target;
+	int ret, fd;
+
+	if (target)
+		ret = filename__sprintf_build_id(target, sbuildid);
+	else {
+		target = DSO__NAME_KALLSYMS;
+		ret = sysfs__sprintf_build_id("/", sbuildid);
+	}
+	if (ret < 0) {
+		pr_debug("Failed to get build-id from %s.\n", target);
+		return ret;
+	}
+
+	/* If we have no buildid cache, make it */
+	if (!build_id_cache__cached(sbuildid)) {
+		ret = build_id_cache__add_s(sbuildid, target,
+					    is_kallsyms, NULL);
+		if (ret < 0) {
+			pr_debug("Failed to add build-id cache: %s\n", target);
+			return ret;
+		}
+	}
+
+	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+					    false);
+	if (!dir_name)
+		return -ENOMEM;
+
+	snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
+	fd = open(cpath, O_CREAT | O_RDWR, 0644);
+	if (fd < 0)
+		pr_debug("Failed to open cache(%d): %s\n", fd, cpath);
+	free(dir_name);
+	pcache->fd = fd;
+
+	return fd;
+}
+
+static int probe_cache__load(struct probe_cache *pcache)
+{
Quoted 8 lines

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 415

Quoted 6 lines
tools/perf/util/probe-file.c +6 -0
+	struct probe_cache_entry *entry = NULL;
+	char buf[MAX_CMDLEN], *p;
+	int ret = 0;
+	FILE *fp;
+
+	fp = fdopen(dup(pcache->fd), "r");

fdopen may return NULL, a check is needed.

Oops, right. I'll add a check.

Quoted 66 lines
Quoted 44 lines
tools/perf/util/probe-file.c +43 -0
+	while (!feof(fp)) {
+		if (!fgets(buf, MAX_CMDLEN, fp))
+			break;
+		p = strchr(buf, '\n');
+		if (p)
+			*p = '\0';
+		if (buf[0] == '#') {	/* #perf_probe_event */
+			entry = probe_cache_entry__new(NULL);
+			if (!entry) {
+				ret = -ENOMEM;
+				goto out;
+			}
+			entry->spev = strdup(buf + 1);
+			if (entry->spev)
+				ret = parse_perf_probe_command(buf + 1,
+								&entry->pev);
+			else
+				ret = -ENOMEM;
+			if (ret < 0) {
+				probe_cache_entry__delete(entry);
+				goto out;
+			}
+			list_add_tail(&entry->list, &pcache->list);
+		} else {	/* trace_probe_event */
+			if (!entry) {
+				ret = -EINVAL;
+				goto out;
+			}
+			strlist__add(entry->tevlist, buf);
+		}
+	}
+out:
+	fclose(fp);
+	return ret;
+}
+
+static struct probe_cache *probe_cache__alloc(void)
+{
+	struct probe_cache *ret = zalloc(sizeof(*ret));
+
+	if (ret) {
+		INIT_LIST_HEAD(&ret->list);
+		ret->fd = -EINVAL;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 468

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	}
+	return ret;
+}
+
+void probe_cache__delete(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+
+	if (!pcache)
+		return;

see, a good destructor, accepts NULL, does nothing with it.

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 473

Quoted 5 lines
tools/perf/util/probe-file.c +5 -0
+
+	while (!list_empty(&pcache->list)) {
+		entry = list_first_entry(&pcache->list, typeof(*entry), list);
+		probe_cache_entry__delete(entry);
+	}

the above while is the definition of a "purge()" operation, that may be useful outside of a delete operation, please consider adding it, like:

Quoted 6 lines

void probe_cache__purge(struct probe_cache *pcache)

{
	struct probe_cache_entry *entry, *n;
	list_for_each_entry_safe(entry, n, &pcache->list, node)
		probe_cache_entry__delete(entry);
}

Hm, I see.

Quoted 9 lines

And please use 'list' for lists and 'node' for entries in a list, i.e. you have, at the end of this patch:

Quoted 2 lines
struct probe_cache_entry {
	struct list_head	list;

This one should be 'node', not list, this way we know that this is an entry in a list, not a list of some other structs.

OK.

Quoted 6 lines
Quoted 5 lines
	struct perf_probe_event pev;
	char			*spev;
	struct strlist		*tevlist;
};
Quoted 8 lines
Quoted 3 lines
struct probe_cache {
	int	fd;
	struct list_head list;

This one is ok, but then if you rename it from the generic name 'list' to something more informative, for instance 'cache_entries', I think it will help people reading your code to grasp what it is doing more quickly.

OK.

Quoted 17 lines

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 482

Quoted 12 lines

};

tools/perf/util/probe-file.c +9 -0
+	if (pcache->fd > 0)
+		close(pcache->fd);
+	free(pcache);
+}
+
+struct probe_cache *probe_cache__new(const char *target)
+{
+	struct probe_cache *pcache = probe_cache__alloc();
+	int ret;

This is odd, what you call "probe_cache__alloc() looks like what a "new()" does, if you think that the constructor for 'probe_cache' has to always open and load it, then why not just do the zalloc() here and then call probe_cache__init() on it?

I just wanted to keep probe_cache always initialized. But yeah, add probe_cache__init() is OK for me too.

Quoted 45 lines
Quoted 31 lines
tools/perf/util/probe-file.c +31 -0
+
+	if (!pcache)
+		return NULL;
+
+	ret = probe_cache__open(pcache, target);
+	if (ret < 0) {
+		pr_debug("Cache open error: %d\n", ret);
+		goto out_err;
+	}
+
+	ret = probe_cache__load(pcache);
+	if (ret < 0) {
+		pr_debug("Cache read error: %d\n", ret);
+		goto out_err;
+	}
+
+	return pcache;
+
+out_err:
+	probe_cache__delete(pcache);
+	return NULL;
+}
+
+static bool streql(const char *a, const char *b)
+{
+	if (a == b)
+		return true;
+
+	if (!a || !b)
+		return false;
+

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 523

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	return !strcmp(a, b);
+}
+
+static struct probe_cache_entry *
+probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *cmd = NULL;
+
+	cmd = synthesize_perf_probe_command(pev);

Why init it to NULL only to immediately init it again to something else? Perhaps:

Quoted 3 lines
	char *cmd = synthesize_perf_probe_command(pev);

instead?

OK, I'll do.

Quoted 39 lines
Quoted 26 lines
tools/perf/util/probe-file.c +25 -0
+	if (!cmd)
+		return NULL;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		/* Hit if same event name or same command-string */
+		if ((pev->event &&
+		     (streql(entry->pev.group, pev->group) &&
+		      streql(entry->pev.event, pev->event))) ||
+		    (!strcmp(entry->spev, cmd)))
+			goto found;
+	}
+	entry = NULL;
+
+found:
+	free(cmd);
+	return entry;
+}
+
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs)
+{
+	struct probe_cache_entry *entry = NULL;
+	char *command;
+	int i, ret = 0;

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 558

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+
+	if (!pcache || !pev || !tevs || ntevs <= 0) {
+		ret = -EINVAL;
+		goto out_err;
+	}
+
+	/* Remove old cache entry */
+	entry = probe_cache__find(pcache, pev);
+	if (entry)
+		probe_cache_entry__delete(entry);

Here you could be more compact with:

Quoted 3 lines
	probe_cache_entry__delete(probe_cache__find(pcache, pev));

Because delete() functions accept NULL?

Ah, right! maybe I forgot that :(

Quoted 19 lines
Quoted 19 lines
tools/perf/util/probe-file.c +18 -0
+
+	ret = -ENOMEM;
+	entry = probe_cache_entry__new(pev);
+	if (!entry)
+		goto out_err;
+
+	for (i = 0; i < ntevs; i++) {
+		if (!tevs[i].point.symbol)
+			continue;
+
+		command = synthesize_probe_trace_command(&tevs[i]);
+		if (!command)
+			goto out_err;
+		strlist__add(entry->tevlist, command);
+		free(command);
+	}
+	list_add_tail(&entry->list, &pcache->list);
+	pr_debug("Added probe cache: %d\n", ntevs);
Quoted 8 lines

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 582

Quoted 6 lines
tools/perf/util/probe-file.c +6 -0
+	return 0;
+
+out_err:
+	pr_debug("Failed to add probe caches\n");
+	if (entry)
+		probe_cache_entry__delete(entry);

No need to check for NULL, call the destructor directly.

OK.

Quoted 12 lines
Quoted 12 lines
tools/perf/util/probe-file.c +11 -0
+	return ret;
+}
+
+static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
+{
+	struct str_node *snode;
+	struct iovec iov[3];
+	int ret;
+
+	pr_debug("Writing cache: #%s\n", entry->spev);
+	iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
Quoted 8 lines

tools/perf/util/probe-file.c int probe_file__del_events(int fd, struct strfilter *filter) · line 598

Quoted 5 lines
tools/perf/util/probe-file.c +5 -0
+	iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
+	iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
+	ret = writev(fd, iov, 3);
+	if (ret < 0)
+		return ret;

Shouldn't we check short writes? writev() returns the number of bytes written, isn't it possible to return less than what you asked for?

Yeah, I found this in write(2):

If a write() is interrupted by a signal handler before any bytes are written, then the call fails with the error EINTR; if it is interrupted after at least one byte has been written, the call succeeds, and returns the number of bytes written.

Since writev can also be interrupted intermediate of writes, it can return a smaller number. Let me add a check.

Thanks!!

Quoted 72 lines
Quoted 72 lines
tools/perf/util/probe-file.c +35 -0
+
+	strlist__for_each(snode, entry->tevlist) {
+		iov[0].iov_base = (void *)snode->s;
+		iov[0].iov_len = strlen(snode->s);
+		iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1;
+		ret = writev(fd, iov, 2);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+int probe_cache__commit(struct probe_cache *pcache)
+{
+	struct probe_cache_entry *entry;
+	int ret = 0;
+
+	/* TBD: if we do not update existing entries, skip it */
+	ret = lseek(pcache->fd, 0, SEEK_SET);
+	if (ret < 0)
+		goto out;
+
+	ret = ftruncate(pcache->fd, 0);
+	if (ret < 0)
+		goto out;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		ret = probe_cache_entry__write(entry, pcache->fd);
+		pr_debug("Cache committed: %d\n", ret);
+		if (ret < 0)
+			break;
+	}
+out:
+	return ret;
+}
tools/perf/util/probe-file.h +19 -0
index 18ac9cf..d2b8791d 100644
@@ -5,6 +5,19 @@
 #include "strfilter.h"
 #include "probe-event.h"
 
+/* Cache of probe definitions */
+struct probe_cache_entry {
+	struct list_head	list;
+	struct perf_probe_event pev;
+	char			*spev;
+	struct strlist		*tevlist;
+};
+
+struct probe_cache {
+	int	fd;
+	struct list_head list;
+};
+
 #define PF_FL_UPROBE	1
 #define PF_FL_RW	2
 
@@ -18,5 +31,11 @@ int probe_file__get_events(int fd, struct strfilter *filter,
 				  struct strlist *plist);
 int probe_file__del_strlist(int fd, struct strlist *namelist);
 
+struct probe_cache *probe_cache__new(const char *target);
+int probe_cache__add_entry(struct probe_cache *pcache,
+			   struct perf_probe_event *pev,
+			   struct probe_trace_event *tevs, int ntevs);
+int probe_cache__commit(struct probe_cache *pcache);
+void probe_cache__delete(struct probe_cache *pcache);
 
 #endif
Signature · 1 line
Masami Hiramatsu <[email protected]>
#14 lore raw

PATCH v10 7/23 perf probe: Add --cache option to cache the probe definitions

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

Add --cache option to cache the probe definitions. This just saves the result of the dwarf analysis to probe cache.

---
Changes in v10:
- Split into 5 subpatches.
Changes in v6:
- Remove unneeded O_APPEND from open(). (Thanks Namhyung!)
- Fix to check the return value of probe_cache_entry__new and strdup.(ditto)
Changes in v5:
- Move probe_cache* definitions. (code cleanup)

Changes in v4:
- Remove cache saving failure message.

---
 tools/perf/Documentation/perf-probe.txt |    4 ++++
 tools/perf/builtin-probe.c              |    1 +
 tools/perf/util/probe-event.c           |    9 +++++++++
 tools/perf/util/probe-event.h           |    1 +
 4 files changed, 15 insertions(+)
tools/perf/Documentation/perf-probe.txt +4 -0
index 3a8a9ba..947db6f 100644
@@ -109,6 +109,10 @@ OPTIONS
 	Dry run. With this option, --add and --del doesn't execute actual
 	adding and removal operations.
 
+--cache::
+	Cache the probes (with --add option). Any events which successfully added
+	are also stored in the cache file.
+
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
 
tools/perf/builtin-probe.c +1 -0
index 9af859b..6d7ab431 100644
@@ -512,6 +512,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
 		    "Enable symbol demangling"),
 	OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
 		    "Enable kernel symbol demangling"),
+	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
 	OPT_END()
 	};
 	int ret;
tools/perf/util/probe-event.c +9 -0
index cbc8a8b..7cfd1fa 100644
@@ -2514,6 +2514,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 {
 	int i, fd, ret;
 	struct probe_trace_event *tev = NULL;
+	struct probe_cache *cache = NULL;
 	struct strlist *namelist;
 
 	fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
@@ -2555,6 +2556,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	}
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
+	if (ret == 0 && probe_conf.cache) {
+		cache = probe_cache__new(pev->target);
+		if (cache) {
+			probe_cache__add_entry(cache, pev, tevs, ntevs);
+			probe_cache__commit(cache);
+			probe_cache__delete(cache);
+		}
+	}
 
 	strlist__delete(namelist);
 close_out:
tools/perf/util/probe-event.h +1 -0
index 0b024ba..432b690 100644
@@ -12,6 +12,7 @@ struct probe_conf {
 	bool	show_location_range;
 	bool	force_add;
 	bool	no_inlines;
+	bool	cache;
 	int	max_probes;
 };
 extern struct probe_conf probe_conf;
#15 lore raw

Re: [PATCH perf/core v10 07/23] perf probe: Add --cache option to cache the probe definitions

Em Wed, Jun 08, 2016 at 06:30:10PM +0900, Masami Hiramatsu escreveu:

tools/perf/util/probe-event.c static int __add_probe_trace_events(struct perf_probe_event *pev, · line 2563

Quoted 9 lines
tools/perf/util/probe-event.c +5 -0
@@ -2555,6 +2556,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	}
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
+	if (ret == 0 && probe_conf.cache) {
+		cache = probe_cache__new(pev->target);
+		if (cache) {
+			probe_cache__add_entry(cache, pev, tevs, ntevs);
+			probe_cache__commit(cache);

These two functions may fail, please check its return and forward errors appropriately.

Quoted 1 line
tools/perf/util/probe-event.c +1 -0
+			probe_cache__delete(cache);
#16 lore raw

Re: [PATCH perf/core v10 07/23] perf probe: Add --cache option to cache the probe definitions

Quoted 13 lines · On Thu, 9 Jun 2016 11:18:50 -0300 Arnaldo Carvalho de Melo <[email protected]>…

Em Wed, Jun 08, 2016 at 06:30:10PM +0900, Masami Hiramatsu escreveu:

tools/perf/util/probe-event.c static int __add_probe_trace_events(struct perf_probe_event *pev, · line 2563

Quoted 9 lines
tools/perf/util/probe-event.c +5 -0
@@ -2555,6 +2556,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	}
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
+	if (ret == 0 && probe_conf.cache) {
+		cache = probe_cache__new(pev->target);
+		if (cache) {
+			probe_cache__add_entry(cache, pev, tevs, ntevs);
+			probe_cache__commit(cache);

These two functions may fail, please check its return and forward errors appropriately.

OK, will check and warn that.

Thank you!

Signature · 1 line
Masami Hiramatsu <[email protected]>
#17 lore raw

PATCH v10 8/23 perf probe: Use cache entry if possible

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

Before analyzing debuginfo, try to find a corresponding entry from probe cache always. This does not depend on --cache, the --cache enables to store/update cache, but looking up the cache is always enabled.

---
Changes in v6:
- Remove fallback lookup routine by using function name as cached event name, because it should be done by following patch which supports %cached-event.

---
 tools/perf/util/probe-event.c |   65 ++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/probe-file.c  |   20 ++++++++++++-
 tools/perf/util/probe-file.h  |    5 +++
 3 files changed, 86 insertions(+), 4 deletions(-)
tools/perf/util/probe-event.c +63 -2
index 7cfd1fa..d8e5004 100644
@@ -2476,17 +2476,24 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
 	char buf[64];
 	int ret;
 
+	/* If probe_event or trace_event already have the name, reuse it */
 	if (pev->event)
 		event = pev->event;
-	else
+	else if (tev->event)
+		event = tev->event;
+	else {
+		/* Or generate new one from probe point */
 		if (pev->point.function &&
 			(strncmp(pev->point.function, "0x", 2) != 0) &&
 			!strisglob(pev->point.function))
 			event = pev->point.function;
 		else
 			event = tev->point.realname;
+	}
 	if (pev->group)
 		group = pev->group;
+	else if (tev->group)
+		group = tev->group;
 	else
 		group = PERFPROBE_GROUP;
 
@@ -2533,7 +2540,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	for (i = 0; i < ntevs; i++) {
 		tev = &tevs[i];
 		/* Skip if the symbol is out of .text or blacklisted */
-		if (!tev->point.symbol)
+		if (!tev->point.symbol && !pev->uprobes)
 			continue;
 
 		/* Set new name for tev (and update namelist) */
@@ -2846,6 +2853,55 @@ errout:
 
 bool __weak arch__prefers_symtab(void) { return false; }
 
+static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
+					      struct probe_trace_event **tevs)
+{
+	struct probe_cache *cache;
+	struct probe_cache_entry *entry;
+	struct probe_trace_event *tev;
+	struct str_node *node;
+	int ret, i;
+
+	cache = probe_cache__new(pev->target);
+	if (!cache)
+		return 0;
+
+	entry = probe_cache__find(cache, pev);
+	if (!entry) {
+		ret = 0;
+		goto out;
+	}
+
+	ret = strlist__nr_entries(entry->tevlist);
+	if (ret > probe_conf.max_probes) {
+		pr_debug("Too many entries matched in the cache of %s\n",
+			 pev->target ? : "kernel");
+		ret = -E2BIG;
+		goto out;
+	}
+
+	*tevs = zalloc(ret * sizeof(*tev));
+	if (!*tevs) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	i = 0;
+	strlist__for_each(node, entry->tevlist) {
+		tev = &(*tevs)[i++];
+		ret = parse_probe_trace_command(node->s, tev);
+		if (ret < 0)
+			goto out;
+		/* Set the uprobes attribute as same as original */
+		tev->uprobes = pev->uprobes;
+	}
+	ret = i;
+
+out:
+	probe_cache__delete(cache);
+	return ret;
+}
+
 static int convert_to_probe_trace_events(struct perf_probe_event *pev,
 					 struct probe_trace_event **tevs)
 {
@@ -2868,6 +2924,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
 	if (ret > 0)
 		return ret;
 
+	/* At first, we need to lookup cache entry */
+	ret = find_probe_trace_events_from_cache(pev, tevs);
+	if (ret > 0)
+		return ret;	/* Found in probe cache */
+
 	if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
 		ret = find_probe_trace_events_from_map(pev, tevs);
 		if (ret > 0)
tools/perf/util/probe-file.c +19 -1
index 689d874..f30f609 100644
@@ -514,7 +514,7 @@ static bool streql(const char *a, const char *b)
 	return !strcmp(a, b);
 }
 
-static struct probe_cache_entry *
+struct probe_cache_entry *
 probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
 {
 	struct probe_cache_entry *entry = NULL;
@@ -539,6 +539,24 @@ found:
 	return entry;
 }
 
+struct probe_cache_entry *
+probe_cache__find_by_name(struct probe_cache *pcache,
+			  const char *group, const char *event)
+{
+	struct probe_cache_entry *entry = NULL;
+
+	list_for_each_entry(entry, &pcache->list, list) {
+		/* Hit if same event name or same command-string */
+		if (streql(entry->pev.group, group) &&
+		    streql(entry->pev.event, event))
+			goto found;
+	}
+	entry = NULL;
+
+found:
+	return entry;
+}
+
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
 			   struct probe_trace_event *tevs, int ntevs)
tools/perf/util/probe-file.h +4 -1
index d2b8791d..1385c8b 100644
@@ -37,5 +37,8 @@ int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct probe_trace_event *tevs, int ntevs);
 int probe_cache__commit(struct probe_cache *pcache);
 void probe_cache__delete(struct probe_cache *pcache);
-
+struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
+					    struct perf_probe_event *pev);
+struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
+					const char *group, const char *event);
 #endif
#18 lore raw

PATCH v10 9/23 perf probe: Show all cached probes

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

perf probe --list shows all cached probes when --cache is given. Each caches are shown with on which binary that probed. e.g.
-----

  # perf probe --cache vfs_read \$params
  # perf probe --cache -x /lib64/libc-2.17.so getaddrinfo \$params
  # perf probe --cache --list
  [kernel.kallsyms] (1466a0a250b5d0070c6d0f03c5fed30b237970a1):
  vfs_read $params
  /usr/lib64/libc-2.17.so (c31ffe7942bfd77b2fca8f9bd5709d387a86d3bc):
  getaddrinfo $params
  -----
Note that $params requires debuginfo.

---
Changes in v7:
- Remove the top '/' from binary name if it is not a regular file.

---
 tools/perf/Documentation/perf-probe.txt |    8 ++-
 tools/perf/builtin-probe.c              |    2 -
 tools/perf/util/build-id.c              |   86 ++++++++++++++++++++++++++++++-
 tools/perf/util/build-id.h              |    3 +
 tools/perf/util/probe-event.c           |    3 +
 tools/perf/util/probe-file.c            |   67 +++++++++++++++++++++++-
 tools/perf/util/probe-file.h            |    1 
 7 files changed, 163 insertions(+), 7 deletions(-)
tools/perf/Documentation/perf-probe.txt +6 -2
index 947db6f..5a70d45 100644
@@ -67,7 +67,10 @@ OPTIONS
 
 -l::
 --list[=[GROUP:]EVENT]::
-	List up current probe events. This can also accept filtering patterns of event names.
+	List up current probe events. This can also accept filtering patterns of
+	event names.
+	When this is used with --cache, perf shows all cached probes instead of
+	the live probes.
 
 -L::
 --line=::
@@ -110,8 +113,9 @@ OPTIONS
 	adding and removal operations.
 
 --cache::
-	Cache the probes (with --add option). Any events which successfully added
+	(With --add) Cache the probes. Any events which successfully added
 	are also stored in the cache file.
+	(With --list) Show cached probes.
 
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
tools/perf/builtin-probe.c +1 -1
index 6d7ab431..53e380c0e 100644
@@ -44,7 +44,7 @@
 
 #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
 #define DEFAULT_FUNC_FILTER "!_*"
-#define DEFAULT_LIST_FILTER "*:*"
+#define DEFAULT_LIST_FILTER "*"
 
 /* Session management structure */
 static struct {
tools/perf/util/build-id.c +84 -2

104 lines not on this page · raw message

tools/perf/util/build-id.h +3 -0
index d8c7f2f..c05503e 100644
@@ -30,8 +30,11 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
 int perf_session__write_buildid_table(struct perf_session *session, int fd);
 int perf_session__cache_build_ids(struct perf_session *session);
 
+char *build_id_cache__origname(const char *sbuild_id);
+char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 			       bool is_kallsyms, bool is_vdso);
+int build_id_cache__list_all(struct strlist **result);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
tools/perf/util/probe-event.c +3 -0
index d8e5004..6a55fa3 100644
@@ -2368,6 +2368,9 @@ int show_perf_probe_events(struct strfilter *filter)
 
 	setup_pager();
 
+	if (probe_conf.cache)
+		return probe_cache__show_all_caches(filter);
+
 	ret = init_probe_symbol_maps(false);
 	if (ret < 0)
 		return ret;
tools/perf/util/probe-file.c +65 -2

86 lines not on this page · raw message

tools/perf/util/probe-file.h +1 -0
index 1385c8b..ac70446 100644
@@ -41,4 +41,5 @@ struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
 					    struct perf_probe_event *pev);
 struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
 					const char *group, const char *event);
+int probe_cache__show_all_caches(struct strfilter *filter);
 #endif
#19 lore raw

Re: [PATCH perf/core v10 09/23] perf probe: Show all cached probes

Em Wed, Jun 08, 2016 at 06:30:30PM +0900, Masami Hiramatsu escreveu:

Quoted 14 lines
tools/perf/util/build-id.c +14 -0
+char *build_id_cache__origname(const char *sbuild_id)
+{
+	char *linkname;
+	char buf[PATH_MAX];
+	char *ret = NULL, *p;
+	size_t offs = 5;	/* == strlen("../..") */
+
+	linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
+	if (!linkname)
+		return NULL;
+
+	if (readlink(linkname, buf, PATH_MAX) < 0)
+		goto out;
+	/* The link should be "../..<origpath>/<sbuild_id>" */

tools/perf/util/build-id.c static char *build_id_cache__linkname(const char *sbuild_id, char *bf, · line 204

Quoted 10 lines
tools/perf/util/build-id.c +10 -0
+	p = strrchr(buf, '/');	/* Cut off the "/<sbuild_id>" */
+	if (p && (p > buf + offs)) {
+		*p = '\0';
+		if (buf[offs + 1] == '[')
+			offs++;	/*
+				 * This is a DSO name, like [kernel.kallsyms].
+				 * Skip the first '/', since this is not the
+				 * cache of a regular file.
+				 */
+		ret = strdup(buf + offs);	/* Skip "../..[/]" */

strdup can fail.

Quoted 14 lines
tools/perf/util/build-id.c +7 -0
+	}
+out:
+	free(linkname);
+	return ret;
+}
+
 static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
 {
 	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
@@ -390,6 +419,59 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
+int build_id_cache__list_all(struct strlist **result)

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 432

Quoted 10 lines
tools/perf/util/build-id.c +10 -0
+{
+	struct strlist *toplist, *list, *bidlist;
+	struct str_node *nd, *nd2;
+	char *topdir, *linkdir;
+	char sbuild_id[SBUILD_ID_SIZE];
+	int ret = 0;
+
+	/* Open the top-level directory */
+	if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
+		return -errno;

Wouldn't be better to just return -1 and leave the caller to look at errno? I know that there is code that does that, but better stick to the errno convention?

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 436

Quoted 4 lines
tools/perf/util/build-id.c +4 -0
+	toplist = lsdir(topdir, lsdir_no_dot_filter);
+	if (!toplist) {
+		pr_debug("Failed to opendir %s\n", topdir);
+		ret = -errno;

ditto

Quoted 13 lines
tools/perf/util/build-id.c +13 -0
+		goto out;
+	}
+	bidlist = strlist__new(NULL, NULL);
+	strlist__for_each(nd, toplist) {
+		if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0) {
+			ret = -errno;
+			goto out;
+		}
+		/* Open the lower-level directory */
+		list = lsdir(linkdir, lsdir_no_dot_filter);
+		if (!list) {
+			pr_debug("Failed to open %s: %d\n", linkdir, -errno);
+			goto next;

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 459

Quoted 10 lines
tools/perf/util/build-id.c +10 -0
+		}
+		strlist__for_each(nd2, list) {
+			ret = snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s",
+					nd->s, nd2->s);
+			if (ret != SBUILD_ID_SIZE - 1) {
+				pr_debug("%s/%s is not buildid cache\n",
+					nd->s, nd2->s);
+				continue;
+			}
+			strlist__add(bidlist, sbuild_id);

strlist__add() can fail, please check its result.

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 469

Quoted 10 lines
tools/perf/util/build-id.c +10 -0
+		}
+		strlist__delete(list);
+next:
+		free(linkdir);
+	}
+
+	*result = bidlist;
+out:
+	if (toplist)
+		strlist__delete(toplist);

No need for checking toplist.

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 473

Quoted 4 lines
tools/perf/util/build-id.c +4 -0
+	free(topdir);
+
+	return ret;
+}

<SNIP>

- Arnaldo

#20 lore raw

Re: [PATCH perf/core v10 09/23] perf probe: Show all cached probes

Quoted 19 lines · On Thu, 9 Jun 2016 11:22:55 -0300 Arnaldo Carvalho de Melo <[email protected]>…

Em Wed, Jun 08, 2016 at 06:30:30PM +0900, Masami Hiramatsu escreveu:

Quoted 18 lines
tools/perf/util/build-id.c +18 -0
+char *build_id_cache__origname(const char *sbuild_id)
+{
+	char *linkname;
+	char buf[PATH_MAX];
+	char *ret = NULL, *p;
+	size_t offs = 5;	/* == strlen("../..") */
+
+	linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
+	if (!linkname)
+		return NULL;
+
+	if (readlink(linkname, buf, PATH_MAX) < 0)
+		goto out;
+	/* The link should be "../..<origpath>/<sbuild_id>" */
+	p = strrchr(buf, '/');	/* Cut off the "/<sbuild_id>" */
+	if (p && (p > buf + offs)) {
+		*p = '\0';
+		if (buf[offs + 1] == '[')
Quoted 8 lines

tools/perf/util/build-id.c static char *build_id_cache__linkname(const char *sbuild_id, char *bf, · line 204

Quoted 6 lines
tools/perf/util/build-id.c +6 -0
+			offs++;	/*
+				 * This is a DSO name, like [kernel.kallsyms].
+				 * Skip the first '/', since this is not the
+				 * cache of a regular file.
+				 */
+		ret = strdup(buf + offs);	/* Skip "../..[/]" */

strdup can fail.

Ah, right.

Quoted 14 lines
Quoted 14 lines
tools/perf/util/build-id.c +6 -0
+	}
+out:
+	free(linkname);
+	return ret;
+}
+
 static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
 {
 	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
@@ -390,6 +419,59 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
Quoted 15 lines

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 432

Quoted 11 lines
tools/perf/util/build-id.c +11 -0
+int build_id_cache__list_all(struct strlist **result)
+{
+	struct strlist *toplist, *list, *bidlist;
+	struct str_node *nd, *nd2;
+	char *topdir, *linkdir;
+	char sbuild_id[SBUILD_ID_SIZE];
+	int ret = 0;
+
+	/* Open the top-level directory */
+	if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
+		return -errno;

Wouldn't be better to just return -1 and leave the caller to look at errno? I know that there is code that does that, but better stick to the errno convention?

Hmm, IMHO, that may depends on case. This case is possible, but in below case, pr_debug() also calls libc function and it can override the errno. This can also happen when we run some cleanup code before returning.

So, I think it will be good for enough small functions, but not good for complex bigger functions.

Quoted 1 line

From the viewpoint of debugging, maybe it is also possible

to return -N, where N is based on the place where the error happens :)

Quoted 24 lines

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 436

Quoted 4 lines
tools/perf/util/build-id.c +4 -0
+	toplist = lsdir(topdir, lsdir_no_dot_filter);
+	if (!toplist) {
+		pr_debug("Failed to opendir %s\n", topdir);
+		ret = -errno;

ditto

Quoted 17 lines
tools/perf/util/build-id.c +17 -0
+		goto out;
+	}
+	bidlist = strlist__new(NULL, NULL);
+	strlist__for_each(nd, toplist) {
+		if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0) {
+			ret = -errno;
+			goto out;
+		}
+		/* Open the lower-level directory */
+		list = lsdir(linkdir, lsdir_no_dot_filter);
+		if (!list) {
+			pr_debug("Failed to open %s: %d\n", linkdir, -errno);
+			goto next;
+		}
+		strlist__for_each(nd2, list) {
+			ret = snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s",
+					nd->s, nd2->s);
Quoted 8 lines

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 459

Quoted 6 lines
tools/perf/util/build-id.c +6 -0
+			if (ret != SBUILD_ID_SIZE - 1) {
+				pr_debug("%s/%s is not buildid cache\n",
+					nd->s, nd2->s);
+				continue;
+			}
+			strlist__add(bidlist, sbuild_id);

strlist__add() can fail, please check its result.

Ah, I forgot to update that...

Quoted 13 lines

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 469

Quoted 11 lines
tools/perf/util/build-id.c +10 -0
+		}
+		strlist__delete(list);
+next:
+		free(linkdir);
+	}
+
+	*result = bidlist;
+out:
+	if (toplist)
+		strlist__delete(toplist);

No need for checking toplist.

OK.

Quoted 9 lines

tools/perf/util/build-id.c void disable_buildid_cache(void) · line 473

Quoted 5 lines
tools/perf/util/build-id.c +4 -0
+	free(topdir);
+
+	return ret;
+}

<SNIP>

- Arnaldo

Thank you!

Signature · 1 line
Masami Hiramatsu <[email protected]>
#21 lore raw

Re: [PATCH perf/core v10 09/23] perf probe: Show all cached probes

Quoted 31 lines · On Sat, 11 Jun 2016 09:28:09 +0900 Masami Hiramatsu <[email protected]> wrote:
Quoted 19 lines · On Thu, 9 Jun 2016 11:22:55 -0300 Arnaldo Carvalho de Melo <[email protected]>…

Em Wed, Jun 08, 2016 at 06:30:30PM +0900, Masami Hiramatsu escreveu:

Quoted 18 lines
tools/perf/util/build-id.c +18 -0
+char *build_id_cache__origname(const char *sbuild_id)
+{
+	char *linkname;
+	char buf[PATH_MAX];
+	char *ret = NULL, *p;
+	size_t offs = 5;	/* == strlen("../..") */
+
+	linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
+	if (!linkname)
+		return NULL;
+
+	if (readlink(linkname, buf, PATH_MAX) < 0)
+		goto out;
+	/* The link should be "../..<origpath>/<sbuild_id>" */
+	p = strrchr(buf, '/');	/* Cut off the "/<sbuild_id>" */
+	if (p && (p > buf + offs)) {
+		*p = '\0';
+		if (buf[offs + 1] == '[')
Quoted 8 lines

tools/perf/util/build-id.c static char *build_id_cache__linkname(const char *sbuild_id, char *bf, · line 204

Quoted 6 lines
tools/perf/util/build-id.c +6 -0
+			offs++;	/*
+				 * This is a DSO name, like [kernel.kallsyms].
+				 * Skip the first '/', since this is not the
+				 * cache of a regular file.
+				 */
+		ret = strdup(buf + offs);	/* Skip "../..[/]" */

strdup can fail.

Quoted 1 line

Ah, right.

In this case, build_id_cache__origname() returns ret immediately(see below), so we don't need to take reaction about that :)

Quoted 5 lines
Quoted 5 lines

tools/perf/util/build-id.c static char *build_id_cache__linkname(const char *sbuild_id, char *bf, · line 209

Quoted 5 lines
tools/perf/util/build-id.c +5 -0
+	}
+out:
+	free(linkname);
+	return ret;
+}

here, if strdup() fails, this just return NULL. that is OK.

Thanks!

Signature · 1 line
Masami Hiramatsu <[email protected]>
#22 lore raw

PATCH v10 10/23 perf probe: Remove caches when --cache is given

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

perf-probe --del removes caches when --cache is given. Note that the delete pattern is not same as normal events.

If you cached probes with event name, --del "eventname" works as expected. However, if you skipped it, the cached probes doesn't have actual event name. In that case
--del "probe-desc" is required (wildcard is acceptable).
For example a cache entry has the probe-desc "vfs_read $params", you can remove it with --del 'vfs_read*'.

-----

  # perf probe --cache --list
  /[kernel.kallsyms] (1466a0a250b5d0070c6d0f03c5fed30b237970a1):
  vfs_read $params
  /usr/lib64/libc-2.17.so (c31ffe7942bfd77b2fca8f9bd5709d387a86d3bc):
  getaddrinfo $params
  # perf probe --cache --del vfs_read\*
  Removed cached event: probe:vfs_read
  # perf probe --cache --list
  /[kernel.kallsyms] (1466a0a250b5d0070c6d0f03c5fed30b237970a1):
  /usr/lib64/libc-2.17.so (c31ffe7942bfd77b2fca8f9bd5709d387a86d3bc):
  getaddrinfo $params
  -----

---
Changes in v9:
- Fix to show which event is deleted (Thanks Hemant!)
Changes in v4:
- move del_perf_probe_caches() into builtin-probe.c since command-line related delete procedure is there now.

---
 tools/perf/Documentation/perf-probe.txt |    1 +
 tools/perf/builtin-probe.c              |   27 +++++++++++++++++++++++++
 tools/perf/util/probe-file.c            |   34 +++++++++++++++++++++++++------
 tools/perf/util/probe-file.h            |    2 ++
 4 files changed, 57 insertions(+), 7 deletions(-)
tools/perf/Documentation/perf-probe.txt +1 -0
index 5a70d45..8d09173 100644
@@ -116,6 +116,7 @@ OPTIONS
 	(With --add) Cache the probes. Any events which successfully added
 	are also stored in the cache file.
 	(With --list) Show cached probes.
+	(With --del) Remove cached probes.
 
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
tools/perf/builtin-probe.c +27 -0
index 53e380c0e..8f61525 100644
@@ -363,6 +363,30 @@ out_cleanup:
 	return ret;
 }
 
+static int del_perf_probe_caches(struct strfilter *filter)
+{
+	struct probe_cache *cache;
+	struct strlist *bidlist;
+	struct str_node *nd;
+	int ret;
+
+	ret = build_id_cache__list_all(&bidlist);
+	if (ret < 0) {
+		pr_debug("Failed to get buildids: %d\n", ret);
+		return ret;
+	}
+
+	strlist__for_each(nd, bidlist) {
+		cache = probe_cache__new(nd->s);
+		if (!cache)
+			continue;
+		probe_cache__remove_entries(cache, filter);
+		probe_cache__commit(cache);
+		probe_cache__delete(cache);
+	}
+	return 0;
+}
+
 static int perf_del_probe_events(struct strfilter *filter)
 {
 	int ret, ret2, ufd = -1, kfd = -1;
@@ -375,6 +399,9 @@ static int perf_del_probe_events(struct strfilter *filter)
 
 	pr_debug("Delete filter: \'%s\'\n", str);
 
+	if (probe_conf.cache)
+		return del_perf_probe_caches(filter);
+
 	/* Get current event names */
 	ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
 	if (ret < 0)
tools/perf/util/probe-file.c +27 -7
index a6d4a67..f687607 100644
@@ -660,19 +660,39 @@ out:
 	return ret;
 }
 
+static bool probe_cache_entry__compare(struct probe_cache_entry *entry,
+				       struct strfilter *filter)
+{
+	char buf[128], *ptr = entry->spev;
+
+	if (entry->pev.event) {
+		snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
+		ptr = buf;
+	}
+	return strfilter__compare(filter, ptr);
+}
+
+int probe_cache__remove_entries(struct probe_cache *pcache,
+				struct strfilter *filter)
+{
+	struct probe_cache_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &pcache->list, list) {
+		if (probe_cache_entry__compare(entry, filter)) {
+			pr_info("Removed cached event: %s\n", entry->spev);
+			probe_cache_entry__delete(entry);
+		}
+	}
+	return 0;
+}
+
 static int probe_cache__show_entries(struct probe_cache *pcache,
 				     struct strfilter *filter)
 {
 	struct probe_cache_entry *entry;
-	char buf[128], *ptr;
 
 	list_for_each_entry(entry, &pcache->list, list) {
-		if (entry->pev.event) {
-			ptr = buf;
-			snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
-		} else
-			ptr = entry->spev;
-		if (strfilter__compare(filter, ptr))
+		if (probe_cache_entry__compare(entry, filter))
 			printf("%s\n", entry->spev);
 	}
 	return 0;
tools/perf/util/probe-file.h +2 -0
index ac70446..e6fd9b9 100644
@@ -37,6 +37,8 @@ int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct probe_trace_event *tevs, int ntevs);
 int probe_cache__commit(struct probe_cache *pcache);
 void probe_cache__delete(struct probe_cache *pcache);
+int probe_cache__remove_entries(struct probe_cache *pcache,
+				struct strfilter *filter);
 struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
 					    struct perf_probe_event *pev);
 struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
#23 lore raw

Re: [PATCH perf/core v10 10/23] perf probe: Remove caches when --cache is given

Em Wed, Jun 08, 2016 at 06:30:40PM +0900, Masami Hiramatsu escreveu:

Quoted 119 lines

From: Masami Hiramatsu <[email protected]>

perf-probe --del removes caches when --cache is given. Note that the delete pattern is not same as normal events.

If you cached probes with event name, --del "eventname" works as expected. However, if you skipped it, the cached probes doesn't have actual event name. In that case
--del "probe-desc" is required (wildcard is acceptable).
For example a cache entry has the probe-desc "vfs_read $params", you can remove it with --del 'vfs_read*'.

-----

  # perf probe --cache --list
  /[kernel.kallsyms] (1466a0a250b5d0070c6d0f03c5fed30b237970a1):
  vfs_read $params
  /usr/lib64/libc-2.17.so (c31ffe7942bfd77b2fca8f9bd5709d387a86d3bc):
  getaddrinfo $params
  # perf probe --cache --del vfs_read\*
  Removed cached event: probe:vfs_read
  # perf probe --cache --list
  /[kernel.kallsyms] (1466a0a250b5d0070c6d0f03c5fed30b237970a1):
  /usr/lib64/libc-2.17.so (c31ffe7942bfd77b2fca8f9bd5709d387a86d3bc):
  getaddrinfo $params
  -----

---
Changes in v9:
- Fix to show which event is deleted (Thanks Hemant!)
Changes in v4:
- move del_perf_probe_caches() into builtin-probe.c since command-line related delete procedure is there now.

---
 tools/perf/Documentation/perf-probe.txt |    1 +
 tools/perf/builtin-probe.c              |   27 +++++++++++++++++++++++++
 tools/perf/util/probe-file.c            |   34 +++++++++++++++++++++++++------
 tools/perf/util/probe-file.h            |    2 ++
 4 files changed, 57 insertions(+), 7 deletions(-)
tools/perf/Documentation/perf-probe.txt +1 -0
index 5a70d45..8d09173 100644
@@ -116,6 +116,7 @@ OPTIONS
 	(With --add) Cache the probes. Any events which successfully added
 	are also stored in the cache file.
 	(With --list) Show cached probes.
+	(With --del) Remove cached probes.
 
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
tools/perf/builtin-probe.c +24 -0
index 53e380c0e..8f61525 100644
@@ -363,6 +363,30 @@ out_cleanup:
 	return ret;
 }
 
+static int del_perf_probe_caches(struct strfilter *filter)
+{
+	struct probe_cache *cache;
+	struct strlist *bidlist;
+	struct str_node *nd;
+	int ret;
+
+	ret = build_id_cache__list_all(&bidlist);
+	if (ret < 0) {
+		pr_debug("Failed to get buildids: %d\n", ret);
+		return ret;
+	}
+
+	strlist__for_each(nd, bidlist) {
+		cache = probe_cache__new(nd->s);
+		if (!cache)
+			continue;
+		probe_cache__remove_entries(cache, filter);
+		probe_cache__commit(cache);
 
 
 
+		probe_cache__delete(cache);
+	}
+	return 0;
+}
+

static int perf_del_probe_events(struct strfilter *filter) {

tools/perf/builtin-probe.c +3 -0
 	int ret, ret2, ufd = -1, kfd = -1;
@@ -375,6 +399,9 @@ static int perf_del_probe_events(struct strfilter *filter)
 
 	pr_debug("Delete filter: \'%s\'\n", str);
 
+	if (probe_conf.cache)
+		return del_perf_probe_caches(filter);
+
 	/* Get current event names */
 	ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
 	if (ret < 0)
tools/perf/util/probe-file.c +8 -0
index a6d4a67..f687607 100644
@@ -660,19 +660,39 @@ out:
 	return ret;
 }
 
+static bool probe_cache_entry__compare(struct probe_cache_entry *entry,
+				       struct strfilter *filter)
+{
+	char buf[128], *ptr = entry->spev;
+
+	if (entry->pev.event) {
+		snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
+		ptr = buf;

tools/perf/util/probe-file.c out: · line 680

Quoted 10 lines
tools/perf/util/probe-file.c +10 -0
+	}
+	return strfilter__compare(filter, ptr);
+}
+
+int probe_cache__remove_entries(struct probe_cache *pcache,
+				struct strfilter *filter)
+{
+	struct probe_cache_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &pcache->list, list) {

so here you used the preferred idiom, i.e. using list_for_each_entry_safe(), once you stop doing the list removal at the list entry destructor, it gets the good old boring usual idiom, please do that.

Also please consider renaming perf_cache__remove_entries() to perf_cache__filter_purge(), as it doesn't simply remove entries, it purges them (that is, remove an entry and delete it), and only if the entry got filtered.

Quoted 38 lines
tools/perf/util/probe-file.c +9 -7
+		if (probe_cache_entry__compare(entry, filter)) {
+			pr_info("Removed cached event: %s\n", entry->spev);
+			probe_cache_entry__delete(entry);
+		}
+	}
+	return 0;
+}
+
 static int probe_cache__show_entries(struct probe_cache *pcache,
 				     struct strfilter *filter)
 {
 	struct probe_cache_entry *entry;
-	char buf[128], *ptr;
 
 	list_for_each_entry(entry, &pcache->list, list) {
-		if (entry->pev.event) {
-			ptr = buf;
-			snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
-		} else
-			ptr = entry->spev;
-		if (strfilter__compare(filter, ptr))
+		if (probe_cache_entry__compare(entry, filter))
 			printf("%s\n", entry->spev);
 	}
 	return 0;
tools/perf/util/probe-file.h +2 -0
index ac70446..e6fd9b9 100644
@@ -37,6 +37,8 @@ int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct probe_trace_event *tevs, int ntevs);
 int probe_cache__commit(struct probe_cache *pcache);
 void probe_cache__delete(struct probe_cache *pcache);
+int probe_cache__remove_entries(struct probe_cache *pcache,
+				struct strfilter *filter);
 struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
 					    struct perf_probe_event *pev);
 struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
#24 lore raw

Re: [PATCH perf/core v10 10/23] perf probe: Remove caches when --cache is given

Quoted 23 lines · On Thu, 9 Jun 2016 11:28:28 -0300 Arnaldo Carvalho de Melo <[email protected]>…
Quoted 23 lines
tools/perf/util/probe-file.c +15 -0
index a6d4a67..f687607 100644
@@ -660,19 +660,39 @@ out:
 	return ret;
 }
 
+static bool probe_cache_entry__compare(struct probe_cache_entry *entry,
+				       struct strfilter *filter)
+{
+	char buf[128], *ptr = entry->spev;
+
+	if (entry->pev.event) {
+		snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
+		ptr = buf;
+	}
+	return strfilter__compare(filter, ptr);
+}
+
+int probe_cache__remove_entries(struct probe_cache *pcache,
+				struct strfilter *filter)
+{
Quoted 8 lines

tools/perf/util/probe-file.c out: · line 680

Quoted 3 lines
tools/perf/util/probe-file.c +3 -0
+	struct probe_cache_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &pcache->list, list) {

so here you used the preferred idiom, i.e. using list_for_each_entry_safe(), once you stop doing the list removal at the list entry destructor, it gets the good old boring usual idiom, please do that.

OK, I see.

Quoted 5 lines

Also please consider renaming perf_cache__remove_entries() to perf_cache__filter_purge(), as it doesn't simply remove entries, it purges them (that is, remove an entry and delete it), and only if the entry got filtered.

OK, such advice about naming helps me a lot :)

Thank you!

Signature · 1 line
Masami Hiramatsu <[email protected]>
#25 lore raw

PATCH v10 11/23 perf/sdt: ELF support for SDT

In reply to Masami Hiramatsu

From: Hemant Kumar <[email protected]>

This patch serves the initial support to identify and list SDT events in binaries. When programs containing SDT markers are compiled, gcc with the help of assembler directives identifies them and places them in the section ".note.stapsdt". To find these markers from the binaries, one needs to traverse through this section and parse the relevant details like the name, type and location of the marker. Also, the original location could be skewed due to the effect of prelinking. If that is the case, the locations need to be adjusted.

The functions in this patch open a given ELF, find out the SDT section, parse the relevant details, adjust the location (if necessary) and populate them in a list.

A typical note entry in ".note.stapsdt" section is as follows :

|--nhdr.n_namesz--|
------------------------------------
| nhdr | "stapsdt" |
----- |----------------------------------|
| | <location> <base_address> |
| | <semaphore> |

nhdr.n_descsize |  "provider_name"   "note_name"   |
         |      |   <args>                         |
        -----   |----------------------------------|
                |      nhdr      |     "stapsdt"   |
                |...

The above shows an excerpt from the section ".note.stapsdt". 'nhdr' is a structure which has the note name size (n_namesz), note description size (n_desc_sz) and note type (n_type). So, in order to parse the note note info, we need nhdr to tell us where to start from. As can be seen from <sys/sdt.h>, the name of the SDT notes given is "stapsdt". But this is not the identifier of the note.
After that, we go to description of the note to find out its location, the address of the ".stapsdt.base" section and the semaphore address. Then, we find the provider name and the SDT marker name and then follow the arguments.

---
 tools/perf/util/symbol-elf.c |  252 ++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/symbol.h     |   22 ++++
 2 files changed, 274 insertions(+)
tools/perf/util/symbol-elf.c +252 -0
index 87a297d..e74ce17 100644
@@ -1781,6 +1781,258 @@ void kcore_extract__delete(struct kcore_extract *kce)
 	unlink(kce->extract_filename);
 }
 
+/**
+ * populate_sdt_note : Parse raw data and identify SDT note
+ * @elf: elf of the opened file
+ * @data: raw data of a section with description offset applied
+ * @len: note description size
+ * @type: type of the note
+ * @sdt_notes: List to add the SDT note
+ *
+ * Responsible for parsing the @data in section .note.stapsdt in @elf and
+ * if its an SDT note, it appends to @sdt_notes list.
+ */
+static int populate_sdt_note(Elf **elf, const char *data, size_t len,
+			     struct list_head *sdt_notes)
+{
+	const char *provider, *name;
+	struct sdt_note *tmp = NULL;
+	GElf_Ehdr ehdr;
+	GElf_Addr base_off = 0;
+	GElf_Shdr shdr;
+	int ret = -EINVAL;
+
+	union {
+		Elf64_Addr a64[NR_ADDR];
+		Elf32_Addr a32[NR_ADDR];
+	} buf;
+
+	Elf_Data dst = {
+		.d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
+		.d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT),
+		.d_off = 0, .d_align = 0
+	};
+	Elf_Data src = {
+		.d_buf = (void *) data, .d_type = ELF_T_ADDR,
+		.d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
+		.d_align = 0
+	};
+
+	tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
+	if (!tmp) {
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	INIT_LIST_HEAD(&tmp->note_list);
+
+	if (len < dst.d_size + 3)
+		goto out_free_note;
+
+	/* Translation from file representation to memory representation */
+	if (gelf_xlatetom(*elf, &dst, &src,
+			  elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
+		pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
+		goto out_free_note;
+	}
+
+	/* Populate the fields of sdt_note */
+	provider = data + dst.d_size;
+
+	name = (const char *)memchr(provider, '\0', data + len - provider);
+	if (name++ == NULL)
+		goto out_free_note;
+
+	tmp->provider = strdup(provider);
+	if (!tmp->provider) {
+		ret = -ENOMEM;
+		goto out_free_note;
+	}
+	tmp->name = strdup(name);
+	if (!tmp->name) {
+		ret = -ENOMEM;
+		goto out_free_prov;
+	}
+
+	if (gelf_getclass(*elf) == ELFCLASS32) {
+		memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr));
+		tmp->bit32 = true;
+	} else {
+		memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr));
+		tmp->bit32 = false;
+	}
+
+	if (!gelf_getehdr(*elf, &ehdr)) {
+		pr_debug("%s : cannot get elf header.\n", __func__);
+		ret = -EBADF;
+		goto out_free_name;
+	}
+
+	/* Adjust the prelink effect :
+	 * Find out the .stapsdt.base section.
+	 * This scn will help us to handle prelinking (if present).
+	 * Compare the retrieved file offset of the base section with the
+	 * base address in the description of the SDT note. If its different,
+	 * then accordingly, adjust the note location.
+	 */
+	if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
+		base_off = shdr.sh_offset;
+		if (base_off) {
+			if (tmp->bit32)
+				tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
+					tmp->addr.a32[1];
+			else
+				tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
+					tmp->addr.a64[1];
+		}
+	}
+
+	list_add_tail(&tmp->note_list, sdt_notes);
+	return 0;
+
+out_free_name:
+	free(tmp->name);
+out_free_prov:
+	free(tmp->provider);
+out_free_note:
+	free(tmp);
+out_err:
+	return ret;
+}
+
+/**
+ * construct_sdt_notes_list : constructs a list of SDT notes
+ * @elf : elf to look into
+ * @sdt_notes : empty list_head
+ *
+ * Scans the sections in 'elf' for the section
+ * .note.stapsdt. It, then calls populate_sdt_note to find
+ * out the SDT events and populates the 'sdt_notes'.
+ */
+static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
+{
+	GElf_Ehdr ehdr;
+	Elf_Scn *scn = NULL;
+	Elf_Data *data;
+	GElf_Shdr shdr;
+	size_t shstrndx, next;
+	GElf_Nhdr nhdr;
+	size_t name_off, desc_off, offset;
+	int ret = 0;
+
+	if (gelf_getehdr(elf, &ehdr) == NULL) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+	if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
+		ret = -EBADF;
+		goto out_ret;
+	}
+
+	/* Look for the required section */
+	scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
+	if (!scn) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
+		ret = -ENOENT;
+		goto out_ret;
+	}
+
+	data = elf_getdata(scn, NULL);
+
+	/* Get the SDT notes */
+	for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
+					      &desc_off)) > 0; offset = next) {
+		if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
+		    !memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
+			    sizeof(SDT_NOTE_NAME))) {
+			/* Check the type of the note */
+			if (nhdr.n_type != SDT_NOTE_TYPE)
+				goto out_ret;
+
+			ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
+						nhdr.n_descsz, sdt_notes);
+			if (ret < 0)
+				goto out_ret;
+		}
+	}
+	if (list_empty(sdt_notes))
+		ret = -ENOENT;
+
+out_ret:
+	return ret;
+}
+
+/**
+ * get_sdt_note_list : Wrapper to construct a list of sdt notes
+ * @head : empty list_head
+ * @target : file to find SDT notes from
+ *
+ * This opens the file, initializes
+ * the ELF and then calls construct_sdt_notes_list.
+ */
+int get_sdt_note_list(struct list_head *head, const char *target)
+{
+	Elf *elf;
+	int fd, ret;
+
+	fd = open(target, O_RDONLY);
+	if (fd < 0)
+		return -EBADF;
+
+	elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+	if (!elf) {
+		ret = -EBADF;
+		goto out_close;
+	}
+	ret = construct_sdt_notes_list(elf, head);
+	elf_end(elf);
+out_close:
+	close(fd);
+	return ret;
+}
+
+/**
+ * cleanup_sdt_note_list : free the sdt notes' list
+ * @sdt_notes: sdt notes' list
+ *
+ * Free up the SDT notes in @sdt_notes.
+ * Returns the number of SDT notes free'd.
+ */
+int cleanup_sdt_note_list(struct list_head *sdt_notes)
+{
+	struct sdt_note *tmp, *pos;
+	int nr_free = 0;
+
+	list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
+		list_del(&pos->note_list);
+		free(pos->name);
+		free(pos->provider);
+		free(pos);
+		nr_free++;
+	}
+	return nr_free;
+}
+
+/**
+ * sdt_notes__get_count: Counts the number of sdt events
+ * @start: list_head to sdt_notes list
+ *
+ * Returns the number of SDT notes in a list
+ */
+int sdt_notes__get_count(struct list_head *start)
+{
+	struct sdt_note *sdt_ptr;
+	int count = 0;
+
+	list_for_each_entry(sdt_ptr, start, note_list)
+		count++;
+	return count;
+}
+
 void symbol__elf_init(void)
 {
 	elf_version(EV_CURRENT);
tools/perf/util/symbol.h +22 -0
index b10d558..699f7cb 100644
@@ -342,4 +342,26 @@ void arch__sym_update(struct symbol *s, GElf_Sym *sym);
 
 int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb);
 
+/* structure containing an SDT note's info */
+struct sdt_note {
+	char *name;			/* name of the note*/
+	char *provider;			/* provider name */
+	bool bit32;			/* whether the location is 32 bits? */
+	union {				/* location, base and semaphore addrs */
+		Elf64_Addr a64[3];
+		Elf32_Addr a32[3];
+	} addr;
+	struct list_head note_list;	/* SDT notes' list */
+};
+
+int get_sdt_note_list(struct list_head *head, const char *target);
+int cleanup_sdt_note_list(struct list_head *sdt_notes);
+int sdt_notes__get_count(struct list_head *start);
+
+#define SDT_BASE_SCN ".stapsdt.base"
+#define SDT_NOTE_SCN  ".note.stapsdt"
+#define SDT_NOTE_TYPE 3
+#define SDT_NOTE_NAME "stapsdt"
+#define NR_ADDR 3
+
 #endif /* __PERF_SYMBOL */
#26 lore raw

PATCH v10 12/23 perf probe: Add group name support

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

Allow user to set group name for adding new event. Note that user must ensure that the group name doesn't conflict with existing group name carefully. E.g. Existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide kernel embedded events when loading modules.

---
Changes in v4:
- Update Documentation/perf-probe.txt too.

---
 tools/perf/Documentation/perf-probe.txt |   10 ++++++----
 tools/perf/util/probe-event.c           |   23 ++++++++++++++---------
 2 files changed, 20 insertions(+), 13 deletions(-)
tools/perf/Documentation/perf-probe.txt +6 -4
index 8d09173..7a258e9 100644
@@ -143,16 +143,18 @@ PROBE SYNTAX
 Probe points are defined by following syntax.
 
     1) Define event based on function name
-     [EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
+     [[GROUP:]EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
 
     2) Define event based on source file with line number
-     [EVENT=]SRC:ALN [ARG ...]
+     [[GROUP:]EVENT=]SRC:ALN [ARG ...]
 
     3) Define event based on source file with lazy pattern
-     [EVENT=]SRC;PTN [ARG ...]
+     [[GROUP:]EVENT=]SRC;PTN [ARG ...]
 
 
-'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
+'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe.
+Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the
+modules.
 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition.  In addition, '@SRC' specifies a source file which has that function.
 It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
 'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
tools/perf/util/probe-event.c +14 -9
index 6a55fa3..2178d26 100644
@@ -1208,10 +1208,8 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 	bool file_spec = false;
 	/*
 	 * <Syntax>
-	 * perf probe [EVENT=]SRC[:LN|;PTN]
-	 * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
-	 *
-	 * TODO:Group name support
+	 * perf probe [GRP:][EVENT=]SRC[:LN|;PTN]
+	 * perf probe [GRP:][EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
 	 */
 	if (!arg)
 		return -EINVAL;
@@ -1220,11 +1218,19 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 	if (ptr && *ptr == '=') {	/* Event name */
 		*ptr = '\0';
 		tmp = ptr + 1;
-		if (strchr(arg, ':')) {
-			semantic_error("Group name is not supported yet.\n");
-			return -ENOTSUP;
-		}
+		ptr = strchr(arg, ':');
+		if (ptr) {
+			*ptr = '\0';
+			if (!is_c_func_name(arg))
+				goto not_fname;
+			pev->group = strdup(arg);
+			if (!pev->group)
+				return -ENOMEM;
+			arg = ptr + 1;
+		} else
+			pev->group = NULL;
 		if (!is_c_func_name(arg)) {
+not_fname:
 			semantic_error("%s is bad for event name -it must "
 				       "follow C symbol-naming rule.\n", arg);
 			return -EINVAL;
@@ -1232,7 +1238,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 		pev->event = strdup(arg);
 		if (pev->event == NULL)
 			return -ENOMEM;
-		pev->group = NULL;
 		arg = tmp;
 	}
 
#27 lore raw

PATCH v10 13/23 perf buildid-cache: Scan and import user SDT events to probe cache

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

perf buildid-cache --add <binary> scans given binary and add the SDT events to probe cache. "sdt_" prefix is appended for all SDT providers to avoid event-name clash with other pre-defined events. It is possible to use the cached SDT events as other cached events, via perf probe --add "sdt_<provider>:<event>=<event>".

e.g.
----

  # perf buildid-cache --add /lib/libc-2.17.so
  # perf probe --cache --list | head -n 5
  /usr/lib/libc-2.17.so (a6fb821bdf53660eb2c29f778757aef294d3d392):
  sdt_libc:setjmp=setjmp
  sdt_libc:longjmp=longjmp
  sdt_libc:longjmp_target=longjmp_target
  sdt_libc:memory_heap_new=memory_heap_new
  # perf probe -x /usr/lib/libc-2.17.so \
    -a sdt_libc:memory_heap_new=memory_heap_new
  Added new event:
    sdt_libc:memory_heap_new (on memory_heap_new
   in /usr/lib/libc-2.17.so)

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

perf record -e sdt_libc:memory_heap_new -aR sleep 1

  # perf probe -l
    sdt_libc:memory_heap_new (on new_heap+183 in /usr/lib/libc-2.17.so)
  ----

Note that SDT event entries in probe-cache file is somewhat different from normal cached events. Normal one starts with "#", but SDTs are starting with "%".

---
Changes in v10:
- Update Documentation/perf-buildid-cache.txt too.
Changes in v4:
- Fix a bug to copy correct group name to entries.
- Fix to consolidate same-name entries.

---
 tools/perf/Documentation/perf-buildid-cache.txt |    3 +
 tools/perf/util/build-id.c                      |   27 ++++++++-
 tools/perf/util/probe-file.c                    |   67 ++++++++++++++++++++++-
 tools/perf/util/probe-file.h                    |    2 +
 4 files changed, 93 insertions(+), 6 deletions(-)
tools/perf/Documentation/perf-buildid-cache.txt +3 -0
index dd07b55..058064d 100644
@@ -15,6 +15,9 @@ DESCRIPTION
 This command manages the build-id cache. It can add, remove, update and purge
 files to/from the cache. In the future it should as well set upper limits for
 the space used by the cache, etc.
+This also scans the target binary for SDT (Statically Defined Tracing) and
+record it along with the buildid-cache, which will be used by perf-probe.
+For more details, see linkperf:perf-probe[1].
 
 OPTIONS
 -------
tools/perf/util/build-id.c +24 -3
index 14fbd37..68a67eb 100644
@@ -17,6 +17,7 @@
 #include "tool.h"
 #include "header.h"
 #include "vdso.h"
+#include "probe-file.h"
 
 
 static bool no_buildid_cache;
@@ -513,6 +514,23 @@ int build_id_cache__list_build_ids(const char *pathname,
 	return ret;
 }
 
+#ifdef HAVE_LIBELF_SUPPORT
+static void build_id_cache__add_sdt_cache(const char *sbuild_id,
+					  const char *realname)
+{
+	struct probe_cache *cache;
+
+	cache = probe_cache__new(sbuild_id);
+	if (!cache)
+		return;
+	if (probe_cache__scan_sdt(cache, realname) >= 0)
+		probe_cache__commit(cache);
+	probe_cache__delete(cache);
+}
+#else
+#define build_id_cache__add_sdt_cache(sbuild_id, realname) do { } while (0)
+#endif
+
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			  bool is_kallsyms, bool is_vdso)
 {
@@ -556,20 +574,23 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
+	/* Make a symbolic link */
 	if (!build_id_cache__linkname(sbuild_id, linkname, size))
 		goto out_free;
+
 	tmp = strrchr(linkname, '/');
 	*tmp = '\0';
-
 	if (access(linkname, X_OK) && mkdir_p(linkname, 0755))
 		goto out_free;
-
 	*tmp = '/';
 	tmp = dir_name + strlen(buildid_dir) - 5;
 	memcpy(tmp, "../..", 5);
-
 	if (symlink(tmp, linkname) == 0)
 		err = 0;
+
+	/* Update SDT cache */
+	build_id_cache__add_sdt_cache(sbuild_id, realname);
+
 out_free:
 	if (!is_kallsyms)
 		free(realname);
tools/perf/util/probe-file.c +64 -3

90 lines not on this page · raw message

tools/perf/util/probe-file.h +2 -0
index e6fd9b9..93aa193 100644
@@ -8,6 +8,7 @@
 /* Cache of probe definitions */
 struct probe_cache_entry {
 	struct list_head	list;
+	bool			sdt;
 	struct perf_probe_event pev;
 	char			*spev;
 	struct strlist		*tevlist;
@@ -35,6 +36,7 @@ struct probe_cache *probe_cache__new(const char *target);
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
 			   struct probe_trace_event *tevs, int ntevs);
+int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname);
 int probe_cache__commit(struct probe_cache *pcache);
 void probe_cache__delete(struct probe_cache *pcache);
 int probe_cache__remove_entries(struct probe_cache *pcache,
#28 lore raw

PATCH v10 14/23 perf probe: Accept %sdt and %cached event name

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

To improbe usability, support %[PROVIDER:]SDTEVENT format to add new probes on SDT and cached events.

e.g.
----

  # perf probe -x /lib/libc-2.17.so  %lll_lock_wait_private
  Added new event:
    sdt_libc:lll_lock_wait_private (on %lll_lock_wait_private in
  /usr/lib/libc-2.17.so)

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

perf record -e sdt_libc:lll_lock_wait_private -aR sleep 1

  # perf probe -l | more
    sdt_libc:lll_lock_wait_private (on __lll_lock_wait_private+21
   in /usr/lib/libc-2.17.so)
  ----

Note that this is not only for SDT events, but also normal events with event-name.

e.g. define "myevent" on cache (-n doesn't add the real probe)
----

  # perf probe -x ./perf --cache -n --add 'myevent=dso__load $params'
  ----
  Reuse the "myevent" from cache as below.
  ----
  # perf probe -x ./perf %myevent
  ----

---
Changes in v10:
- Update Documentation/perf-probe.txt to add a link about SDT.
Changes in v7:
- Fix a bug to return an error if no SDT/cached events found in cache.

---
 tools/perf/Documentation/perf-probe.txt |    9 +++
 tools/perf/util/probe-event.c           |   82 ++++++++++++++++++++++---------
 tools/perf/util/probe-event.h           |    1 
 tools/perf/util/probe-file.c            |    9 +++
 4 files changed, 76 insertions(+), 25 deletions(-)
tools/perf/Documentation/perf-probe.txt +8 -1
index 7a258e9..39e3870 100644
@@ -151,6 +151,8 @@ Probe points are defined by following syntax.
     3) Define event based on source file with lazy pattern
      [[GROUP:]EVENT=]SRC;PTN [ARG ...]
 
+    4) Pre-defined SDT events or cached event with name
+     %[PROVIDER:]SDTEVENT
 
 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe.
 Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the
@@ -158,6 +160,11 @@ modules.
 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition.  In addition, '@SRC' specifies a source file which has that function.
 It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
 'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
+'SDTEVENT' and 'PROVIDER' is the pre-defined event name which is defined by user SDT (Statically Defined Tracing) or the pre-cached probes with event name.
+Note that before using the SDT event, the target binary (on which SDT events are defined) must be scanned by linkperf:perf-buildid-cache[1] to make SDT events as cached events.
+
+For details of the SDT, see below.
+https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html
 
 PROBE ARGUMENT
 --------------
@@ -237,4 +244,4 @@ Add probes at malloc() function on libc
 
 SEE ALSO
 --------
-linkperf:perf-trace[1], linkperf:perf-record[1]
+linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
tools/perf/util/probe-event.c +58 -24

124 lines not on this page · raw message

tools/perf/util/probe-event.h +1 -0
index 432b690..e18ea9f 100644
@@ -85,6 +85,7 @@ struct perf_probe_event {
 	char			*group;	/* Group name */
 	struct perf_probe_point	point;	/* Probe point */
 	int			nargs;	/* Number of arguments */
+	bool			sdt;	/* SDT/cached event flag */
 	bool			uprobes;	/* Uprobe event flag */
 	char			*target;	/* Target binary */
 	struct perf_probe_arg	*args;	/* Arguments */
tools/perf/util/probe-file.c +9 -0
index 0fcd275..a222fb2 100644
@@ -538,6 +538,15 @@ probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
 		return NULL;
 
 	list_for_each_entry(entry, &pcache->list, list) {
+		if (pev->sdt) {
+			if (entry->pev.event &&
+			    streql(entry->pev.event, pev->event) &&
+			    (!pev->group ||
+			     streql(entry->pev.group, pev->group)))
+				goto found;
+
+			continue;
+		}
 		/* Hit if same event name or same command-string */
 		if ((pev->event &&
 		     (streql(entry->pev.group, pev->group) &&
#29 lore raw

PATCH v10 15/23 perf-list: Show SDT and pre-cached events

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

Show SDT and pre-cached events by perf-list with "sdt". This also shows the binary and build-id where the events are placed only when there are same name events on different binaries. e.g.
----

  # perf list sdt

List of pre-defined events (to be used in -e):

    sdt_libc:lll_futex_wake                            [SDT event]
    sdt_libc:lll_lock_wait_private                     [SDT event]
    sdt_libc:longjmp                                   [SDT event]
    sdt_libc:longjmp_target                            [SDT event]
  ...
    sdt_libstdcxx:rethrow@/usr/bin/gcc(0cc207fc4b27)   [SDT event]
    sdt_libstdcxx:rethrow@/usr/lib64/libstdc++.so.6.0.20(91c7a88fdf49)
    sdt_libstdcxx:throw@/usr/bin/gcc(0cc207fc4b27)     [SDT event]
    sdt_libstdcxx:throw@/usr/lib64/libstdc++.so.6.0.20(91c7a88fdf49)

----

The binary path and build-id are shown in below format;

<GROUP>:<EVENT>@<PATH>(<BUILD-ID>)

---
Changes in v9:
- Update list_usage too. (Thanks Hemant!)
Changes in v5:
- Fix a build error for minimal option.

Changes in v4:
- Update patch description.
- Change event list format.

---
 tools/perf/builtin-list.c      |    6 ++-
 tools/perf/util/parse-events.c |   83 ++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/parse-events.h |    2 +
 tools/perf/util/probe-file.h   |    9 ++++
 4 files changed, 99 insertions(+), 1 deletion(-)
tools/perf/builtin-list.c +5 -1
index 5e22db4..88ee419 100644
@@ -25,7 +25,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
 		OPT_END()
 	};
 	const char * const list_usage[] = {
-		"perf list [hw|sw|cache|tracepoint|pmu|event_glob]",
+		"perf list [hw|sw|cache|tracepoint|pmu|sdt|event_glob]",
 		NULL
 	};
 
@@ -62,6 +62,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
 			print_hwcache_events(NULL, raw_dump);
 		else if (strcmp(argv[i], "pmu") == 0)
 			print_pmu_events(NULL, raw_dump);
+		else if (strcmp(argv[i], "sdt") == 0)
+			print_sdt_events(NULL, NULL, raw_dump);
 		else if ((sep = strchr(argv[i], ':')) != NULL) {
 			int sep_idx;
 
@@ -76,6 +78,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
 
 			s[sep_idx] = '\0';
 			print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
+			print_sdt_events(s, s + sep_idx + 1, raw_dump);
 			free(s);
 		} else {
 			if (asprintf(&s, "*%s*", argv[i]) < 0) {
@@ -89,6 +92,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
 			print_hwcache_events(s, raw_dump);
 			print_pmu_events(s, raw_dump);
 			print_tracepoint_events(NULL, s, raw_dump);
+			print_sdt_events(NULL, s, raw_dump);
 			free(s);
 		}
 	}
tools/perf/util/parse-events.c +83 -0

101 lines not on this page · raw message

tools/perf/util/parse-events.h +2 -0
index 46c05cc..7325c9a 100644
@@ -183,6 +183,8 @@ void print_symbol_events(const char *event_glob, unsigned type,
 void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
 			     bool name_only);
 int print_hwcache_events(const char *event_glob, bool name_only);
+void print_sdt_events(const char *subsys_glob, const char *event_glob,
+		      bool name_only);
 int is_valid_tracepoint(const char *event_string);
 
 int valid_event_mount(const char *eventfs);
tools/perf/util/probe-file.h +9 -0
index 93aa193..ce98162 100644
@@ -22,6 +22,8 @@ struct probe_cache {
 #define PF_FL_UPROBE	1
 #define PF_FL_RW	2
 
+/* probe-file.c depends on libelf */
+#ifdef HAVE_LIBELF_SUPPORT
 int probe_file__open(int flag);
 int probe_file__open_both(int *kfd, int *ufd, int flag);
 struct strlist *probe_file__get_namelist(int fd);
@@ -46,4 +48,11 @@ struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
 struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
 					const char *group, const char *event);
 int probe_cache__show_all_caches(struct strfilter *filter);
+#else	/* ! HAVE_LIBELF_SUPPORT */
+static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
+{
+	return NULL;
+}
+#define probe_cache__delete(pcache) do {} while(0)
+#endif
 #endif
#30 lore raw

PATCH v10 16/23 perf-list: Skip SDTs placed in invalid binaries

In reply to Masami Hiramatsu

From: Masami Hiramatsu <[email protected]>

Skip SDTs placed in invalid (non-exist, or older version) binaries. Note that perf-probe --cache --list and perf-probe --cache --del still handle all the caches including invalid binaries.

---
Changes in v7:
- Validate build-id via sysfs if it is for kallsyms,
Changes in v4:
- Rename a parameter 'valid' to 'validonly' :)

---
 tools/perf/builtin-probe.c     |    2 +-
 tools/perf/util/build-id.c     |   33 ++++++++++++++++++++++++++++++++-
 tools/perf/util/build-id.h     |    2 +-
 tools/perf/util/parse-events.c |    2 +-
 tools/perf/util/probe-file.c   |    2 +-
 5 files changed, 36 insertions(+), 5 deletions(-)
tools/perf/builtin-probe.c +1 -1
index 8f61525..4a86aea 100644
@@ -370,7 +370,7 @@ static int del_perf_probe_caches(struct strfilter *filter)
 	struct str_node *nd;
 	int ret;
 
-	ret = build_id_cache__list_all(&bidlist);
+	ret = build_id_cache__list_all(&bidlist, false);
 	if (ret < 0) {
 		pr_debug("Failed to get buildids: %d\n", ret);
 		return ret;
tools/perf/util/build-id.c +32 -1
index 68a67eb..424f4e2 100644
@@ -209,6 +209,31 @@ out:
 	return ret;
 }
 
+/* Check if the given build_id cache is valid on current running system */
+static bool build_id_cache__valid_id(char *sbuild_id)
+{
+	char real_sbuild_id[SBUILD_ID_SIZE] = "";
+	char *pathname;
+	int ret = 0;
+	bool result = false;
+
+	pathname = build_id_cache__origname(sbuild_id);
+	if (!pathname)
+		return false;
+
+	if (!strcmp(pathname, DSO__NAME_KALLSYMS))
+		ret = sysfs__sprintf_build_id("/", real_sbuild_id);
+	else if (pathname[0] == '/')
+		ret = filename__sprintf_build_id(pathname, real_sbuild_id);
+	else
+		ret = -EINVAL;	/* Should we support other special DSO cache? */
+	if (ret >= 0)
+		result = (strcmp(sbuild_id, real_sbuild_id) == 0);
+	free(pathname);
+
+	return result;
+}
+
 static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
 {
 	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
@@ -420,7 +445,7 @@ void disable_buildid_cache(void)
 	no_buildid_cache = true;
 }
 
-int build_id_cache__list_all(struct strlist **result)
+int build_id_cache__list_all(struct strlist **result, bool validonly)
 {
 	struct strlist *toplist, *list, *bidlist;
 	struct str_node *nd, *nd2;
@@ -428,6 +453,10 @@ int build_id_cache__list_all(struct strlist **result)
 	char sbuild_id[SBUILD_ID_SIZE];
 	int ret = 0;
 
+	/* for filename__ functions */
+	if (validonly)
+		symbol__init(NULL);
+
 	/* Open the top-level directory */
 	if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
 		return -errno;
@@ -457,6 +486,8 @@ int build_id_cache__list_all(struct strlist **result)
 					nd->s, nd2->s);
 				continue;
 			}
+			if (validonly && !build_id_cache__valid_id(sbuild_id))
+				continue;
 			strlist__add(bidlist, sbuild_id);
 		}
 		strlist__delete(list);
tools/perf/util/build-id.h +1 -1
index c05503e..480600b 100644
@@ -34,7 +34,7 @@ char *build_id_cache__origname(const char *sbuild_id);
 char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 			       bool is_kallsyms, bool is_vdso);
-int build_id_cache__list_all(struct strlist **result);
+int build_id_cache__list_all(struct strlist **result, bool validonly);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
tools/perf/util/parse-events.c +1 -1
index 4c7437f..5932b58 100644
@@ -2002,7 +2002,7 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
 		pr_debug("Failed to allocate new strlist for SDT\n");
 		return;
 	}
-	ret = build_id_cache__list_all(&bidlist);
+	ret = build_id_cache__list_all(&bidlist, true);
 	if (ret < 0) {
 		pr_debug("Failed to get buildids: %d\n", ret);
 		return;
tools/perf/util/probe-file.c +1 -1
index a222fb2..4d8ba72 100644
@@ -781,7 +781,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 	pr_debug("list cache with filter: %s\n", buf);
 	free(buf);
 
-	ret = build_id_cache__list_all(&bidlist);
+	ret = build_id_cache__list_all(&bidlist, false);
 	if (ret < 0) {
 		pr_debug("Failed to get buildids: %d\n", ret);
 		return ret == -ENOENT ? 0 : ret;
#31 lore raw

PATCH v10 17/23 perf: probe-cache: Add for_each_probe_cache_entry() wrapper

In reply to Masami Hiramatsu

Add for_each_probe_cache_entry() wrapper macro
for hiding list in probe_cache.

---
Changes in v10:
- Splitted from "perf probe: Allow wildcard for cached events"

---
 tools/perf/util/probe-file.c |    8 ++++----
 tools/perf/util/probe-file.h |    2 ++
 2 files changed, 6 insertions(+), 4 deletions(-)
tools/perf/util/probe-file.c +4 -4
index 4d8ba72..7b60380 100644
@@ -537,7 +537,7 @@ probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
 	if (!cmd)
 		return NULL;
 
-	list_for_each_entry(entry, &pcache->list, list) {
+	for_each_probe_cache_entry(entry, pcache) {
 		if (pev->sdt) {
 			if (entry->pev.event &&
 			    streql(entry->pev.event, pev->event) &&
@@ -567,7 +567,7 @@ probe_cache__find_by_name(struct probe_cache *pcache,
 {
 	struct probe_cache_entry *entry = NULL;
 
-	list_for_each_entry(entry, &pcache->list, list) {
+	for_each_probe_cache_entry(entry, pcache) {
 		/* Hit if same event name or same command-string */
 		if (streql(entry->pev.group, group) &&
 		    streql(entry->pev.event, event))
@@ -720,7 +720,7 @@ int probe_cache__commit(struct probe_cache *pcache)
 	if (ret < 0)
 		goto out;
 
-	list_for_each_entry(entry, &pcache->list, list) {
+	for_each_probe_cache_entry(entry, pcache) {
 		ret = probe_cache_entry__write(entry, pcache->fd);
 		pr_debug("Cache committed: %d\n", ret);
 		if (ret < 0)
@@ -761,7 +761,7 @@ static int probe_cache__show_entries(struct probe_cache *pcache,
 {
 	struct probe_cache_entry *entry;
 
-	list_for_each_entry(entry, &pcache->list, list) {
+	for_each_probe_cache_entry(entry, pcache) {
 		if (probe_cache_entry__compare(entry, filter))
 			printf("%s\n", entry->spev);
 	}
tools/perf/util/probe-file.h +2 -0
index ce98162..6271ec6 100644
@@ -21,6 +21,8 @@ struct probe_cache {
 
 #define PF_FL_UPROBE	1
 #define PF_FL_RW	2
+#define for_each_probe_cache_entry(entry, pcache) \
+	list_for_each_entry(entry, &pcache->list, list)
 
 /* probe-file.c depends on libelf */
 #ifdef HAVE_LIBELF_SUPPORT
#32 lore raw

PATCH v10 18/23 perf probe: Allow wildcard for cached events

In reply to Masami Hiramatsu

Allo glob wildcard for reusing cached/SDT events. E.g.

  # perf probe -x /usr/lib64/libc-2.20.so -a %sdt_libc:\*

This example adds probes for all SDT in libc.
Note that the SDTs must have been scanned by perf buildid-cache.

---
Changes in v10:
- Split off bugfix, adding interface, and target search patches.
- Do not export clear_probe_trace_events().
Changes in v7:
- Continue to search caches if a build-id cache has no probe cache.
- Make probe_cache__open() to accept DSO__NAME_KALLSYMS for kernel.
- Fix to add probes correctly when a wildcard matchs both of uprobes and kprobes.
Changes in v5.1:
- Fix a SEGV bug when a group name is omitted. (Thanks Hemant!)

---
 tools/perf/util/probe-event.c |  107 +++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/probe-file.c  |   38 ++++++++++++---
 tools/perf/util/probe-file.h  |    3 +
 3 files changed, 138 insertions(+), 10 deletions(-)
tools/perf/util/probe-event.c +103 -4
index 443340f..f692fd5 100644
@@ -1206,7 +1206,7 @@ static int parse_perf_probe_event_name(char **arg, struct perf_probe_event *pev)
 	ptr = strchr(*arg, ':');
 	if (ptr) {
 		*ptr = '\0';
-		if (!is_c_func_name(*arg))
+		if (!pev->sdt && !is_c_func_name(*arg))
 			goto ng_name;
 		pev->group = strdup(*arg);
 		if (!pev->group)
@@ -1214,7 +1214,7 @@ static int parse_perf_probe_event_name(char **arg, struct perf_probe_event *pev)
 		*arg = ptr + 1;
 	} else
 		pev->group = NULL;
-	if (!is_c_func_name(*arg)) {
+	if (!pev->sdt && !is_c_func_name(*arg)) {
 ng_name:
 		semantic_error("%s is bad for event name -it must "
 			       "follow C symbol-naming rule.\n", *arg);
@@ -1644,6 +1644,7 @@ int parse_probe_trace_command(const char *cmd, struct probe_trace_event *tev)
 			ret = -ENOMEM;
 			goto out;
 		}
+		tev->uprobes = (tp->module[0] == '/');
 		p++;
 	} else
 		p = argv[1];
@@ -2518,7 +2519,7 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
 	int ret;
 
 	/* If probe_event or trace_event already have the name, reuse it */
-	if (pev->event)
+	if (pev->event && !pev->sdt)
 		event = pev->event;
 	else if (tev->event)
 		event = tev->event;
@@ -2531,7 +2532,7 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
 		else
 			event = tev->point.realname;
 	}
-	if (pev->group)
+	if (pev->group && !pev->sdt)
 		group = pev->group;
 	else if (tev->group)
 		group = tev->group;
@@ -2894,6 +2895,100 @@ errout:
 
 bool __weak arch__prefers_symtab(void) { return false; }
 
+/* Concatinate two arrays */
+static void *memcat(void *a, size_t sz_a, void *b, size_t sz_b)
+{
+	void *ret;
+
+	ret = malloc(sz_a + sz_b);
+	if (ret) {
+		memcpy(ret, a, sz_a);
+		memcpy(ret + sz_a, b, sz_b);
+	}
+	return ret;
+}
+
+static int
+concat_probe_trace_events(struct probe_trace_event **tevs, int *ntevs,
+			  struct probe_trace_event **tevs2, int ntevs2)
+{
+	struct probe_trace_event *new_tevs;
+	int ret = 0;
+
+	if (ntevs == 0) {
+		*tevs = *tevs2;
+		*ntevs = ntevs2;
+		*tevs2 = NULL;
+		return 0;
+	}
+
+	if (*ntevs + ntevs2 > probe_conf.max_probes)
+		ret = -E2BIG;
+	else {
+		/* Concatinate the array of probe_trace_event */
+		new_tevs = memcat(*tevs, (*ntevs) * sizeof(**tevs),
+				  *tevs2, ntevs2 * sizeof(**tevs2));
+		if (!new_tevs)
+			ret = -ENOMEM;
+		else {
+			free(*tevs);
+			*tevs = new_tevs;
+			*ntevs += ntevs2;
+		}
+	}
+	if (ret < 0)
+		clear_probe_trace_events(*tevs2, ntevs2);
+	zfree(tevs2);
+
+	return ret;
+}
+
+/*
+ * Try to find probe_trace_event from given probe caches. Return the number
+ * of cached events found, if an error occurs return the error.
+ */
+static int find_cached_events(struct perf_probe_event *pev,
+			      struct probe_trace_event **tevs,
+			      const char *target)
+{
+	struct probe_cache *cache;
+	struct probe_cache_entry *entry;
+	struct probe_trace_event *tmp_tevs = NULL;
+	int ntevs = 0;
+	int ret = 0;
+
+	cache = probe_cache__new(target);
+	/* Return 0 ("not found") if the target has no probe cache. */
+	if (!cache)
+		return 0;
+
+	for_each_probe_cache_entry(entry, cache) {
+		/* Skip the cache entry which has no name */
+		if (!entry->pev.event || !entry->pev.group)
+			continue;
+		if ((!pev->group || strglobmatch(entry->pev.group, pev->group)) &&
+		    strglobmatch(entry->pev.event, pev->event)) {
+			ret = probe_cache_entry__get_event(entry, &tmp_tevs);
+			if (ret > 0)
+				ret = concat_probe_trace_events(tevs, &ntevs,
+								&tmp_tevs, ret);
+			if (ret < 0)
+				break;
+		}
+	}
+	probe_cache__delete(cache);
+	if (ret < 0) {
+		clear_probe_trace_events(*tevs, ntevs);
+		zfree(tevs);
+	} else {
+		ret = ntevs;
+		if (ntevs > 0 && target && target[0] == '/')
+			pev->uprobes = true;
+	}
+
+	return ret;
+}
+
 static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 					      struct probe_trace_event **tevs)
 {
@@ -2903,6 +2998,10 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 	struct str_node *node;
 	int ret, i;
 
+	if (pev->sdt)
+		/* For SDT/cached events, we use special search functions */
+		return find_cached_events(pev, tevs, pev->target);
+
 	cache = probe_cache__new(pev->target);
 	if (!cache)
 		return 0;
tools/perf/util/probe-file.c +32 -6
index 7b60380..3a1d42a 100644
@@ -360,13 +360,38 @@ probe_cache_entry__new(struct perf_probe_event *pev)
 	return ret;
 }
 
-/* For the kernel probe caches, pass target = NULL */
+int probe_cache_entry__get_event(struct probe_cache_entry *entry,
+				 struct probe_trace_event **tevs)
+{
+	struct probe_trace_event *tev;
+	struct str_node *node;
+	int ret, i;
+
+	ret = strlist__nr_entries(entry->tevlist);
+	if (ret > probe_conf.max_probes)
+		return -E2BIG;
+
+	*tevs = zalloc(ret * sizeof(*tev));
+	if (!*tevs)
+		return -ENOMEM;
+
+	i = 0;
+	strlist__for_each(node, entry->tevlist) {
+		tev = &(*tevs)[i++];
+		ret = parse_probe_trace_command(node->s, tev);
+		if (ret < 0)
+			break;
+	}
+	return i;
+}
+
+/* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
 static int probe_cache__open(struct probe_cache *pcache, const char *target)
 {
 	char cpath[PATH_MAX];
 	char sbuildid[SBUILD_ID_SIZE];
 	char *dir_name = NULL;
-	bool is_kallsyms = !target;
+	bool is_kallsyms = false;
 	int ret, fd;
 
 	if (target && build_id_cache__cached(target)) {
@@ -376,12 +401,13 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		goto found;
 	}
 
-	if (target)
-		ret = filename__sprintf_build_id(target, sbuildid);
-	else {
+	if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) {
 		target = DSO__NAME_KALLSYMS;
+		is_kallsyms = true;
 		ret = sysfs__sprintf_build_id("/", sbuildid);
-	}
+	} else
+		ret = filename__sprintf_build_id(target, sbuildid);
+
 	if (ret < 0) {
 		pr_debug("Failed to get build-id from %s.\n", target);
 		return ret;
tools/perf/util/probe-file.h +3 -0
index 6271ec6..0faf083 100644
@@ -36,6 +36,9 @@ int probe_file__get_events(int fd, struct strfilter *filter,
 				  struct strlist *plist);
 int probe_file__del_strlist(int fd, struct strlist *namelist);
 
+int probe_cache_entry__get_event(struct probe_cache_entry *entry,
+				 struct probe_trace_event **tevs);
+
 struct probe_cache *probe_cache__new(const char *target);
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
#33 lore raw

PATCH v10 19/23 perf probe: Search SDT/cached event from all probe caches

In reply to Masami Hiramatsu

Search SDT/cached event from all probe caches if user doesn't pass any binary. With this, we don't have to specify target binary for SDT and named cached events (which start with %).

E.g. without this, a target binary must be passed with -x.

 # perf probe -x /usr/lib64/libc-2.20.so -a %sdt_libc:\*

With this change, we don't need it anymore.

 # perf probe -a %sdt_libc:\*

---
Changes from v10:
- Splitted from "perf probe: Allow wildcard for cached events"

---
 tools/perf/util/probe-event.c |  104 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 85 insertions(+), 19 deletions(-)
tools/perf/util/probe-event.c +85 -19
index f692fd5..d30c307 100644
@@ -2557,41 +2557,60 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
 	return 0;
 }
 
-static int __add_probe_trace_events(struct perf_probe_event *pev,
-				     struct probe_trace_event *tevs,
-				     int ntevs, bool allow_suffix)
+static int __open_probe_file_and_namelist(bool uprobe,
+					  struct strlist **namelist)
 {
-	int i, fd, ret;
-	struct probe_trace_event *tev = NULL;
-	struct probe_cache *cache = NULL;
-	struct strlist *namelist;
+	int fd;
 
-	fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
+	fd = probe_file__open(PF_FL_RW | (uprobe ? PF_FL_UPROBE : 0));
 	if (fd < 0)
 		return fd;
 
 	/* Get current event names */
-	namelist = probe_file__get_namelist(fd);
-	if (!namelist) {
+	*namelist = probe_file__get_namelist(fd);
+	if (!(*namelist)) {
 		pr_debug("Failed to get current event list.\n");
-		ret = -ENOMEM;
-		goto close_out;
+		close(fd);
+		return -ENOMEM;
 	}
+	return fd;
+}
+
+static int __add_probe_trace_events(struct perf_probe_event *pev,
+				     struct probe_trace_event *tevs,
+				     int ntevs, bool allow_suffix)
+{
+	int i, fd[2] = {-1, -1}, up, ret;
+	struct probe_trace_event *tev = NULL;
+	struct probe_cache *cache = NULL;
+	struct strlist *namelist[2] = {NULL, NULL};
+
+	up = pev->uprobes ? 1 : 0;
+	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
+	if (fd[up] < 0)
+		return fd[up];
 
 	ret = 0;
 	for (i = 0; i < ntevs; i++) {
 		tev = &tevs[i];
+		up = tev->uprobes ? 1 : 0;
+		if (fd[up] == -1) {	/* Open the kprobe/uprobe_events */
+			fd[up] = __open_probe_file_and_namelist(up,
+								&namelist[up]);
+			if (fd[up] < 0)
+				goto close_out;
+		}
 		/* Skip if the symbol is out of .text or blacklisted */
 		if (!tev->point.symbol && !pev->uprobes)
 			continue;
 
 		/* Set new name for tev (and update namelist) */
-		ret = probe_trace_event__set_name(tev, pev, namelist,
+		ret = probe_trace_event__set_name(tev, pev, namelist[up],
 						  allow_suffix);
 		if (ret < 0)
 			break;
 
-		ret = probe_file__add_event(fd, tev);
+		ret = probe_file__add_event(fd[up], tev);
 		if (ret < 0)
 			break;
 
@@ -2614,9 +2633,12 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 		}
 	}
 
-	strlist__delete(namelist);
 close_out:
-	close(fd);
+	for (up = 0; up < 2; up++) {
+		strlist__delete(namelist[up]);
+		if (fd[up] >= 0)
+			close(fd[up]);
+	}
 	return ret;
 }
 
@@ -2989,6 +3011,47 @@ static int find_cached_events(struct perf_probe_event *pev,
 	return ret;
 }
 
+/* Try to find probe_trace_event from all probe caches */
+static int find_cached_events_all(struct perf_probe_event *pev,
+				   struct probe_trace_event **tevs)
+{
+	struct probe_trace_event *tmp_tevs = NULL;
+	struct strlist *bidlist;
+	struct str_node *nd;
+	char *pathname;
+	int ntevs = 0;
+	int ret;
+
+	/* Get the buildid list of all valid caches */
+	ret = build_id_cache__list_all(&bidlist, true);
+	if (ret < 0) {
+		pr_debug("Failed to get buildids: %d\n", ret);
+		return ret;
+	}
+
+	ret = 0;
+	strlist__for_each(nd, bidlist) {
+		pathname = build_id_cache__origname(nd->s);
+		ret = find_cached_events(pev, &tmp_tevs, pathname);
+		/* In the case of cnt == 0, we just skip it */
+		if (ret > 0)
+			ret = concat_probe_trace_events(tevs, &ntevs,
+							&tmp_tevs, ret);
+		free(pathname);
+		if (ret < 0)
+			break;
+	}
+	strlist__delete(bidlist);
+
+	if (ret < 0) {
+		clear_probe_trace_events(*tevs, ntevs);
+		zfree(tevs);
+	} else
+		ret = ntevs;
+
+	return ret;
+}
+
 static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 					      struct probe_trace_event **tevs)
 {
@@ -2998,10 +3061,13 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 	struct str_node *node;
 	int ret, i;
 
-	if (pev->sdt)
+	if (pev->sdt) {
 		/* For SDT/cached events, we use special search functions */
-		return find_cached_events(pev, tevs, pev->target);
-
+		if (!pev->target)
+			return find_cached_events_all(pev, tevs);
+		else
+			return find_cached_events(pev, tevs, pev->target);
+	}
 	cache = probe_cache__new(pev->target);
 	if (!cache)
 		return 0;
#34 lore raw

PATCH v10 20/23 perf probe: Support @BUILDID or @FILE suffix for SDT events

In reply to Masami Hiramatsu

Support @BUILDID or @FILE suffix for SDT events. This allows perf to add probes on SDTs/pre-cached events on given FILE or the file which has given BUILDID (also, this complements BUILDID.)
For example, both gcc and libstdc++ has same SDTs as below. If you would like to add a probe on sdt_libstdcxx:catch on gcc, you can do as below.

----

  # perf list sdt | tail -n 6
    sdt_libstdcxx:catch@/usr/bin/gcc(0cc207fc4b27)     [SDT event]
    sdt_libstdcxx:catch@/usr/lib64/libstdc++.so.6.0.20(91c7a88fdf49)
    sdt_libstdcxx:rethrow@/usr/bin/gcc(0cc207fc4b27)   [SDT event]
    sdt_libstdcxx:rethrow@/usr/lib64/libstdc++.so.6.0.20(91c7a88fdf49)
    sdt_libstdcxx:throw@/usr/bin/gcc(0cc207fc4b27)     [SDT event]
    sdt_libstdcxx:throw@/usr/lib64/libstdc++.so.6.0.20(91c7a88fdf49)
  # perf probe -a %sdt_libstdcxx:catch@0cc
  Added new event:
    sdt_libstdcxx:catch  (on %catch in /usr/bin/gcc)

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

perf record -e sdt_libstdcxx:catch -aR sleep 1
----

---
 tools/perf/util/build-id.c    |   40 ++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/build-id.h    |    1 +
 tools/perf/util/probe-event.c |   16 ++++++++++++++--
 3 files changed, 55 insertions(+), 2 deletions(-)
tools/perf/util/build-id.c +40 -0
index 424f4e2..c32931c 100644
@@ -504,6 +504,46 @@ out:
 	return ret;
 }
 
+static bool str_is_build_id(const char *maybe_sbuild_id, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (!isxdigit(maybe_sbuild_id[i]))
+			return false;
+	}
+	return true;
+}
+
+/* Return the valid complete build-id */
+char *build_id_cache__complement(const char *incomplete_sbuild_id)
+{
+	struct strlist *list;
+	struct str_node *nd, *cand = NULL;
+	char *sbuild_id = NULL;
+	size_t len = strlen(incomplete_sbuild_id);
+
+	if (len >= SBUILD_ID_SIZE ||
+	    !str_is_build_id(incomplete_sbuild_id, len) ||
+	    build_id_cache__list_all(&list, true) < 0)
+		return NULL;
+
+	strlist__for_each(nd, list) {
+		if (strncmp(nd->s, incomplete_sbuild_id, len) != 0)
+			continue;
+		if (cand) {	/* Error: There are more than 2 candidates. */
+			cand = NULL;
+			break;
+		}
+		cand = nd;
+	}
+	if (cand)
+		sbuild_id = strdup(cand->s);
+	strlist__delete(list);
+
+	return sbuild_id;
+}
+
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 			       bool is_kallsyms, bool is_vdso)
 {
tools/perf/util/build-id.h +1 -0
index 480600b..9bd6d0b 100644
@@ -35,6 +35,7 @@ char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 			       bool is_kallsyms, bool is_vdso);
 int build_id_cache__list_all(struct strlist **result, bool validonly);
+char *build_id_cache__complement(const char *incomplete_sbuild_id);
 int build_id_cache__list_build_ids(const char *pathname,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
tools/perf/util/probe-event.c +14 -2
index d30c307..6d3398b 100644
@@ -1253,8 +1253,20 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 	ptr = strpbrk(arg, ";=@+%");
 	if (pev->sdt) {
 		if (ptr) {
-			semantic_error("%s must contain only an SDT event name.\n", arg);
-			return -EINVAL;
+			if (*ptr != '@') {
+				semantic_error("%s must contain only an SDT event name.\n", arg);
+				return -EINVAL;
+			}
+			/* This must be a target file name or build id */
+			tmp = build_id_cache__complement(ptr + 1);
+			if (tmp) {
+				pev->target = build_id_cache__origname(tmp);
+				free(tmp);
+			} else
+				pev->target = strdup(ptr + 1);
+			if (!pev->target)
+				return -ENOMEM;
+			*ptr = '\0';
 		}
 		ret = parse_perf_probe_event_name(&arg, pev);
 		if (ret == 0) {
#35 lore raw

PATCH v10 21/23 perf probe: Support a special SDT probe format

In reply to Masami Hiramatsu

Support a special SDT probe format which can omit the '%' prefix only if the SDT group name starts with "sdt_". So, for example both of "%sdt_libc:setjump" and "sdt_libc:setjump" are acceptable for perf probe --add.

---
 tools/perf/Documentation/perf-probe.txt |    4 +++-
 tools/perf/util/probe-event.c           |   12 ++++++++++--
 2 files changed, 13 insertions(+), 3 deletions(-)
tools/perf/Documentation/perf-probe.txt +3 -1
index 39e3870..736da44 100644
@@ -152,7 +152,9 @@ Probe points are defined by following syntax.
      [[GROUP:]EVENT=]SRC;PTN [ARG ...]
 
     4) Pre-defined SDT events or cached event with name
-     %[PROVIDER:]SDTEVENT
+     %[sdt_PROVIDER:]SDTEVENT
+     or,
+     sdt_PROVIDER:SDTEVENT
 
 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe.
 Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the
tools/perf/util/probe-event.c +10 -2
index 6d3398b..7c43c6a 100644
@@ -1245,9 +1245,17 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 	if (!arg)
 		return -EINVAL;
 
-	if (arg[0] == '%') {
+	/*
+	 * If the probe point starts with '%',
+	 * or starts with "sdt_" and has a ':' but no '=',
+	 * then it should be a SDT/cached probe point.
+	 */
+	if (arg[0] == '%' ||
+	    (!strncmp(arg, "sdt_", 4) &&
+	     !!strchr(arg, ':') && !strchr(arg, '='))) {
 		pev->sdt = true;
-		arg++;
+		if (arg[0] == '%')
+			arg++;
 	}
 
 	ptr = strpbrk(arg, ";=@+%");
#36 lore raw

PATCH v10 22/23 perf build: Add sdt feature detection

In reply to Masami Hiramatsu

Will be used to define SDT events in perf test code.

---
 tools/perf/Makefile.perf   |    3 +++
 tools/perf/config/Makefile |   10 ++++++++++
 tools/perf/tests/make      |    3 ++-
 3 files changed, 15 insertions(+), 1 deletion(-)
tools/build/Makefile.feature +2 -1
index 57c8f98..6bc7767 100644
@@ -60,7 +60,8 @@ FEATURE_TESTS_BASIC :=			\
 	zlib				\
 	lzma				\
 	get_cpuid			\
-	bpf
+	bpf				\
+	sdt
 
 # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
 # of all feature tests
tools/build/feature/Makefile +5 -1
index 3d88f09..82d357f 100644
@@ -43,7 +43,8 @@ FILES=					\
 	test-zlib.bin			\
 	test-lzma.bin			\
 	test-bpf.bin			\
-	test-get_cpuid.bin
+	test-get_cpuid.bin		\
+	test-sdt.bin
 
 FILES := $(addprefix $(OUTPUT),$(FILES))
 
@@ -205,6 +206,9 @@ $(OUTPUT)test-get_cpuid.bin:
 $(OUTPUT)test-bpf.bin:
 	$(BUILD)
 
+$(OUTPUT)test-sdt.bin:
+	$(BUILD)
+
 -include $(OUTPUT)*.d
 
 ###############################
tools/build/feature/test-all.c +5 -0
index a282e8c..f538c5f 100644
@@ -137,6 +137,10 @@
 # include "test-libcrypto.c"
 #undef main
 
+#define main main_test_sdt
+# include "test-sdt.c"
+#undef main
+
 int main(int argc, char *argv[])
 {
 	main_test_libpython();
@@ -168,6 +172,7 @@ int main(int argc, char *argv[])
 	main_test_get_cpuid();
 	main_test_bpf();
 	main_test_libcrypto();
+	main_test_sdt();
 
 	return 0;
 }
tools/build/feature/test-sdt.c new file +7 -0
new file mode 100644
index 0000000..e4531a6
@@ -0,0 +1,7 @@
+#include <sys/sdt.h>
+
+int main(void)
+{
+	DTRACE_PROBE(provider, name);
+	return 0;
+}
tools/perf/Makefile.perf +3 -0
index bde8cba..a818388 100644
@@ -81,6 +81,9 @@ include ../scripts/utilities.mak
 #
 # Define NO_LIBBPF if you do not want BPF support
 #
+# Define NO_SDT if you do not want to define SDT event in perf tools,
+# note that it doesn't disable SDT scanning support.
+#
 # Define FEATURES_DUMP to provide features detection dump file
 # and bypass the feature detection
 
tools/perf/config/Makefile +10 -0
index 1e46277..641a82a 100644
@@ -331,6 +331,16 @@ ifndef NO_LIBELF
   endif # NO_LIBBPF
 endif # NO_LIBELF
 
+ifndef NO_SDT
+  ifneq ($(feature-sdt), 1)
+    msg := $(warning No sys/sdt.h found, no SDT events are defined, please install systemtap-sdt-devel or systemtap-sdt-dev);
+    NO_SDT := 1;
+  else
+    CFLAGS += -DHAVE_SDT_EVENT
+    $(call detected,CONFIG_SDT_EVENT)
+  endif
+endif
+
 ifdef PERF_HAVE_JITDUMP
   ifndef NO_DWARF
     $(call detected,CONFIG_JITDUMP)
tools/perf/tests/make +2 -1
index cac15d9..ac801be 100644
@@ -81,6 +81,7 @@ make_no_libbionic   := NO_LIBBIONIC=1
 make_no_auxtrace    := NO_AUXTRACE=1
 make_no_libbpf	    := NO_LIBBPF=1
 make_no_libcrypto   := NO_LIBCRYPTO=1
+make_no_sdt	    := NO_SDT=1
 make_tags           := tags
 make_cscope         := cscope
 make_help           := help
@@ -104,7 +105,7 @@ make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
 make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
 make_minimal        += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
 make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
-make_minimal        += NO_LIBCRYPTO=1
+make_minimal        += NO_LIBCRYPTO=1 NO_SDT=1
 
 # $(run) contains all available tests
 run := make_pure
#37 lore raw

PATCH v10 22/23 perf build: Add sdt feature detection

In reply to Masami Hiramatsu

Will be used to define SDT events in perf test code.

---
 tools/perf/Makefile.perf   |    3 +++
 tools/perf/config/Makefile |   10 ++++++++++
 tools/perf/tests/make      |    3 ++-
 3 files changed, 15 insertions(+), 1 deletion(-)
tools/build/Makefile.feature +2 -1
index 57c8f98..6bc7767 100644
@@ -60,7 +60,8 @@ FEATURE_TESTS_BASIC :=			\
 	zlib				\
 	lzma				\
 	get_cpuid			\
-	bpf
+	bpf				\
+	sdt
 
 # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
 # of all feature tests
tools/build/feature/Makefile +5 -1
index 3d88f09..82d357f 100644
@@ -43,7 +43,8 @@ FILES=					\
 	test-zlib.bin			\
 	test-lzma.bin			\
 	test-bpf.bin			\
-	test-get_cpuid.bin
+	test-get_cpuid.bin		\
+	test-sdt.bin
 
 FILES := $(addprefix $(OUTPUT),$(FILES))
 
@@ -205,6 +206,9 @@ $(OUTPUT)test-get_cpuid.bin:
 $(OUTPUT)test-bpf.bin:
 	$(BUILD)
 
+$(OUTPUT)test-sdt.bin:
+	$(BUILD)
+
 -include $(OUTPUT)*.d
 
 ###############################
tools/build/feature/test-all.c +5 -0
index a282e8c..f538c5f 100644
@@ -137,6 +137,10 @@
 # include "test-libcrypto.c"
 #undef main
 
+#define main main_test_sdt
+# include "test-sdt.c"
+#undef main
+
 int main(int argc, char *argv[])
 {
 	main_test_libpython();
@@ -168,6 +172,7 @@ int main(int argc, char *argv[])
 	main_test_get_cpuid();
 	main_test_bpf();
 	main_test_libcrypto();
+	main_test_sdt();
 
 	return 0;
 }
tools/build/feature/test-sdt.c new file +7 -0
new file mode 100644
index 0000000..e4531a6
@@ -0,0 +1,7 @@
+#include <sys/sdt.h>
+
+int main(void)
+{
+	DTRACE_PROBE(provider, name);
+	return 0;
+}
tools/perf/Makefile.perf +3 -0
index bde8cba..a818388 100644
@@ -81,6 +81,9 @@ include ../scripts/utilities.mak
 #
 # Define NO_LIBBPF if you do not want BPF support
 #
+# Define NO_SDT if you do not want to define SDT event in perf tools,
+# note that it doesn't disable SDT scanning support.
+#
 # Define FEATURES_DUMP to provide features detection dump file
 # and bypass the feature detection
 
tools/perf/config/Makefile +10 -0
index 1e46277..641a82a 100644
@@ -331,6 +331,16 @@ ifndef NO_LIBELF
   endif # NO_LIBBPF
 endif # NO_LIBELF
 
+ifndef NO_SDT
+  ifneq ($(feature-sdt), 1)
+    msg := $(warning No sys/sdt.h found, no SDT events are defined, please install systemtap-sdt-devel or systemtap-sdt-dev);
+    NO_SDT := 1;
+  else
+    CFLAGS += -DHAVE_SDT_EVENT
+    $(call detected,CONFIG_SDT_EVENT)
+  endif
+endif
+
 ifdef PERF_HAVE_JITDUMP
   ifndef NO_DWARF
     $(call detected,CONFIG_JITDUMP)
tools/perf/tests/make +2 -1
index cac15d9..ac801be 100644
@@ -81,6 +81,7 @@ make_no_libbionic   := NO_LIBBIONIC=1
 make_no_auxtrace    := NO_AUXTRACE=1
 make_no_libbpf	    := NO_LIBBPF=1
 make_no_libcrypto   := NO_LIBCRYPTO=1
+make_no_sdt	    := NO_SDT=1
 make_tags           := tags
 make_cscope         := cscope
 make_help           := help
@@ -104,7 +105,7 @@ make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
 make_minimal        += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
 make_minimal        += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
 make_minimal        += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
-make_minimal        += NO_LIBCRYPTO=1
+make_minimal        += NO_LIBCRYPTO=1 NO_SDT=1
 
 # $(run) contains all available tests
 run := make_pure
#38 lore raw
In reply to Masami Hiramatsu
Quoted 11 lines · On Wed, 8 Jun 2016 18:29:00 +0900 Masami Hiramatsu <[email protected]> wrote:

Hi,

Here is the 10th version of the patchset for probe-cache and initial SDT support.

This version fixes a bug and split big patches into small subsets according to Namhyung's comment.
Also I've added a testcase for SDT. In this version, the number of patches are increased, but basically, only 3 patches (bugfix, and adding test) are new.

Oops, the last 2 patches has no signed-off, I resend it.

Quoted 67 lines

Changes in v10:
- [1/23] Fix a bug in rm_rf().
- [2/23,] Separate a big patch into 3 sub patches.
- [3/23 - 7/23] separate a big patch into 5 sub patches.
- [13/23, 14/23] Update documents for SDT explanations.
- [22/23, 23/23] Add a test case for SDT and update build config to detect sys/sdt.h.

Thank you,

---

Hemant Kumar (1):
perf/sdt: ELF support for SDT

Masami Hiramatsu (22):
perf: util: Fix rm_rf() to handle non-regular files correctly
perf-probe: Fix to add NULL check for strndup
perf-buildid: Rename and export build_id_cache__cachedir() perf probe: Add perf_probe_event__copy()
perf probe: Recover and export synthesize_perf_probe_point() perf probe-file: Introduce perf_cache interfaces
perf probe: Add --cache option to cache the probe definitions perf probe: Use cache entry if possible
perf probe: Show all cached probes
perf probe: Remove caches when --cache is given
perf probe: Add group name support
perf buildid-cache: Scan and import user SDT events to probe cache perf probe: Accept %sdt and %cached event name
perf-list: Show SDT and pre-cached events
perf-list: Skip SDTs placed in invalid binaries
perf: probe-cache: Add for_each_probe_cache_entry() wrapper perf probe: Allow wildcard for cached events
perf probe: Search SDT/cached event from all probe caches perf probe: Support @BUILDID or @FILE suffix for SDT events perf probe: Support a special SDT probe format
perf build: Add sdt feature detection
perf-test: Add a test case for SDT event

 tools/perf/Documentation/perf-buildid-cache.txt |    3 
 tools/perf/Documentation/perf-probe.txt         |   32 +
 tools/perf/Makefile.perf                        |    3 
 tools/perf/builtin-list.c                       |    6 
 tools/perf/builtin-probe.c                      |   30 +
 tools/perf/config/Makefile                      |   10 
 tools/perf/tests/Build                          |    1 
 tools/perf/tests/builtin-test.c                 |    4 
 tools/perf/tests/make                           |    3 
 tools/perf/tests/sdt.c                          |  114 +++++
 tools/perf/tests/tests.h                        |    1 
 tools/perf/util/build-id.c                      |  196 ++++++++-
 tools/perf/util/build-id.h                      |    6 
 tools/perf/util/parse-events.c                  |   83 ++++
 tools/perf/util/parse-events.h                  |    2 
 tools/perf/util/probe-event.c                   |  490 ++++++++++++++++++++--
 tools/perf/util/probe-event.h                   |    6 
 tools/perf/util/probe-file.c                    |  504 +++++++++++++++++++++++
 tools/perf/util/probe-file.h                    |   41 ++
 tools/perf/util/symbol-elf.c                    |  252 ++++++++++++
 tools/perf/util/symbol.h                        |   22 +
 tools/perf/util/util.c                          |   13 -
 22 files changed, 1736 insertions(+), 86 deletions(-)
 create mode 100644 tools/perf/tests/sdt.c

--
Masami Hiramatsu

Signature · 1 line
Masami Hiramatsu
#39 lore raw

PATCH v10 23/23 perf-test: Add a test case for SDT event

In reply to Masami Hiramatsu

Add a basic test case for SDT event support.
This test scans an SDT event in perftools and
check whether the SDT event is correctly stored
into the buildid cache.

---
 tools/perf/tests/Build          |    1 
 tools/perf/tests/builtin-test.c |    4 +
 tools/perf/tests/sdt.c          |  114 +++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/tests.h        |    1 
 4 files changed, 120 insertions(+)
 create mode 100644 tools/perf/tests/sdt.c
tools/perf/tests/Build +1 -0
index 66a2898..4158422 100644
@@ -39,6 +39,7 @@ perf-y += stat.o
 perf-y += event_update.o
 perf-y += event-times.o
 perf-y += backward-ring-buffer.o
+perf-y += sdt.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
 	$(call rule_mkdir)
tools/perf/tests/builtin-test.c +4 -0
index 0e95c20..c0e0ccb 100644
@@ -212,6 +212,10 @@ static struct test generic_tests[] = {
 		.func = test__backward_ring_buffer,
 	},
 	{
+		.desc = "Test SDT event probing",
+		.func = test__sdt_event,
+	},
+	{
 		.func = NULL,
 	},
 };
tools/perf/tests/sdt.c new file +114 -0

114 lines not on this page · raw message

tools/perf/tests/tests.h +1 -0
index c57e72c..93cfb0b 100644
@@ -87,6 +87,7 @@ int test__synthesize_stat_round(int subtest);
 int test__event_update(int subtest);
 int test__event_times(int subtest);
 int test__backward_ring_buffer(int subtest);
+int test__sdt_event(int subtest);
 
 #if defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT
#40 lore raw

PATCH v10 23/23 perf-test: Add a test case for SDT event

In reply to Masami Hiramatsu

Add a basic test case for SDT event support.
This test scans an SDT event in perftools and
check whether the SDT event is correctly stored
into the buildid cache.

---
 tools/perf/tests/Build          |    1 
 tools/perf/tests/builtin-test.c |    4 +
 tools/perf/tests/sdt.c          |  114 +++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/tests.h        |    1 
 4 files changed, 120 insertions(+)
 create mode 100644 tools/perf/tests/sdt.c
tools/perf/tests/Build +1 -0
index 66a2898..4158422 100644
@@ -39,6 +39,7 @@ perf-y += stat.o
 perf-y += event_update.o
 perf-y += event-times.o
 perf-y += backward-ring-buffer.o
+perf-y += sdt.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
 	$(call rule_mkdir)
tools/perf/tests/builtin-test.c +4 -0
index 0e95c20..c0e0ccb 100644
@@ -212,6 +212,10 @@ static struct test generic_tests[] = {
 		.func = test__backward_ring_buffer,
 	},
 	{
+		.desc = "Test SDT event probing",
+		.func = test__sdt_event,
+	},
+	{
 		.func = NULL,
 	},
 };
tools/perf/tests/sdt.c new file +114 -0

114 lines not on this page · raw message

tools/perf/tests/tests.h +1 -0
index c57e72c..93cfb0b 100644
@@ -87,6 +87,7 @@ int test__synthesize_stat_round(int subtest);
 int test__event_update(int subtest);
 int test__event_times(int subtest);
 int test__backward_ring_buffer(int subtest);
+int test__sdt_event(int subtest);
 
 #if defined(__arm__) || defined(__aarch64__)
 #ifdef HAVE_DWARF_UNWIND_SUPPORT