Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4de189fe authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Ingo Molnar
Browse files

perf probe: Add --list option for listing current probe events



Add --list option for listing currently defined probe events
in the kernel. This shows events in below format;

 [group:event]	<perf-probe probe-definition>

for example:

 [probe:schedule_0]	schedule+30 cpu

Note that source file/line information is not supported yet.
So even if you added a probe by line, it will be shown in
<symbol+offset>.

Signed-off-by: default avatarMasami Hiramatsu <mhiramat@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20091201002017.10235.76575.stgit@harusame>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent e1c01d61
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -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");
+214 −17
Original line number Diff line number Diff line
@@ -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",
		ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s",
				 pp->args[i]);
		if (ret <= 0 || ret >= MAX_CMDLEN - len)
		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;
+5 −0
Original line number Diff line number Diff line
@@ -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 */