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...@hitachi.com>
---
 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 +++
 7 files changed, 443 insertions(+), 32 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 1272559..97f4d07 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -397,6 +397,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 707be38..ea40d1da 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 */
@@ -1548,7 +1547,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;
@@ -1581,30 +1580,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)
@@ -1685,7 +1690,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);
@@ -1831,6 +1835,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;
@@ -2191,14 +2268,16 @@ 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;
+       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;
 
@@ -2244,6 +2323,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 && tev->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", tev->group,
@@ -2273,9 +2362,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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to