lkml.org 
[lkml]   [2015]   [Jul]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Subject[RFC PATCH perf/core v2 09/16] perf probe: Add --cache option to cache the probe definitions
    From
    Date
    Add --cache option to cache the probe definitions. This
    just saves the result of the dwarf analysis to probe cache.

    Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>

    ---
    Changes in v2:
    - Update documentations/perf-probe.txt.
    ---
    tools/perf/Documentation/perf-probe.txt | 4
    tools/perf/builtin-probe.c | 1
    tools/perf/util/build-id.c | 13 +
    tools/perf/util/build-id.h | 2
    tools/perf/util/probe-event.c | 136 ++++++++++++--
    tools/perf/util/probe-event.h | 5 +
    tools/perf/util/probe-file.c | 298 +++++++++++++++++++++++++++++++
    tools/perf/util/probe-file.h | 20 ++
    8 files changed, 447 insertions(+), 32 deletions(-)

    diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
    index 3a8a9ba..947db6f 100644
    --- a/tools/perf/Documentation/perf-probe.txt
    +++ b/tools/perf/Documentation/perf-probe.txt
    @@ -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.

    diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
    index b81cec3..5ac8a79 100644
    --- a/tools/perf/builtin-probe.c
    +++ b/tools/perf/builtin-probe.c
    @@ -396,6 +396,7 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
    opt_set_filter),
    OPT_CALLBACK('x', "exec", NULL, "executable|path",
    "target executable name or path", opt_set_target),
    + OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
    OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
    "Enable symbol demangling"),
    OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
    diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
    index 02a5e0d..169639e 100644
    --- a/tools/perf/util/build-id.c
    +++ b/tools/perf/util/build-id.c
    @@ -313,9 +313,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__dirname_from_path(const char *sbuild_id, const char *name,
    + bool is_kallsyms, bool is_vdso)
    {
    char *realname = (char *)name, *filename;
    bool slash = is_kallsyms || is_vdso;
    @@ -344,8 +343,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,
    - NULL);
    + dir_name = build_id_cache__dirname_from_path(NULL, pathname,
    + false, false);
    if (!dir_name)
    return -ENOMEM;

    @@ -372,8 +371,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__dirname_from_path(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 27a14a8..a1f428d 100644
    --- a/tools/perf/util/build-id.h
    +++ b/tools/perf/util/build-id.h
    @@ -27,6 +27,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__dirname_from_path(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);
    diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
    index b6ed15e..fc30c2d 100644
    --- a/tools/perf/util/probe-event.c
    +++ b/tools/perf/util/probe-event.c
    @@ -68,7 +68,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 */
    @@ -1593,7 +1592,7 @@ char *synthesize_perf_probe_arg(struct perf_probe_arg *pa)
    }

    /* 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;
    @@ -1626,30 +1625,36 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
    return tmp;
    }

    -#if 0
    char *synthesize_perf_probe_command(struct perf_probe_event *pev)
    {
    - char *buf;
    - int i, len, ret;
    + struct strbuf buf;
    + char *tmp;
    + int i;

    - buf = synthesize_perf_probe_point(&pev->point);
    - if (!buf)
    - return NULL;
    + strbuf_init(&buf, 64);
    + if (pev->event)
    + strbuf_addf(&buf, "%s:%s=", pev->group ?: PERFPROBE_GROUP,
    + pev->event);
    +
    + tmp = synthesize_perf_probe_point(&pev->point);
    + if (!tmp)
    + goto out;
    + strbuf_addstr(&buf, tmp);
    + 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)
    + goto out;
    + strbuf_addf(&buf, " %s", tmp);
    + free(tmp);
    }

    - return buf;
    + tmp = strbuf_detach(&buf, NULL);
    +out:
    + strbuf_release(&buf);
    + return tmp;
    }
    -#endif

    static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
    struct strbuf *buf, int depth)
    @@ -1730,7 +1735,6 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
    if (synthesize_probe_trace_arg(&tev->args[i], &buf) < 0)
    goto error;
    }
    -
    ret = strbuf_detach(&buf, NULL);
    error:
    strbuf_release(&buf);
    @@ -1876,6 +1880,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;
    @@ -2262,15 +2339,17 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
    }

    static int __add_probe_trace_events(struct perf_probe_event *pev,
    - struct probe_trace_event *tevs,
    - int ntevs, bool allow_suffix)
    + struct probe_trace_event *tevs,
    + int ntevs, bool allow_suffix)
    {
    int i, fd, ret;
    struct probe_trace_event *tev = NULL;
    const char *event = NULL, *group = NULL;
    + struct probe_cache *cache = NULL;
    struct strlist *namelist;
    + int flag = PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0);

    - fd = probe_file__open(PF_FL_RW | (pev->uprobes ? PF_FL_UPROBE : 0));
    + fd = probe_file__open(flag);
    if (fd < 0)
    return fd;

    @@ -2320,6 +2399,16 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,

    /* Note that it is possible to skip all events because of blacklist */
    if (ret >= 0 && event) {
    + if (probe_conf.cache) {
    + cache = probe_cache__new(pev->target);
    + if (!cache)
    + pr_info("Warning: Failed to add cache\n");
    + else {
    + probe_cache__add_entry(cache, pev, tevs, ntevs);
    + probe_cache__commit(cache);
    + probe_cache__delete(cache);
    + }
    + }
    /* Show how to use the event. */
    pr_info("\nYou can now use it in all perf tools, such as:\n\n");
    pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
    @@ -2352,9 +2441,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) { }
    diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
    index 4cff6be..9aceba3 100644
    --- a/tools/perf/util/probe-event.h
    +++ b/tools/perf/util/probe-event.h
    @@ -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;
    @@ -116,6 +117,10 @@ extern int parse_probe_trace_command(const char *cmd,
    extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
    extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
    extern 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);

    /* Check the perf_probe_event needs debuginfo */
    extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
    diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
    index 2a7cc8c..572e8c2 100644
    --- a/tools/perf/util/probe-file.c
    +++ b/tools/perf/util/probe-file.c
    @@ -14,6 +14,7 @@
    * GNU General Public License for more details.
    *
    */
    +#include <sys/uio.h>
    #include "util.h"
    #include "event.h"
    #include "strlist.h"
    @@ -300,3 +301,300 @@ 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(true, 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 = "[kernel.kallsyms]";
    + ret = sysfs__sprintf_build_id("/", sbuildid);
    + }
    + if (ret < 0) {
    + pr_warning("Failed to get build-id from %s.\n", target ?: "kernel");
    + 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_warning("Failed to add cache: %s\n", target);
    + return ret;
    + }
    + }
    +
    + dir_name = build_id_cache__dirname_from_path(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 | O_APPEND, 0644);
    + if (fd < 0)
    + pr_warning("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);
    + entry->spev = strdup(buf + 1);
    + ret = parse_perf_probe_command(buf + 1, &entry->pev);
    + if (!entry->spev || 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;
    +}
    diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
    index ada94a2..7b473b39 100644
    --- a/tools/perf/util/probe-file.h
    +++ b/tools/perf/util/probe-file.h
    @@ -15,4 +15,24 @@ struct strlist *probe_file__get_rawlist(int fd);
    int probe_file__add_event(int fd, struct probe_trace_event *tev);
    int probe_file__del_events(int fd, struct strfilter *filter);

    +/* 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;
    +};
    +
    +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



    \
     
     \ /
      Last update: 2015-07-15 11:41    [W:4.391 / U:1.336 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site