summaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/builtin-probe.c12
-rw-r--r--tools/perf/util/probe-event.c231
-rw-r--r--tools/perf/util/probe-event.h5
3 files changed, 230 insertions, 18 deletions
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index bf20df2e816d..b5d15cf25471 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -62,6 +62,8 @@ static struct {
struct probe_point probes[MAX_PROBES];
} session;
+static bool listing;
+
/* Parse an event definition. Note that any error must die. */
static void parse_probe_event(const char *str)
{
@@ -119,6 +121,7 @@ static int open_default_vmlinux(void)
static const char * const probe_usage[] = {
"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
+ "perf probe --list",
NULL
};
@@ -129,6 +132,7 @@ static const struct option options[] = {
OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
"vmlinux/module pathname"),
#endif
+ OPT_BOOLEAN('l', "list", &listing, "list up current probes"),
OPT_CALLBACK('a', "add", NULL,
#ifdef NO_LIBDWARF
"FUNC[+OFFS|%return] [ARG ...]",
@@ -164,9 +168,15 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
for (i = 0; i < argc; i++)
parse_probe_event(argv[i]);
- if (session.nr_probe == 0)
+ if ((session.nr_probe == 0 && !listing) ||
+ (session.nr_probe != 0 && listing))
usage_with_options(probe_usage, options);
+ if (listing) {
+ show_perf_probe_events();
+ return 0;
+ }
+
if (session.need_dwarf)
#ifdef NO_LIBDWARF
die("Debuginfo-analysis is not supported");
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e3a683ab976f..7f4f288c642e 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -29,10 +29,13 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
#undef _GNU_SOURCE
#include "event.h"
#include "string.h"
+#include "strlist.h"
#include "debug.h"
#include "parse-events.h" /* For debugfs_path */
#include "probe-event.h"
@@ -43,6 +46,19 @@
#define semantic_error(msg ...) die("Semantic error :" msg)
+/* If there is no space to write, returns -E2BIG. */
+static int e_snprintf(char *str, size_t size, const char *format, ...)
+{
+ int ret;
+ va_list ap;
+ va_start(ap, format);
+ ret = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ if (ret >= (int)size)
+ ret = -E2BIG;
+ return ret;
+}
+
/* Parse probepoint definition. */
static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp)
{
@@ -166,6 +182,103 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp)
return need_dwarf;
}
+/* Parse kprobe_events event into struct probe_point */
+void parse_trace_kprobe_event(const char *str, char **group, char **event,
+ struct probe_point *pp)
+{
+ char pr;
+ char *p;
+ int ret, i, argc;
+ char **argv;
+
+ pr_debug("Parsing kprobe_events: %s\n", str);
+ argv = argv_split(str, &argc);
+ if (!argv)
+ die("argv_split failed.");
+ if (argc < 2)
+ semantic_error("Too less arguments.");
+
+ /* Scan event and group name. */
+ ret = sscanf(argv[0], "%c:%m[^/ \t]/%m[^ \t]",
+ &pr, group, event);
+ if (ret != 3)
+ semantic_error("Failed to parse event name: %s", argv[0]);
+ pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr);
+
+ if (!pp)
+ goto end;
+
+ pp->retprobe = (pr == 'r');
+
+ /* Scan function name and offset */
+ ret = sscanf(argv[1], "%m[^+]+%d", &pp->function, &pp->offset);
+ if (ret == 1)
+ pp->offset = 0;
+
+ /* kprobe_events doesn't have this information */
+ pp->line = 0;
+ pp->file = NULL;
+
+ pp->nr_args = argc - 2;
+ pp->args = zalloc(sizeof(char *) * pp->nr_args);
+ for (i = 0; i < pp->nr_args; i++) {
+ p = strchr(argv[i + 2], '=');
+ if (p) /* We don't need which register is assigned. */
+ *p = '\0';
+ pp->args[i] = strdup(argv[i + 2]);
+ if (!pp->args[i])
+ die("Failed to copy argument.");
+ }
+
+end:
+ argv_free(argv);
+}
+
+int synthesize_perf_probe_event(struct probe_point *pp)
+{
+ char *buf;
+ char offs[64] = "", line[64] = "";
+ int i, len, ret;
+
+ pp->probes[0] = buf = zalloc(MAX_CMDLEN);
+ if (!buf)
+ die("Failed to allocate memory by zalloc.");
+ if (pp->offset) {
+ ret = e_snprintf(offs, 64, "+%d", pp->offset);
+ if (ret <= 0)
+ goto error;
+ }
+ if (pp->line) {
+ ret = e_snprintf(line, 64, ":%d", pp->line);
+ if (ret <= 0)
+ goto error;
+ }
+
+ if (pp->function)
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function,
+ offs, pp->retprobe ? "%return" : "", line);
+ else
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->file, line);
+ if (ret <= 0)
+ goto error;
+ len = ret;
+
+ for (i = 0; i < pp->nr_args; i++) {
+ ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
+ pp->args[i]);
+ if (ret <= 0)
+ goto error;
+ len += ret;
+ }
+ pp->found = 1;
+
+ return pp->found;
+error:
+ free(pp->probes[0]);
+
+ return ret;
+}
+
int synthesize_trace_kprobe_event(struct probe_point *pp)
{
char *buf;
@@ -174,15 +287,15 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
pp->probes[0] = buf = zalloc(MAX_CMDLEN);
if (!buf)
die("Failed to allocate memory by zalloc.");
- ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
- if (ret <= 0 || ret >= MAX_CMDLEN)
+ ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset);
+ if (ret <= 0)
goto error;
len = ret;
for (i = 0; i < pp->nr_args; i++) {
- ret = snprintf(&buf[len], MAX_CMDLEN - len, " %s",
- pp->args[i]);
- if (ret <= 0 || ret >= MAX_CMDLEN - len)
+ ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
+ pp->args[i]);
+ if (ret <= 0)
goto error;
len += ret;
}
@@ -191,12 +304,105 @@ int synthesize_trace_kprobe_event(struct probe_point *pp)
return pp->found;
error:
free(pp->probes[0]);
- if (ret > 0)
- ret = -E2BIG;
return ret;
}
+static int open_kprobe_events(int flags, int mode)
+{
+ char buf[PATH_MAX];
+ int ret;
+
+ ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path);
+ if (ret < 0)
+ die("Failed to make kprobe_events path.");
+
+ ret = open(buf, flags, mode);
+ if (ret < 0) {
+ if (errno == ENOENT)
+ die("kprobe_events file does not exist -"
+ " please rebuild with CONFIG_KPROBE_TRACER.");
+ else
+ die("Could not open kprobe_events file: %s",
+ strerror(errno));
+ }
+ return ret;
+}
+
+/* Get raw string list of current kprobe_events */
+static struct strlist *get_trace_kprobe_event_rawlist(int fd)
+{
+ int ret, idx;
+ FILE *fp;
+ char buf[MAX_CMDLEN];
+ char *p;
+ struct strlist *sl;
+
+ sl = strlist__new(true, NULL);
+
+ fp = fdopen(dup(fd), "r");
+ while (!feof(fp)) {
+ p = fgets(buf, MAX_CMDLEN, fp);
+ if (!p)
+ break;
+
+ idx = strlen(p) - 1;
+ if (p[idx] == '\n')
+ p[idx] = '\0';
+ ret = strlist__add(sl, buf);
+ if (ret < 0)
+ die("strlist__add failed: %s", strerror(-ret));
+ }
+ fclose(fp);
+
+ return sl;
+}
+
+/* Free and zero clear probe_point */
+static void clear_probe_point(struct probe_point *pp)
+{
+ int i;
+
+ if (pp->function)
+ free(pp->function);
+ if (pp->file)
+ free(pp->file);
+ for (i = 0; i < pp->nr_args; i++)
+ free(pp->args[i]);
+ if (pp->args)
+ free(pp->args);
+ for (i = 0; i < pp->found; i++)
+ free(pp->probes[i]);
+ memset(pp, 0, sizeof(pp));
+}
+
+/* List up current perf-probe events */
+void show_perf_probe_events(void)
+{
+ unsigned int i;
+ int fd;
+ char *group, *event;
+ struct probe_point pp;
+ struct strlist *rawlist;
+ struct str_node *ent;
+
+ fd = open_kprobe_events(O_RDONLY, 0);
+ rawlist = get_trace_kprobe_event_rawlist(fd);
+ close(fd);
+
+ for (i = 0; i < strlist__nr_entries(rawlist); i++) {
+ ent = strlist__entry(rawlist, i);
+ parse_trace_kprobe_event(ent->s, &group, &event, &pp);
+ synthesize_perf_probe_event(&pp);
+ printf("[%s:%s]\t%s\n", group, event, pp.probes[0]);
+ free(group);
+ free(event);
+ clear_probe_point(&pp);
+ }
+
+ strlist__delete(rawlist);
+}
+
static int write_trace_kprobe_event(int fd, const char *buf)
{
int ret;
@@ -216,16 +422,7 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes)
struct probe_point *pp;
char buf[MAX_CMDLEN];
- snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path);
- fd = open(buf, O_WRONLY, O_APPEND);
- if (fd < 0) {
- if (errno == ENOENT)
- die("kprobe_events file does not exist -"
- " please rebuild with CONFIG_KPROBE_TRACER.");
- else
- die("Could not open kprobe_events file: %s",
- strerror(errno));
- }
+ fd = open_kprobe_events(O_WRONLY, O_APPEND);
for (j = 0; j < nr_probes; j++) {
pp = probes + j;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 0089c455ecac..88db7d1a9472 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -2,9 +2,14 @@
#define _PROBE_EVENT_H
#include "probe-finder.h"
+#include "strlist.h"
extern int parse_perf_probe_event(const char *str, struct probe_point *pp);
+extern int synthesize_perf_probe_event(struct probe_point *pp);
+extern void parse_trace_kprobe_event(const char *str, char **group,
+ char **event, struct probe_point *pp);
extern int synthesize_trace_kprobe_event(struct probe_point *pp);
extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes);
+extern void show_perf_probe_events(void);
#endif /*_PROBE_EVENT_H */