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: Masami 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: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
		
							parent
							
								
									e1c01d61a9
								
							
						
					
					
						commit
						4de189fe6e
					
				| @ -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"); | ||||
|  | ||||
| @ -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,23 +182,91 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | ||||
| 	return need_dwarf; | ||||
| } | ||||
| 
 | ||||
| int synthesize_trace_kprobe_event(struct probe_point *pp) | ||||
| /* 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."); | ||||
| 	ret = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | ||||
| 	if (ret <= 0 || ret >= MAX_CMDLEN) | ||||
| 	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 = 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 +275,134 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) | ||||
| 	return pp->found; | ||||
| error: | ||||
| 	free(pp->probes[0]); | ||||
| 	if (ret > 0) | ||||
| 		ret = -E2BIG; | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int synthesize_trace_kprobe_event(struct probe_point *pp) | ||||
| { | ||||
| 	char *buf; | ||||
| 	int i, len, ret; | ||||
| 
 | ||||
| 	pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||||
| 	if (!buf) | ||||
| 		die("Failed to allocate memory by zalloc."); | ||||
| 	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 = 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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
|  | ||||
| @ -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 */ | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user