mirror of
https://github.com/FEX-Emu/linux.git
synced 2025-01-03 07:41:40 +00:00
perf buildid-cache: Scan and import user SDT events to probe cache
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 "%". Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com> Cc: Brendan Gregg <brendan.d.gregg@gmail.com> Cc: Hemant Kumar <hemant@linux.vnet.ibm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/146736025058.27797.13043265488541434502.stgit@devbox Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
8d993d9690
commit
6430a94ead
@ -15,6 +15,9 @@ DESCRIPTION
|
|||||||
This command manages the build-id cache. It can add, remove, update and purge
|
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
|
files to/from the cache. In the future it should as well set upper limits for
|
||||||
the space used by the cache, etc.
|
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
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "header.h"
|
#include "header.h"
|
||||||
#include "vdso.h"
|
#include "vdso.h"
|
||||||
|
#include "probe-file.h"
|
||||||
|
|
||||||
|
|
||||||
static bool no_buildid_cache;
|
static bool no_buildid_cache;
|
||||||
@ -532,6 +533,30 @@ int build_id_cache__list_build_ids(const char *pathname,
|
|||||||
return ret;
|
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,
|
int build_id_cache__add_s(const char *sbuild_id, const char *name,
|
||||||
bool is_kallsyms, bool is_vdso)
|
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)
|
if (symlink(tmp, linkname) == 0)
|
||||||
err = 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:
|
out_free:
|
||||||
if (!is_kallsyms)
|
if (!is_kallsyms)
|
||||||
free(realname);
|
free(realname);
|
||||||
|
@ -434,12 +434,15 @@ static int probe_cache__load(struct probe_cache *pcache)
|
|||||||
p = strchr(buf, '\n');
|
p = strchr(buf, '\n');
|
||||||
if (p)
|
if (p)
|
||||||
*p = '\0';
|
*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);
|
entry = probe_cache_entry__new(NULL);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (buf[0] == '%')
|
||||||
|
entry->sdt = true;
|
||||||
entry->spev = strdup(buf + 1);
|
entry->spev = strdup(buf + 1);
|
||||||
if (entry->spev)
|
if (entry->spev)
|
||||||
ret = parse_perf_probe_command(buf + 1,
|
ret = parse_perf_probe_command(buf + 1,
|
||||||
@ -621,19 +624,79 @@ out_err:
|
|||||||
return ret;
|
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)
|
static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
|
||||||
{
|
{
|
||||||
struct str_node *snode;
|
struct str_node *snode;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct iovec iov[3];
|
struct iovec iov[3];
|
||||||
|
const char *prefix = entry->sdt ? "%" : "#";
|
||||||
int ret;
|
int ret;
|
||||||
/* Save stat for rollback */
|
/* Save stat for rollback */
|
||||||
ret = fstat(fd, &st);
|
ret = fstat(fd, &st);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
pr_debug("Writing cache: #%s\n", entry->spev);
|
pr_debug("Writing cache: %s%s\n", prefix, entry->spev);
|
||||||
iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
|
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[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
|
||||||
iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
|
iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
|
||||||
ret = writev(fd, iov, 3);
|
ret = writev(fd, iov, 3);
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
/* Cache of probe definitions */
|
/* Cache of probe definitions */
|
||||||
struct probe_cache_entry {
|
struct probe_cache_entry {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
bool sdt;
|
||||||
struct perf_probe_event pev;
|
struct perf_probe_event pev;
|
||||||
char *spev;
|
char *spev;
|
||||||
struct strlist *tevlist;
|
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,
|
int probe_cache__add_entry(struct probe_cache *pcache,
|
||||||
struct perf_probe_event *pev,
|
struct perf_probe_event *pev,
|
||||||
struct probe_trace_event *tevs, int ntevs);
|
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);
|
int probe_cache__commit(struct probe_cache *pcache);
|
||||||
void probe_cache__purge(struct probe_cache *pcache);
|
void probe_cache__purge(struct probe_cache *pcache);
|
||||||
void probe_cache__delete(struct probe_cache *pcache);
|
void probe_cache__delete(struct probe_cache *pcache);
|
||||||
|
Loading…
Reference in New Issue
Block a user