From 01412261d99497021353c4b1d67e8df6c9cdc3c6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Sun, 29 May 2016 00:15:37 +0900 Subject: perf buildid-cache: Use path/to/bin/buildid/elf instead of path/to/bin/buildid Use path/to/bin/buildid/elf instead of path/to/bin/buildid to store corresponding elf binary. This also stores vdso in buildid/vdso, kallsyms in buildid/kallsyms. Note that the existing caches are not updated until user adds or updates the cache. Anyway, if there is the old style build-id cache it falls back to use it. (IOW, it is backward compatible) Signed-off-by: Masami Hiramatsu Signed-off-by: Masami Hiramatsu Acked-by: Namhyung Kim Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20160528151537.16098.85815.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 115 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 27 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 67e5966503b2..67f986c8c378 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -144,7 +144,32 @@ static int asnprintf(char **strp, size_t size, const char *fmt, ...) return ret; } -static char *build_id__filename(const char *sbuild_id, char *bf, size_t size) +char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, + size_t size) +{ + bool is_alloc = !!bf; + bool retry_old = true; + + asnprintf(&bf, size, "%s/%s/%s/kallsyms", + buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); +retry: + if (!access(bf, F_OK)) + return bf; + if (is_alloc) + free(bf); + if (retry_old) { + /* Try old style kallsyms cache */ + asnprintf(&bf, size, "%s/%s/%s", + buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); + retry_old = false; + goto retry; + } + + return NULL; +} + +static char *build_id_cache__linkname(const char *sbuild_id, char *bf, + size_t size) { char *tmp = bf; int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir, @@ -154,23 +179,52 @@ static char *build_id__filename(const char *sbuild_id, char *bf, size_t size) return bf; } +static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso) +{ + return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf"); +} + char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) { - char build_id_hex[SBUILD_ID_SIZE]; + bool is_kallsyms = dso__is_kallsyms((struct dso *)dso); + bool is_vdso = dso__is_vdso((struct dso *)dso); + char sbuild_id[SBUILD_ID_SIZE]; + char *linkname; + bool alloc = (bf == NULL); + int ret; if (!dso->has_build_id) return NULL; - build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); - return build_id__filename(build_id_hex, bf, size); + build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + linkname = build_id_cache__linkname(sbuild_id, NULL, 0); + if (!linkname) + return NULL; + + /* Check if old style build_id cache */ + if (is_regular_file(linkname)) + ret = asnprintf(&bf, size, "%s", linkname); + else + ret = asnprintf(&bf, size, "%s/%s", linkname, + build_id_cache__basename(is_kallsyms, is_vdso)); + if (ret < 0 || (!alloc && size < (unsigned int)ret)) + bf = NULL; + free(linkname); + + return bf; } bool dso__build_id_is_kmod(const struct dso *dso, char *bf, size_t size) { - char *id_name, *ch; + char *id_name = NULL, *ch; struct stat sb; + char sbuild_id[SBUILD_ID_SIZE]; + + if (!dso->has_build_id) + goto err; - id_name = dso__build_id_filename(dso, bf, size); + build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + id_name = build_id_cache__linkname(sbuild_id, NULL, 0); if (!id_name) goto err; if (access(id_name, F_OK)) @@ -194,18 +248,14 @@ bool dso__build_id_is_kmod(const struct dso *dso, char *bf, size_t size) if (ch - 3 < bf) goto err; + free(id_name); return strncmp(".ko", ch - 3, 3) == 0; err: - /* - * If dso__build_id_filename work, get id_name again, - * because id_name points to bf and is broken. - */ - if (id_name) - id_name = dso__build_id_filename(dso, bf, size); pr_err("Invalid build id: %s\n", id_name ? : dso->long_name ? : dso->short_name ? : "[unknown]"); + free(id_name); return false; } @@ -341,7 +391,8 @@ void disable_buildid_cache(void) } static char *build_id_cache__dirname_from_path(const char *name, - bool is_kallsyms, bool is_vdso) + bool is_kallsyms, bool is_vdso, + const char *sbuild_id) { char *realname = (char *)name, *filename; bool slash = is_kallsyms || is_vdso; @@ -352,8 +403,9 @@ static char *build_id_cache__dirname_from_path(const char *name, return NULL; } - if (asprintf(&filename, "%s%s%s", buildid_dir, slash ? "/" : "", - is_vdso ? DSO__NAME_VDSO : realname) < 0) + if (asprintf(&filename, "%s%s%s%s%s", buildid_dir, slash ? "/" : "", + is_vdso ? DSO__NAME_VDSO : realname, + sbuild_id ? "/" : "", sbuild_id ?: "") < 0) filename = NULL; if (!slash) @@ -368,7 +420,8 @@ 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); + dir_name = build_id_cache__dirname_from_path(pathname, false, false, + NULL); if (!dir_name) return -ENOMEM; @@ -385,7 +438,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, { const size_t size = PATH_MAX; char *realname = NULL, *filename = NULL, *dir_name = NULL, - *linkname = zalloc(size), *targetname, *tmp; + *linkname = zalloc(size), *tmp; int err = -1; if (!is_kallsyms) { @@ -394,14 +447,22 @@ 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); + dir_name = build_id_cache__dirname_from_path(name, is_kallsyms, + is_vdso, sbuild_id); if (!dir_name) goto out_free; + /* Remove old style build-id cache */ + if (is_regular_file(dir_name)) + if (unlink(dir_name)) + goto out_free; + if (mkdir_p(dir_name, 0755)) goto out_free; - if (asprintf(&filename, "%s/%s", dir_name, sbuild_id) < 0) { + /* Save the allocated buildid dirname */ + if (asprintf(&filename, "%s/%s", dir_name, + build_id_cache__basename(is_kallsyms, is_vdso)) < 0) { filename = NULL; goto out_free; } @@ -415,7 +476,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, goto out_free; } - if (!build_id__filename(sbuild_id, linkname, size)) + if (!build_id_cache__linkname(sbuild_id, linkname, size)) goto out_free; tmp = strrchr(linkname, '/'); *tmp = '\0'; @@ -424,10 +485,10 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, goto out_free; *tmp = '/'; - targetname = filename + strlen(buildid_dir) - 5; - memcpy(targetname, "../..", 5); + tmp = dir_name + strlen(buildid_dir) - 5; + memcpy(tmp, "../..", 5); - if (symlink(targetname, linkname) == 0) + if (symlink(tmp, linkname) == 0) err = 0; out_free: if (!is_kallsyms) @@ -452,7 +513,7 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, bool build_id_cache__cached(const char *sbuild_id) { bool ret = false; - char *filename = build_id__filename(sbuild_id, NULL, 0); + char *filename = build_id_cache__linkname(sbuild_id, NULL, 0); if (filename && !access(filename, F_OK)) ret = true; @@ -471,7 +532,7 @@ int build_id_cache__remove_s(const char *sbuild_id) if (filename == NULL || linkname == NULL) goto out_free; - if (!build_id__filename(sbuild_id, linkname, size)) + if (!build_id_cache__linkname(sbuild_id, linkname, size)) goto out_free; if (access(linkname, F_OK)) @@ -489,7 +550,7 @@ int build_id_cache__remove_s(const char *sbuild_id) tmp = strrchr(linkname, '/') + 1; snprintf(tmp, size - (tmp - linkname), "%s", filename); - if (unlink(linkname)) + if (rm_rf(linkname)) goto out_free; err = 0; @@ -501,7 +562,7 @@ out_free: static int dso__cache_build_id(struct dso *dso, struct machine *machine) { - bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; + bool is_kallsyms = dso__is_kallsyms(dso); bool is_vdso = dso__is_vdso(dso); const char *name = dso->long_name; char nm[PATH_MAX]; -- cgit v1.2.3 From c58c49ac630979a285d574b3f72a528209515fb3 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 7 Jun 2016 03:54:38 +0000 Subject: perf tools: Fix crash in build_id_cache__kallsyms_path() build_id_cache__kallsyms_path() accepts a string buffer but also allocs a buffer using asnprintf. Unfortunately, the its only user passes it a stack-allocated buffer. Freeing it causes crashes like this: $ perf script *** Error in `/home/wangnan/perf': free(): invalid pointer: 0x00007fffffff9630 *** ======= Backtrace: ========= lib64/libc.so.6(+0x6eeef)[0x7ffff5dbaeef] lib64/libc.so.6(+0x78cae)[0x7ffff5dc4cae] lib64/libc.so.6(+0x79987)[0x7ffff5dc5987] /home/w00229757/perf(build_id_cache__kallsyms_path+0x6b)[0x49681b] /home/w00229757/perf[0x4bdd40] /home/w00229757/perf(dso__load+0xa3a)[0x4c048a] /home/w00229757/perf(map__load+0x6f)[0x4d561f] /home/w00229757/perf(thread__find_addr_map+0x235)[0x49e935] /home/w00229757/perf(machine__resolve+0x7d)[0x49ec6d] /home/w00229757/perf[0x4555a8] /home/w00229757/perf[0x4d9507] /home/w00229757/perf[0x4d9e80] /home/w00229757/perf(ordered_events__flush+0x354)[0x4dd444] /home/w00229757/perf(perf_session__process_events+0x3d0)[0x4dc140] /home/w00229757/perf(cmd_script+0x12b0)[0x4592e0] /home/w00229757/perf[0x4911f1] /home/w00229757/perf(main+0x68f)[0x4352ef] /lib64/libc.so.6(__libc_start_main+0xf5)[0x7ffff5d6dbd5] /home/w00229757/perf[0x435415] ======= Memory map: ======== This patch simplifies build_id_cache__kallsyms_path(), not even considering allocating a string buffer, so never frees anything. Its caller should manage memory allocation. Signed-off-by: Wang Nan Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Fixes: 01412261d994 ("perf buildid-cache: Use path/to/bin/buildid/elf instead of path/to/bin/buildid") Link: http://lkml.kernel.org/r/1465271678-7392-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 67f986c8c378..20aef90bf194 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -147,20 +147,17 @@ static int asnprintf(char **strp, size_t size, const char *fmt, ...) char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf, size_t size) { - bool is_alloc = !!bf; bool retry_old = true; - asnprintf(&bf, size, "%s/%s/%s/kallsyms", - buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); + snprintf(bf, size, "%s/%s/%s/kallsyms", + buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); retry: if (!access(bf, F_OK)) return bf; - if (is_alloc) - free(bf); if (retry_old) { /* Try old style kallsyms cache */ - asnprintf(&bf, size, "%s/%s/%s", - buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); + snprintf(bf, size, "%s/%s/%s", + buildid_dir, DSO__NAME_KALLSYMS, sbuild_id); retry_old = false; goto retry; } -- cgit v1.2.3 From 4698b8b7572ff74d9d17fdb02d5957b7148c64fe Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 8 Jun 2016 18:29:30 +0900 Subject: 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. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20160608092930.3116.67575.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 12 +++++------- tools/perf/util/build-id.h | 2 ++ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 20aef90bf194..62b147366d01 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -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; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index e5435f46e48e..d8c7f2fc6a87 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -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); -- cgit v1.2.3 From 1f3736c9c833e40ac4d3a8dc6d661e341df8a259 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 1 Jul 2016 17:03:26 +0900 Subject: perf probe: Show all cached probes 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. Signed-off-by: Masami Hiramatsu Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/146736020674.27797.13488316780383460180.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-probe.txt | 8 ++- tools/perf/builtin-probe.c | 2 +- tools/perf/util/build-id.c | 108 +++++++++++++++++++++++++++++++- tools/perf/util/build-id.h | 3 + tools/perf/util/probe-event.c | 3 + tools/perf/util/probe-file.c | 66 ++++++++++++++++++- tools/perf/util/probe-file.h | 1 + 7 files changed, 184 insertions(+), 7 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 947db6fe512c..5a70d45015ea 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -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. diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 34262329f405..0bb9084bf6cf 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -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 { diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 62b147366d01..1c49620e98b2 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -165,8 +165,7 @@ retry: return NULL; } -static char *build_id_cache__linkname(const char *sbuild_id, char *bf, - size_t size) +char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size) { char *tmp = bf; int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir, @@ -176,6 +175,36 @@ static char *build_id_cache__linkname(const char *sbuild_id, char *bf, return bf; } +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 "../../" */ + p = strrchr(buf, '/'); /* Cut off the "/" */ + 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 "../..[/]" */ + } +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"); @@ -387,6 +416,81 @@ void disable_buildid_cache(void) no_buildid_cache = true; } +static bool lsdir_bid_head_filter(const char *name __maybe_unused, + struct dirent *d __maybe_unused) +{ + return (strlen(d->d_name) == 2) && + isxdigit(d->d_name[0]) && isxdigit(d->d_name[1]); +} + +static bool lsdir_bid_tail_filter(const char *name __maybe_unused, + struct dirent *d __maybe_unused) +{ + int i = 0; + while (isxdigit(d->d_name[i]) && i < SBUILD_ID_SIZE - 3) + i++; + return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0'); +} + +struct strlist *build_id_cache__list_all(void) +{ + struct strlist *toplist, *linklist = NULL, *bidlist; + struct str_node *nd, *nd2; + char *topdir, *linkdir = NULL; + char sbuild_id[SBUILD_ID_SIZE]; + + /* Open the top-level directory */ + if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0) + return NULL; + + bidlist = strlist__new(NULL, NULL); + if (!bidlist) + goto out; + + toplist = lsdir(topdir, lsdir_bid_head_filter); + if (!toplist) { + pr_debug("Error in lsdir(%s): %d\n", topdir, errno); + /* If there is no buildid cache, return an empty list */ + if (errno == ENOENT) + goto out; + goto err_out; + } + + strlist__for_each_entry(nd, toplist) { + if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0) + goto err_out; + /* Open the lower-level directory */ + linklist = lsdir(linkdir, lsdir_bid_tail_filter); + if (!linklist) { + pr_debug("Error in lsdir(%s): %d\n", linkdir, errno); + goto err_out; + } + strlist__for_each_entry(nd2, linklist) { + if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s", + nd->s, nd2->s) != SBUILD_ID_SIZE - 1) + goto err_out; + if (strlist__add(bidlist, sbuild_id) < 0) + goto err_out; + } + strlist__delete(linklist); + zfree(&linkdir); + } + +out_free: + strlist__delete(toplist); +out: + free(topdir); + + return bidlist; + +err_out: + strlist__delete(linklist); + zfree(&linkdir); + strlist__delete(bidlist); + bidlist = NULL; + goto out_free; +} + char *build_id_cache__cachedir(const char *sbuild_id, const char *name, bool is_kallsyms, bool is_vdso) { diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index d8c7f2fc6a87..b742e271ea2c 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -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); +struct strlist *build_id_cache__list_all(void); int build_id_cache__list_build_ids(const char *pathname, struct strlist **result); bool build_id_cache__cached(const char *sbuild_id); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 47b6b8b7206e..f81b5dd7f1b1 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2366,6 +2366,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; diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index a94ee478178d..156e3d883965 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -367,10 +367,17 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target) { char cpath[PATH_MAX]; char sbuildid[SBUILD_ID_SIZE]; - char *dir_name; + char *dir_name = NULL; bool is_kallsyms = !target; int ret, fd; + if (target && build_id_cache__cached(target)) { + /* This is a cached buildid */ + strncpy(sbuildid, target, SBUILD_ID_SIZE); + dir_name = build_id_cache__linkname(sbuildid, NULL, 0); + goto found; + } + if (target) ret = filename__sprintf_build_id(target, sbuildid); else { @@ -394,8 +401,11 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target) dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms, false); - if (!dir_name) +found: + if (!dir_name) { + pr_debug("Failed to get cache from %s\n", target); return -ENOMEM; + } snprintf(cpath, PATH_MAX, "%s/probes", dir_name); fd = open(cpath, O_CREAT | O_RDWR, 0644); @@ -673,3 +683,55 @@ int probe_cache__commit(struct probe_cache *pcache) out: return ret; } + +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->entries, node) { + 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)) + printf("%s\n", entry->spev); + } + return 0; +} + +/* Show all cached probes */ +int probe_cache__show_all_caches(struct strfilter *filter) +{ + struct probe_cache *pcache; + struct strlist *bidlist; + struct str_node *nd; + char *buf = strfilter__string(filter); + + pr_debug("list cache with filter: %s\n", buf); + free(buf); + + bidlist = build_id_cache__list_all(); + if (!bidlist) { + pr_debug("Failed to get buildids: %d\n", errno); + return -EINVAL; + } + strlist__for_each_entry(nd, bidlist) { + pcache = probe_cache__new(nd->s); + if (!pcache) + continue; + if (!list_empty(&pcache->entries)) { + buf = build_id_cache__origname(nd->s); + printf("%s (%s):\n", buf, nd->s); + free(buf); + probe_cache__show_entries(pcache, filter); + } + probe_cache__delete(pcache); + } + strlist__delete(bidlist); + + return 0; +} diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 910aa74953e9..0009b8a65a5c 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h @@ -42,4 +42,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 -- cgit v1.2.3 From 6430a94ead2a4c8f350441351a735303eb6d1c8a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Fri, 1 Jul 2016 17:04:10 +0900 Subject: perf buildid-cache: Scan and import user SDT events to probe cache perf buildid-cache --add 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_:=". 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 "%". Signed-off-by: Masami Hiramatsu Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/146736025058.27797.13043265488541434502.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-buildid-cache.txt | 3 ++ tools/perf/util/build-id.c | 30 +++++++++++ tools/perf/util/probe-file.c | 69 +++++++++++++++++++++++-- tools/perf/util/probe-file.h | 2 + 4 files changed, 101 insertions(+), 3 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index dd07b55f58d8..058064db39d2 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt @@ -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 ------- diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 1c49620e98b2..e1a16408da9c 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -17,6 +17,7 @@ #include "tool.h" #include "header.h" #include "vdso.h" +#include "probe-file.h" static bool no_buildid_cache; @@ -532,6 +533,30 @@ int build_id_cache__list_build_ids(const char *pathname, return ret; } +#ifdef HAVE_LIBELF_SUPPORT +static int build_id_cache__add_sdt_cache(const char *sbuild_id, + const char *realname) +{ + struct probe_cache *cache; + int ret; + + cache = probe_cache__new(sbuild_id); + if (!cache) + return -1; + + ret = probe_cache__scan_sdt(cache, realname); + if (ret >= 0) { + pr_debug("Found %d SDTs in %s\n", ret, realname); + if (probe_cache__commit(cache) < 0) + ret = -1; + } + probe_cache__delete(cache); + return ret; +} +#else +#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0) +#endif + int build_id_cache__add_s(const char *sbuild_id, const char *name, bool is_kallsyms, bool is_vdso) { @@ -589,6 +614,11 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name, if (symlink(tmp, linkname) == 0) err = 0; + + /* Update SDT cache : error is just warned */ + if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0) + pr_debug("Failed to update/scan SDT cache for %s\n", realname); + out_free: if (!is_kallsyms) free(realname); diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 6cb6ec03c1fe..5b563b2e8b1d 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -434,12 +434,15 @@ static int probe_cache__load(struct probe_cache *pcache) p = strchr(buf, '\n'); if (p) *p = '\0'; - if (buf[0] == '#') { /* #perf_probe_event */ + /* #perf_probe_event or %sdt_event */ + if (buf[0] == '#' || buf[0] == '%') { entry = probe_cache_entry__new(NULL); if (!entry) { ret = -ENOMEM; goto out; } + if (buf[0] == '%') + entry->sdt = true; entry->spev = strdup(buf + 1); if (entry->spev) ret = parse_perf_probe_command(buf + 1, @@ -621,19 +624,79 @@ out_err: return ret; } +static unsigned long long sdt_note__get_addr(struct sdt_note *note) +{ + return note->bit32 ? (unsigned long long)note->addr.a32[0] + : (unsigned long long)note->addr.a64[0]; +} + +int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) +{ + struct probe_cache_entry *entry = NULL; + struct list_head sdtlist; + struct sdt_note *note; + char *buf; + char sdtgrp[64]; + int ret; + + INIT_LIST_HEAD(&sdtlist); + ret = get_sdt_note_list(&sdtlist, pathname); + if (ret < 0) { + pr_debug("Failed to get sdt note: %d\n", ret); + return ret; + } + list_for_each_entry(note, &sdtlist, note_list) { + ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider); + if (ret < 0) + break; + /* Try to find same-name entry */ + entry = probe_cache__find_by_name(pcache, sdtgrp, note->name); + if (!entry) { + entry = probe_cache_entry__new(NULL); + if (!entry) { + ret = -ENOMEM; + break; + } + entry->sdt = true; + ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp, + note->name, note->name); + if (ret < 0) + break; + entry->pev.event = strdup(note->name); + entry->pev.group = strdup(sdtgrp); + list_add_tail(&entry->node, &pcache->entries); + } + ret = asprintf(&buf, "p:%s/%s %s:0x%llx", + sdtgrp, note->name, pathname, + sdt_note__get_addr(note)); + if (ret < 0) + break; + strlist__add(entry->tevlist, buf); + free(buf); + entry = NULL; + } + if (entry) { + list_del_init(&entry->node); + probe_cache_entry__delete(entry); + } + cleanup_sdt_note_list(&sdtlist); + return ret; +} + static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) { struct str_node *snode; struct stat st; struct iovec iov[3]; + const char *prefix = entry->sdt ? "%" : "#"; int ret; /* Save stat for rollback */ ret = fstat(fd, &st); if (ret < 0) return ret; - pr_debug("Writing cache: #%s\n", entry->spev); - iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1; + pr_debug("Writing cache: %s%s\n", prefix, entry->spev); + iov[0].iov_base = (void *)prefix; 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); diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 0ed1fc563b77..ddf5ae212c2f 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h @@ -8,6 +8,7 @@ /* Cache of probe definitions */ struct probe_cache_entry { struct list_head node; + 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__purge(struct probe_cache *pcache); void probe_cache__delete(struct probe_cache *pcache); -- cgit v1.2.3 From 1c1a3a4729aae712c55e001e151ef008d030d4a7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 12 Jul 2016 12:19:09 -0300 Subject: perf tools: Add feature detection for gelf_getnote() That is not present on some libelf implementations, such as the one used in Alpine Linux: libelf-0.8.13. This ends up disabling the SDT code, that relies on this function. One alternative would be to provide an weak fallback implementation or the open coded variant used by the buildid sysfs notes reading code. Cc: Adrian Hunter Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: David Ahern Cc: Hemant Kumar Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-82lh22ybedy9b9lych8xj12g@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 1 + tools/build/feature/Makefile | 4 ++++ tools/build/feature/test-all.c | 5 +++++ tools/build/feature/test-libelf-gelf_getnote.c | 7 +++++++ tools/perf/config/Makefile | 6 ++++++ tools/perf/util/build-id.c | 2 +- tools/perf/util/probe-file.c | 2 ++ tools/perf/util/symbol-elf.c | 2 ++ 8 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tools/build/feature/test-libelf-gelf_getnote.c (limited to 'tools/perf/util/build-id.c') diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 3dd529bb0604..fe12bee57418 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -40,6 +40,7 @@ FEATURE_TESTS_BASIC := \ libbfd \ libelf \ libelf-getphdrnum \ + libelf-gelf_getnote \ libelf-getshdrstrndx \ libelf-mmap \ libnuma \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 674711629ef0..d6017c1abdb0 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -17,6 +17,7 @@ FILES= \ test-cplus-demangle.bin \ test-libelf.bin \ test-libelf-getphdrnum.bin \ + test-libelf-gelf_getnote.bin \ test-libelf-getshdrstrndx.bin \ test-libelf-mmap.bin \ test-libnuma.bin \ @@ -99,6 +100,9 @@ $(OUTPUT)test-libelf-mmap.bin: $(OUTPUT)test-libelf-getphdrnum.bin: $(BUILD) -lelf +$(OUTPUT)test-libelf-gelf_getnote.bin: + $(BUILD) -lelf + $(OUTPUT)test-libelf-getshdrstrndx.bin: $(BUILD) -lelf diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index 7433cca33306..843aed024a3a 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -49,6 +49,10 @@ # include "test-libelf-getphdrnum.c" #undef main +#define main main_test_libelf_gelf_getnote +# include "test-libelf-gelf_getnote.c" +#undef main + #define main main_test_libelf_getshdrstrndx # include "test-libelf-getshdrstrndx.c" #undef main @@ -153,6 +157,7 @@ int main(int argc, char *argv[]) main_test_dwarf(); main_test_dwarf_getlocations(); main_test_libelf_getphdrnum(); + main_test_libelf_gelf_getnote(); main_test_libelf_getshdrstrndx(); main_test_libunwind(); main_test_libaudit(); diff --git a/tools/build/feature/test-libelf-gelf_getnote.c b/tools/build/feature/test-libelf-gelf_getnote.c new file mode 100644 index 000000000000..d78cf4d5271f --- /dev/null +++ b/tools/build/feature/test-libelf-gelf_getnote.c @@ -0,0 +1,7 @@ +#include +#include + +int main(void) +{ + return gelf_getnote(NULL, 0, NULL, NULL, NULL); +} diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 97d47140a637..5ac428060779 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -309,6 +309,12 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT endif + ifeq ($(feature-libelf-gelf_getnote), 1) + CFLAGS += -DHAVE_GELF_GETNOTE_SUPPORT + else + msg := $(warning gelf_getnote() not found on libelf, SDT support disabled); + endif + ifeq ($(feature-libelf-getshdrstrndx), 1) CFLAGS += -DHAVE_ELF_GETSHDRSTRNDX_SUPPORT endif diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index e1a16408da9c..1e504e40dac8 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -533,7 +533,7 @@ int build_id_cache__list_build_ids(const char *pathname, return ret; } -#ifdef HAVE_LIBELF_SUPPORT +#if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT) static int build_id_cache__add_sdt_cache(const char *sbuild_id, const char *realname) { diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 98398b55a03f..e705a742ee1e 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -624,6 +624,7 @@ out_err: return ret; } +#ifdef HAVE_GELF_GETNOTE_SUPPORT static unsigned long long sdt_note__get_addr(struct sdt_note *note) { return note->bit32 ? (unsigned long long)note->addr.a32[0] @@ -682,6 +683,7 @@ int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname) cleanup_sdt_note_list(&sdtlist); return ret; } +#endif static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) { diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 79a6a190f41d..cebf98ec27bc 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1790,6 +1790,7 @@ void kcore_extract__delete(struct kcore_extract *kce) unlink(kce->extract_filename); } +#ifdef HAVE_GELF_GETNOTE_SUPPORT /** * populate_sdt_note : Parse raw data and identify SDT note * @elf: elf of the opened file @@ -2041,6 +2042,7 @@ int sdt_notes__get_count(struct list_head *start) count++; return count; } +#endif void symbol__elf_init(void) { -- cgit v1.2.3 From c3492a3a4e58117f18d96125e67b0bed7c4231e1 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 12 Jul 2016 19:04:54 +0900 Subject: perf probe: Make --list show only available cached events Make "perf probe --cache --list" show only available cached events by checking build-id validity. E.g. without this patch: ---- $ ./perf probe --cache --add oldevent=cmd_probe $ make #(to update ./perf) $ ./perf probe --cache --add newevent=cmd_probe $ ./perf probe --cache --list /home/mhiramat/ksrc/linux/tools/perf/perf (061e90539bac69 probe_perf:newevent=cmd_probe /home/mhiramat/ksrc/linux/tools/perf/perf (c2e44d614e33e1 probe_perf:oldevent=cmd_probe ---- It shows both of old and new events but user can not use old one. With this; ---- $ ./perf probe --cache -l /home/mhiramat/ksrc/linux/tools/perf/perf (061e90539bac69 probe_perf:newevent=cmd_probe ---- This shows only new events which are on the existing binary. Signed-off-by: Masami Hiramatsu Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/146831789417.17065.17896487479879669610.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-probe.c | 2 +- tools/perf/util/build-id.c | 33 ++++++++++++++++++++++++++++++++- tools/perf/util/build-id.h | 2 +- tools/perf/util/probe-file.c | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index c6d890ad2c1a..ee5b42173ba3 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -370,7 +370,7 @@ static int del_perf_probe_caches(struct strfilter *filter) struct str_node *nd; int ret; - bidlist = build_id_cache__list_all(); + bidlist = build_id_cache__list_all(false); if (!bidlist) { ret = -errno; pr_debug("Failed to get buildids: %d\n", ret); diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 1e504e40dac8..36b4279a9002 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -206,6 +206,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"); @@ -433,13 +458,17 @@ static bool lsdir_bid_tail_filter(const char *name __maybe_unused, return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0'); } -struct strlist *build_id_cache__list_all(void) +struct strlist *build_id_cache__list_all(bool validonly) { struct strlist *toplist, *linklist = NULL, *bidlist; struct str_node *nd, *nd2; char *topdir, *linkdir = NULL; char sbuild_id[SBUILD_ID_SIZE]; + /* for filename__ functions */ + if (validonly) + symbol__init(NULL); + /* Open the top-level directory */ if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0) return NULL; @@ -470,6 +499,8 @@ struct strlist *build_id_cache__list_all(void) if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s", nd->s, nd2->s) != SBUILD_ID_SIZE - 1) goto err_out; + if (validonly && !build_id_cache__valid_id(sbuild_id)) + continue; if (strlist__add(bidlist, sbuild_id) < 0) goto err_out; } diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index b742e271ea2c..64e740f4bc28 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -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); -struct strlist *build_id_cache__list_all(void); +struct strlist *build_id_cache__list_all(bool validonly); int build_id_cache__list_build_ids(const char *pathname, struct strlist **result); bool build_id_cache__cached(const char *sbuild_id); diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index fc16b172579f..a5059dc3921a 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c @@ -808,7 +808,7 @@ int probe_cache__show_all_caches(struct strfilter *filter) pr_debug("list cache with filter: %s\n", buf); free(buf); - bidlist = build_id_cache__list_all(); + bidlist = build_id_cache__list_all(true); if (!bidlist) { pr_debug("Failed to get buildids: %d\n", errno); return -EINVAL; -- cgit v1.2.3 From a598180aa1279bac4d24dfc85cd2d78553c4210d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 12 Jul 2016 19:05:37 +0900 Subject: perf probe: Support @BUILDID or @FILE suffix for SDT events 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 ---- Committer note: Doing the full sequence of steps to get the results above: With a clean build-id cache: [root@jouet ~]# rm -rf ~/.debug/ [root@jouet ~]# perf list sdt List of pre-defined events (to be used in -e): [root@jouet ~]# No events whatsoever, then, we can add all events in gcc to the build-id cache, doing a --add + --dry-run: [root@jouet ~]# perf probe --dry-run --cache -x /usr/bin/gcc --add %sdt_libstdcxx:\* Added new events: sdt_libstdcxx:throw (on %* in /usr/bin/gcc) sdt_libstdcxx:rethrow (on %* in /usr/bin/gcc) sdt_libstdcxx:catch (on %* in /usr/bin/gcc) You can now use it in all perf tools, such as: perf record -e sdt_libstdcxx:catch -aR sleep 1 [root@jouet ~]# It really didn't add any events, it just cached them: [root@jouet ~]# perf probe -l [root@jouet ~]# We can see that it was cached as: [root@jouet ~]# ls -la ~/.debug/usr/bin/gcc/9a0730e2bcc6d2a2003d21ac46807e8ee6bcb7c2/ total 976 drwxr-xr-x. 2 root root 4096 Jul 13 21:47 . drwxr-xr-x. 3 root root 4096 Jul 13 21:47 .. -rwxr-xr-x. 4 root root 985912 Jun 22 18:52 elf -rw-r--r--. 1 root root 303 Jul 13 21:47 probes [root@jouet ~]# file ~/.debug/usr/bin/gcc/9a0730e2bcc6d2a2003d21ac46807e8ee6bcb7c2/elf /root/.debug/usr/bin/gcc/9a0730e2bcc6d2a2003d21ac46807e8ee6bcb7c2/elf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9a0730e2bcc6d2a2003d21ac46807e8ee6bcb7c2, stripped [root@jouet ~]# cat ~/.debug/usr/bin/gcc/9a0730e2bcc6d2a2003d21ac46807e8ee6bcb7c2/probes %sdt_libstdcxx:throw=throw p:sdt_libstdcxx/throw /usr/bin/gcc:0x71ffd %sdt_libstdcxx:rethrow=rethrow p:sdt_libstdcxx/rethrow /usr/bin/gcc:0x720b8 %sdt_libstdcxx:catch=catch p:sdt_libstdcxx/catch /usr/bin/gcc:0x7307f %sdt_libgcc:unwind=unwind p:sdt_libgcc/unwind /usr/bin/gcc:0x7eec0 #sdt_libstdcxx:*=%* [root@jouet ~]# Ok, now we can use 'perf probe' to refer to those cached entries as: Humm, nope, doing as above we end up with: [root@jouet ~]# perf probe -a %sdt_libstdcxx:catch Semantic error :* is bad for event name -it must follow C symbol-naming rule. Error: Failed to add events. [root@jouet ~]# But it worked at some point, lets try not using --dry-run: Resetting everything: # rm -rf ~/.debug/ # perf probe -d *:* # perf probe -l # perf list sdt List of pre-defined events (to be used in -e): # Ok, now it cached everything, even things we haven't asked it to (sdt_libgcc:unwind): [root@jouet ~]# perf probe -x /usr/bin/gcc --add %sdt_libstdcxx:\* Added new events: sdt_libstdcxx:throw (on %* in /usr/bin/gcc) sdt_libstdcxx:rethrow (on %* in /usr/bin/gcc) sdt_libstdcxx:catch (on %* in /usr/bin/gcc) You can now use it in all perf tools, such as: perf record -e sdt_libstdcxx:catch -aR sleep 1 [root@jouet ~]# perf list sdt List of pre-defined events (to be used in -e): sdt_libgcc:unwind [SDT event] sdt_libstdcxx:catch [SDT event] sdt_libstdcxx:rethrow [SDT event] sdt_libstdcxx:throw [SDT event] [root@jouet ~]# And we have the events in place: [root@jouet ~]# perf probe -l sdt_libstdcxx:catch (on execute_cfa_program+1551@../../../libgcc/unwind-dw2.c in /usr/bin/gcc) sdt_libstdcxx:rethrow (on d_print_subexpr+280@libsupc++/cp-demangle.c in /usr/bin/gcc) sdt_libstdcxx:throw (on d_print_subexpr+93@libsupc++/cp-demangle.c in /usr/bin/gcc) [root@jouet ~]# And trying to use them at least has 'perf trace --event sdt*:*' working. Then, if we try to add the ones in libstdc++: [root@jouet ~]# perf probe -x /usr/lib64/libstdc++.so.6 -a %sdt_libstdcxx:\* Error: event "catch" already exists. Hint: Remove existing event by 'perf probe -d' or force duplicates by 'perf probe -f' or set 'force=yes' in BPF source. Error: Failed to add events. [root@jouet ~]# Doesn't work, dups, but at least this served to, unbeknownst to the user, add the SDT probes in /usr/lib64/libstdc++.so.6! [root@jouet ~]# perf list sdt List of pre-defined events (to be used in -e): sdt_libgcc:unwind [SDT event] sdt_libstdcxx:catch@/usr/bin/gcc(9a0730e2bcc6) [SDT event] sdt_libstdcxx:catch@/usr/lib64/libstdc++.so.6.0.22(ef2b7066559a) [SDT event] sdt_libstdcxx:rethrow@/usr/bin/gcc(9a0730e2bcc6) [SDT event] sdt_libstdcxx:rethrow@/usr/lib64/libstdc++.so.6.0.22(ef2b7066559a) [SDT event] sdt_libstdcxx:throw@/usr/bin/gcc(9a0730e2bcc6) [SDT event] sdt_libstdcxx:throw@/usr/lib64/libstdc++.so.6.0.22(ef2b7066559a) [SDT event] [root@jouet ~]# Now we should be able to get to the original cset comment, if we remove all SDTs events in place, not from the cache, from the kernel, where it was set up as: [root@jouet ~]# ls -la /sys/kernel/debug/tracing/events/sdt_libstdcxx/ total 0 drwxr-xr-x. 5 root root 0 Jul 13 22:00 . drwxr-xr-x. 80 root root 0 Jul 13 21:56 .. drwxr-xr-x. 2 root root 0 Jul 13 22:00 catch -rw-r--r--. 1 root root 0 Jul 13 22:00 enable -rw-r--r--. 1 root root 0 Jul 13 22:00 filter drwxr-xr-x. 2 root root 0 Jul 13 22:00 rethrow drwxr-xr-x. 2 root root 0 Jul 13 22:00 throw [root@jouet ~]# [root@jouet ~]# head -2 /sys/kernel/debug/tracing/events/sdt_libstdcxx/throw/format name: throw ID: 2059 [root@jouet ~]# Now to remove it: [root@jouet ~]# perf probe -d sdt_libstdc*:* Removed event: sdt_libstdcxx:catch Removed event: sdt_libstdcxx:rethrow Removed event: sdt_libstdcxx:throw [root@jouet ~]# Which caused: [root@jouet ~]# ls -la /sys/kernel/debug/tracing/events/sdt_libstdcxx/ ls: cannot access '/sys/kernel/debug/tracing/events/sdt_libstdcxx/': No such file or directory [root@jouet ~]# Ok, now we can do: [root@jouet ~]# perf list sdt_libstdcxx:catch List of pre-defined events (to be used in -e): sdt_libstdcxx:catch@/usr/bin/gcc(9a0730e2bcc6) [SDT event] sdt_libstdcxx:catch@/usr/lib64/libstdc++.so.6.0.22(ef2b7066559a) [SDT event] [root@jouet ~]# So, these are not really 'pre-defined events', i.e. we can't use them with 'perf record --event': [root@jouet ~]# perf record --event sdt_libstdcxx:catch* event syntax error: 'sdt_libstdcxx:catch*' \___ unknown tracepoint Error: File /sys/kernel/debug/tracing/events/sdt_libstdcxx/catch* not found. Hint: Perhaps this kernel misses some CONFIG_ setting to enable this feature?. [root@jouet ~]# To have it really pre-defined we must use perf probe to get its definition from the cache and set it up in the kernel, creating the tracepoint to _then_ use it with 'perf record --event': [root@jouet ~]# perf probe -a sdt_libstdcxx:catch Semantic error :There is non-digit char in line number. Oops, there is another gotcha here, we need that pesky '%' character: [root@jouet ~]# perf probe -a %sdt_libstdcxx:catch Added new events: sdt_libstdcxx:catch (on %catch in /usr/bin/gcc) sdt_libstdcxx:catch_1 (on %catch in /usr/lib64/libstdc++.so.6.0.22) You can now use it in all perf tools, such as: perf record -e sdt_libstdcxx:catch_1 -aR sleep 1 [root@jouet ~]# But then we added _two_ events, one with the name we expected, the other one with a _ added, when doing the analysis we need to pay attention to who maps to who. And here is where we get to the point of this patch, which is to be able to disambiguate those definitions for 'catch' in the build-id cache, but first we need remove those events we just added: [root@jouet ~]# perf probe -d %sdt_libstdcxx:catch Oops, that didn't remove anything, we need to _remove_ that % char in this case: [root@jouet ~]# perf probe -d sdt_libstdcxx:catch Removed event: sdt_libstdcxx:catch And we need to remove the other event added, i.e. I forgot to add a * at the end: [root@jouet ~]# perf probe -d sdt_libstdcxx:catch* Removed event: sdt_libstdcxx:catch_1 [root@jouet ~]# Ok, disambiguating it using what is in this patch: [root@jouet ~]# perf list sdt_libstdcxx:catch List of pre-defined events (to be used in -e): sdt_libstdcxx:catch@/usr/bin/gcc(9a0730e2bcc6) [SDT event] sdt_libstdcxx:catch@/usr/lib64/libstdc++.so.6.0.22(ef2b7066559a) [SDT event] [root@jouet ~]# [root@jouet ~]# perf probe -a %sdt_libstdcxx:catch@9a07 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 [root@jouet ~]# perf probe -l sdt_libstdcxx:catch (on execute_cfa_program+1551@../../../libgcc/unwind-dw2.c in /usr/bin/gcc) [root@jouet ~]# Yeah, it works! But we need to try and simplify this :-) Update: Some aspects of this simplification take place in the following patches. Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Ananth N Mavinakayanahalli Cc: Brendan Gregg Cc: Hemant Kumar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/146831793746.17065.13065062753978236612.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/build-id.h | 1 + tools/perf/util/probe-event.c | 17 +++++++++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) (limited to 'tools/perf/util/build-id.c') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 36b4279a9002..5651f3c12f93 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -523,6 +523,49 @@ err_out: goto out_free; } +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 *bidlist; + 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)) + return NULL; + + bidlist = build_id_cache__list_all(true); + if (!bidlist) + return NULL; + + strlist__for_each_entry(nd, bidlist) { + 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(bidlist); + + return sbuild_id; +} + char *build_id_cache__cachedir(const char *sbuild_id, const char *name, bool is_kallsyms, bool is_vdso) { diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 64e740f4bc28..d27990610f9f 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -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); struct strlist *build_id_cache__list_all(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); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index c63e3b8704fe..f12081e48a32 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -1251,8 +1251,21 @@ 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 be an SDT 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) { -- cgit v1.2.3