diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/Makefile | 4 | ||||
-rw-r--r-- | tools/perf/builtin-annotate.c | 143 | ||||
-rw-r--r-- | tools/perf/builtin-probe.c | 241 | ||||
-rw-r--r-- | tools/perf/builtin-timechart.c | 170 | ||||
-rw-r--r-- | tools/perf/util/event.c | 3 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 484 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 18 | ||||
-rw-r--r-- | tools/perf/util/string.c | 103 | ||||
-rw-r--r-- | tools/perf/util/string.h | 2 |
9 files changed, 681 insertions, 487 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 20cd66362d2d..23ec66098bdc 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -369,6 +369,8 @@ LIB_H += util/sort.h LIB_H += util/hist.h LIB_H += util/thread.h LIB_H += util/data_map.h +LIB_H += util/probe-finder.h +LIB_H += util/probe-event.h LIB_OBJS += util/abspath.o LIB_OBJS += util/alias.o @@ -412,6 +414,7 @@ LIB_OBJS += util/svghelper.o LIB_OBJS += util/sort.o LIB_OBJS += util/hist.o LIB_OBJS += util/data_map.o +LIB_OBJS += util/probe-event.o BUILTIN_OBJS += builtin-annotate.o @@ -486,7 +489,6 @@ ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <lib BASIC_CFLAGS += -DNO_LIBDWARF else EXTLIBS += -lelf -ldwarf - LIB_H += util/probe-finder.h LIB_OBJS += util/probe-finder.o endif diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 7f85c6e159a4..0bf2e8f9af57 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -25,19 +25,16 @@ #include "util/thread.h" #include "util/sort.h" #include "util/hist.h" +#include "util/data_map.h" static char const *input_name = "perf.data"; static int force; -static int input; static int full_paths; static int print_line; -static unsigned long page_size; -static unsigned long mmap_window = 32; - struct sym_hist { u64 sum; u64 ip[0]; @@ -156,35 +153,6 @@ static int process_sample_event(event_t *event) return 0; } -static int event__process(event_t *self) -{ - switch (self->header.type) { - case PERF_RECORD_SAMPLE: - return process_sample_event(self); - - case PERF_RECORD_MMAP: - return event__process_mmap(self); - - case PERF_RECORD_COMM: - return event__process_comm(self); - - case PERF_RECORD_FORK: - return event__process_task(self); - /* - * We dont process them right now but they are fine: - */ - - case PERF_RECORD_THROTTLE: - case PERF_RECORD_UNTHROTTLE: - return 0; - - default: - return -1; - } - - return 0; -} - static int parse_line(FILE *file, struct hist_entry *he, u64 len) { struct symbol *sym = he->sym; @@ -485,99 +453,26 @@ static void find_annotations(void) } } +static struct perf_file_handler file_handler = { + .process_sample_event = process_sample_event, + .process_mmap_event = event__process_mmap, + .process_comm_event = event__process_comm, + .process_fork_event = event__process_task, +}; + static int __cmd_annotate(void) { - int ret, rc = EXIT_FAILURE; - unsigned long offset = 0; - unsigned long head = 0; - struct stat input_stat; - event_t *event; - uint32_t size; - char *buf; - - register_idle_thread(); - - input = open(input_name, O_RDONLY); - if (input < 0) { - perror("failed to open file"); - exit(-1); - } - - ret = fstat(input, &input_stat); - if (ret < 0) { - perror("failed to stat file"); - exit(-1); - } - - if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { - fprintf(stderr, "file: %s not owned by current user or root\n", input_name); - exit(-1); - } - - if (!input_stat.st_size) { - fprintf(stderr, "zero-sized file, nothing to do!\n"); - exit(0); - } - -remap: - buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, - MAP_SHARED, input, offset); - if (buf == MAP_FAILED) { - perror("failed to mmap file"); - exit(-1); - } - -more: - event = (event_t *)(buf + head); - - size = event->header.size; - if (!size) - size = 8; - - if (head + event->header.size >= page_size * mmap_window) { - unsigned long shift = page_size * (head / page_size); - int munmap_ret; - - munmap_ret = munmap(buf, page_size * mmap_window); - assert(munmap_ret == 0); - - offset += shift; - head -= shift; - goto remap; - } - - size = event->header.size; - - dump_printf("%p [%p]: event: %d\n", - (void *)(offset + head), - (void *)(long)event->header.size, - event->header.type); - - if (!size || event__process(event) < 0) { - - dump_printf("%p [%p]: skipping unknown header type: %d\n", - (void *)(offset + head), - (void *)(long)(event->header.size), - event->header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; - } - - head += size; - - if (offset + head < (unsigned long)input_stat.st_size) - goto more; + struct perf_header *header; + struct thread *idle; + int ret; - rc = EXIT_SUCCESS; - close(input); + idle = register_idle_thread(); + register_perf_file_handler(&file_handler); + ret = mmap_dispatch_perf_file(&header, input_name, 0, 0, + &event__cwdlen, &event__cwd); + if (ret) + return ret; if (dump_trace) { event__print_totals(); @@ -595,7 +490,7 @@ more: find_annotations(); - return rc; + return ret; } static const char * const annotate_usage[] = { @@ -644,8 +539,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) if (symbol__init(&symbol_conf) < 0) return -1; - page_size = getpagesize(); - argc = parse_options(argc, argv, options, annotate_usage, 0); setup_sorting(); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index a2f6daf01ecb..a58e11b7ea80 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -40,6 +40,7 @@ #include "util/parse-options.h" #include "util/parse-events.h" /* For debugfs_path */ #include "util/probe-finder.h" +#include "util/probe-event.h" /* Default vmlinux search paths */ #define NR_SEARCH_PATH 3 @@ -51,8 +52,6 @@ const char *default_search_path[NR_SEARCH_PATH] = { #define MAX_PATH_LEN 256 #define MAX_PROBES 128 -#define MAX_PROBE_ARGS 128 -#define PERFPROBE_GROUP "probe" /* Session management structure */ static struct { @@ -63,152 +62,19 @@ static struct { struct probe_point probes[MAX_PROBES]; } session; -#define semantic_error(msg ...) die("Semantic error :" msg) - -/* Parse probe point. Return 1 if return probe */ -static void parse_probe_point(char *arg, struct probe_point *pp) -{ - char *ptr, *tmp; - char c, nc = 0; - /* - * <Syntax> - * perf probe SRC:LN - * perf probe FUNC[+OFFS|%return][@SRC] - */ - - ptr = strpbrk(arg, ":+@%"); - if (ptr) { - nc = *ptr; - *ptr++ = '\0'; - } - - /* Check arg is function or file and copy it */ - if (strchr(arg, '.')) /* File */ - pp->file = strdup(arg); - else /* Function */ - pp->function = strdup(arg); - DIE_IF(pp->file == NULL && pp->function == NULL); - - /* Parse other options */ - while (ptr) { - arg = ptr; - c = nc; - ptr = strpbrk(arg, ":+@%"); - if (ptr) { - nc = *ptr; - *ptr++ = '\0'; - } - switch (c) { - case ':': /* Line number */ - pp->line = strtoul(arg, &tmp, 0); - if (*tmp != '\0') - semantic_error("There is non-digit charactor" - " in line number."); - break; - case '+': /* Byte offset from a symbol */ - pp->offset = strtoul(arg, &tmp, 0); - if (*tmp != '\0') - semantic_error("There is non-digit charactor" - " in offset."); - break; - case '@': /* File name */ - if (pp->file) - semantic_error("SRC@SRC is not allowed."); - pp->file = strdup(arg); - DIE_IF(pp->file == NULL); - if (ptr) - semantic_error("@SRC must be the last " - "option."); - break; - case '%': /* Probe places */ - if (strcmp(arg, "return") == 0) { - pp->retprobe = 1; - } else /* Others not supported yet */ - semantic_error("%%%s is not supported.", arg); - break; - default: - DIE_IF("Program has a bug."); - break; - } - } - - /* Exclusion check */ - if (pp->line && pp->offset) - semantic_error("Offset can't be used with line number."); - if (!pp->line && pp->file && !pp->function) - semantic_error("File always requires line number."); - if (pp->offset && !pp->function) - semantic_error("Offset requires an entry function."); - if (pp->retprobe && !pp->function) - semantic_error("Return probe requires an entry function."); - if ((pp->offset || pp->line) && pp->retprobe) - semantic_error("Offset/Line can't be used with return probe."); - - pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", - pp->function, pp->file, pp->line, pp->offset, pp->retprobe); -} +static bool listing; /* Parse an event definition. Note that any error must die. */ static void parse_probe_event(const char *str) { - char *argv[MAX_PROBE_ARGS + 2]; /* Event + probe + args */ - int argc, i; struct probe_point *pp = &session.probes[session.nr_probe]; pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); if (++session.nr_probe == MAX_PROBES) - semantic_error("Too many probes"); - - /* Separate arguments, similar to argv_split */ - argc = 0; - do { - /* Skip separators */ - while (isspace(*str)) - str++; - - /* Add an argument */ - if (*str != '\0') { - const char *s = str; - - /* Skip the argument */ - while (!isspace(*str) && *str != '\0') - str++; - - /* Duplicate the argument */ - argv[argc] = strndup(s, str - s); - if (argv[argc] == NULL) - die("strndup"); - if (++argc == MAX_PROBE_ARGS) - semantic_error("Too many arguments"); - pr_debug("argv[%d]=%s\n", argc, argv[argc - 1]); - } - } while (*str != '\0'); - if (!argc) - semantic_error("An empty argument."); - - /* Parse probe point */ - parse_probe_point(argv[0], pp); - free(argv[0]); - if (pp->file || pp->line) - session.need_dwarf = 1; - - /* Copy arguments */ - pp->nr_args = argc - 1; - if (pp->nr_args > 0) { - pp->args = (char **)malloc(sizeof(char *) * pp->nr_args); - if (!pp->args) - die("malloc"); - memcpy(pp->args, &argv[1], sizeof(char *) * pp->nr_args); - } + die("Too many probes (> %d) are specified.", MAX_PROBES); - /* Ensure return probe has no C argument */ - for (i = 0; i < pp->nr_args; i++) - if (is_c_varname(pp->args[i])) { - if (pp->retprobe) - semantic_error("You can't specify local" - " variable for kretprobe"); - session.need_dwarf = 1; - } + /* Parse perf-probe event into probe_point */ + session.need_dwarf = parse_perf_probe_event(str, pp); pr_debug("%d arguments\n", pp->nr_args); } @@ -255,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 }; @@ -265,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 ...]", @@ -285,73 +153,38 @@ static const struct option options[] = { "\t\tALN:\tAbsolute line number in file.\n" "\t\tARG:\tProbe argument (local variable name or\n" #endif - "\t\t\tkprobe-tracer argument format is supported.)\n", + "\t\t\tkprobe-tracer argument format.)\n", opt_add_probe_event), OPT_END() }; -static int write_new_event(int fd, const char *buf) -{ - int ret; - - ret = write(fd, buf, strlen(buf)); - if (ret <= 0) - die("Failed to create event."); - else - printf("Added new event: %s\n", buf); - - return ret; -} - -#define MAX_CMDLEN 256 - -static int synthesize_probe_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 = snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); - if (ret <= 0 || ret >= MAX_CMDLEN) - 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) - goto error; - len += ret; - } - pp->found = 1; - return pp->found; -error: - free(pp->probes[0]); - if (ret > 0) - ret = -E2BIG; - return ret; -} - int cmd_probe(int argc, const char **argv, const char *prefix __used) { - int i, j, fd, ret; + int i, j, ret; +#ifndef NO_LIBDWARF + int fd; +#endif struct probe_point *pp; - char buf[MAX_CMDLEN]; argc = parse_options(argc, argv, options, probe_usage, PARSE_OPT_STOP_AT_NON_OPTION); 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 - semantic_error("Debuginfo-analysis is not supported"); + die("Debuginfo-analysis is not supported"); #else /* !NO_LIBDWARF */ - pr_info("Some probes require debuginfo.\n"); + pr_debug("Some probes require debuginfo.\n"); if (session.vmlinux) fd = open(session.vmlinux, O_RDONLY); @@ -395,41 +228,15 @@ end_dwarf: if (pp->found) /* This probe is already found. */ continue; - ret = synthesize_probe_event(pp); + ret = synthesize_trace_kprobe_event(pp); if (ret == -E2BIG) - semantic_error("probe point is too long."); + die("probe point definition becomes too long."); else if (ret < 0) die("Failed to synthesize a probe point."); } /* Settng up probe points */ - 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)); - } - for (j = 0; j < session.nr_probe; j++) { - pp = &session.probes[j]; - if (pp->found == 1) { - snprintf(buf, MAX_CMDLEN, "%c:%s/%s_%x %s\n", - pp->retprobe ? 'r' : 'p', PERFPROBE_GROUP, - pp->function, pp->offset, pp->probes[0]); - write_new_event(fd, buf); - } else - for (i = 0; i < pp->found; i++) { - snprintf(buf, MAX_CMDLEN, "%c:%s/%s_%x_%d %s\n", - pp->retprobe ? 'r' : 'p', - PERFPROBE_GROUP, - pp->function, pp->offset, i, - pp->probes[0]); - write_new_event(fd, buf); - } - } - close(fd); + add_trace_kprobe_events(session.probes, session.nr_probe); return 0; } diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index dd4d82ac7aa4..cb58b6605fcc 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -29,14 +29,14 @@ #include "util/header.h" #include "util/parse-options.h" #include "util/parse-events.h" +#include "util/event.h" +#include "util/data_map.h" #include "util/svghelper.h" static char const *input_name = "perf.data"; static char const *output_name = "output.svg"; -static unsigned long page_size; -static unsigned long mmap_window = 32; static u64 sample_type; static unsigned int numcpus; @@ -49,8 +49,6 @@ static u64 first_time, last_time; static int power_only; -static struct perf_header *header; - struct per_pid; struct per_pidcomm; @@ -156,9 +154,9 @@ struct sample_wrapper *all_samples; struct process_filter; struct process_filter { - char *name; - int pid; - struct process_filter *next; + char *name; + int pid; + struct process_filter *next; }; static struct process_filter *process_filter; @@ -1045,36 +1043,6 @@ static void write_svg_file(const char *filename) svg_close(); } -static int -process_event(event_t *event) -{ - - switch (event->header.type) { - - case PERF_RECORD_COMM: - return process_comm_event(event); - case PERF_RECORD_FORK: - return process_fork_event(event); - case PERF_RECORD_EXIT: - return process_exit_event(event); - case PERF_RECORD_SAMPLE: - return queue_sample_event(event); - - /* - * We dont process them right now but they are fine: - */ - case PERF_RECORD_MMAP: - case PERF_RECORD_THROTTLE: - case PERF_RECORD_UNTHROTTLE: - return 0; - - default: - return -1; - } - - return 0; -} - static void process_samples(void) { struct sample_wrapper *cursor; @@ -1090,114 +1058,38 @@ static void process_samples(void) } } - -static int __cmd_timechart(void) +static int sample_type_check(u64 type) { - int err, rc = EXIT_FAILURE; - unsigned long offset = 0; - unsigned long head, shift; - struct stat statbuf; - event_t *event; - uint32_t size; - char *buf; - int input; - - input = open(input_name, O_RDONLY); - if (input < 0) { - fprintf(stderr, " failed to open file: %s", input_name); - if (!strcmp(input_name, "perf.data")) - fprintf(stderr, " (try 'perf record' first)"); - fprintf(stderr, "\n"); - exit(-1); - } - - err = fstat(input, &statbuf); - if (err < 0) { - perror("failed to stat file"); - exit(-1); - } - - if (!statbuf.st_size) { - fprintf(stderr, "zero-sized file, nothing to do!\n"); - exit(0); - } + sample_type = type; - header = perf_header__new(); - if (header == NULL) - return -ENOMEM; - - err = perf_header__read(header, input); - if (err < 0) { - perf_header__delete(header); - return err; - } - - head = header->data_offset; - - sample_type = perf_header__sample_type(header); - - shift = page_size * (head / page_size); - offset += shift; - head -= shift; - -remap: - buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, - MAP_SHARED, input, offset); - if (buf == MAP_FAILED) { - perror("failed to mmap file"); - exit(-1); - } - -more: - event = (event_t *)(buf + head); - - size = event->header.size; - if (!size) - size = 8; - - if (head + event->header.size >= page_size * mmap_window) { - int ret2; - - shift = page_size * (head / page_size); - - ret2 = munmap(buf, page_size * mmap_window); - assert(ret2 == 0); - - offset += shift; - head -= shift; - goto remap; - } - - size = event->header.size; - - if (!size || process_event(event) < 0) { - pr_warning("%p [%p]: skipping unknown header type: %d\n", - (void *)(offset + head), - (void *)(long)(event->header.size), - event->header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; + if (!(sample_type & PERF_SAMPLE_RAW)) { + fprintf(stderr, "No trace samples found in the file.\n" + "Have you used 'perf timechart record' to record it?\n"); + return -1; } - head += size; + return 0; +} - if (offset + head >= header->data_offset + header->data_size) - goto done; +static struct perf_file_handler file_handler = { + .process_comm_event = process_comm_event, + .process_fork_event = process_fork_event, + .process_exit_event = process_exit_event, + .process_sample_event = queue_sample_event, + .sample_type_check = sample_type_check, +}; - if (offset + head < (unsigned long)statbuf.st_size) - goto more; +static int __cmd_timechart(void) +{ + struct perf_header *header; + int ret; -done: - rc = EXIT_SUCCESS; - close(input); + register_perf_file_handler(&file_handler); + ret = mmap_dispatch_perf_file(&header, input_name, 0, 0, + &event__cwdlen, &event__cwd); + if (ret) + return EXIT_FAILURE; process_samples(); @@ -1210,7 +1102,7 @@ done: pr_info("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name); - return rc; + return EXIT_SUCCESS; } static const char * const timechart_usage[] = { @@ -1277,8 +1169,6 @@ int cmd_timechart(int argc, const char **argv, const char *prefix __used) { symbol__init(0); - page_size = getpagesize(); - argc = parse_options(argc, argv, options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 233d7ad9bd7f..414b89d1bde9 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -186,8 +186,7 @@ int event__process_comm(event_t *self) { struct thread *thread = threads__findnew(self->comm.pid); - dump_printf("PERF_RECORD_COMM: %s:%d\n", - self->comm.comm, self->comm.pid); + dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c new file mode 100644 index 000000000000..cd7fbda5e2a5 --- /dev/null +++ b/tools/perf/util/probe-event.c @@ -0,0 +1,484 @@ +/* + * probe-event.c : perf-probe definition to kprobe_events format converter + * + * Written by Masami Hiramatsu <mhiramat@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#define _GNU_SOURCE +#include <sys/utsname.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#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" + +#define MAX_CMDLEN 256 +#define MAX_PROBE_ARGS 128 +#define PERFPROBE_GROUP "probe" + +#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) +{ + char *ptr, *tmp; + char c, nc = 0; + /* + * <Syntax> + * perf probe SRC:LN + * perf probe FUNC[+OFFS|%return][@SRC] + */ + + ptr = strpbrk(arg, ":+@%"); + if (ptr) { + nc = *ptr; + *ptr++ = '\0'; + } + + /* Check arg is function or file and copy it */ + if (strchr(arg, '.')) /* File */ + pp->file = strdup(arg); + else /* Function */ + pp->function = strdup(arg); + DIE_IF(pp->file == NULL && pp->function == NULL); + + /* Parse other options */ + while (ptr) { + arg = ptr; + c = nc; + ptr = strpbrk(arg, ":+@%"); + if (ptr) { + nc = *ptr; + *ptr++ = '\0'; + } + switch (c) { + case ':': /* Line number */ + pp->line = strtoul(arg, &tmp, 0); + if (*tmp != '\0') + semantic_error("There is non-digit charactor" + " in line number."); + break; + case '+': /* Byte offset from a symbol */ + pp->offset = strtoul(arg, &tmp, 0); + if (*tmp != '\0') + semantic_error("There is non-digit charactor" + " in offset."); + break; + case '@': /* File name */ + if (pp->file) + semantic_error("SRC@SRC is not allowed."); + pp->file = strdup(arg); + DIE_IF(pp->file == NULL); + if (ptr) + semantic_error("@SRC must be the last " + "option."); + break; + case '%': /* Probe places */ + if (strcmp(arg, "return") == 0) { + pp->retprobe = 1; + } else /* Others not supported yet */ + semantic_error("%%%s is not supported.", arg); + break; + default: + DIE_IF("Program has a bug."); + break; + } + } + + /* Exclusion check */ + if (pp->line && pp->offset) + semantic_error("Offset can't be used with line number."); + + if (!pp->line && pp->file && !pp->function) + semantic_error("File always requires line number."); + + if (pp->offset && !pp->function) + semantic_error("Offset requires an entry function."); + + if (pp->retprobe && !pp->function) + semantic_error("Return probe requires an entry function."); + + if ((pp->offset || pp->line) && pp->retprobe) + semantic_error("Offset/Line can't be used with return probe."); + + pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", + pp->function, pp->file, pp->line, pp->offset, pp->retprobe); +} + +/* Parse perf-probe event definition */ +int parse_perf_probe_event(const char *str, struct probe_point *pp) +{ + char **argv; + int argc, i, need_dwarf = 0; + + argv = argv_split(str, &argc); + if (!argv) + die("argv_split failed."); + if (argc > MAX_PROBE_ARGS + 1) + semantic_error("Too many arguments"); + + /* Parse probe point */ + parse_perf_probe_probepoint(argv[0], pp); + if (pp->file || pp->line) + need_dwarf = 1; + + /* Copy arguments and ensure return probe has no C argument */ + pp->nr_args = argc - 1; + pp->args = zalloc(sizeof(char *) * pp->nr_args); + for (i = 0; i < pp->nr_args; i++) { + pp->args[i] = strdup(argv[i + 1]); + if (!pp->args[i]) + die("Failed to copy argument."); + if (is_c_varname(pp->args[i])) { + if (pp->retprobe) + semantic_error("You can't specify local" + " variable for kretprobe"); + need_dwarf = 1; + } + } + + argv_free(argv); + 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:%a[^/ \t]/%a[^ \t]", + &pr, (float *)(void *)group, (float *)(void *)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], "%a[^+]+%d", (float *)(void *)&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; + 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); +} + +/* Get current perf-probe event names */ +static struct strlist *get_perf_event_names(int fd) +{ + unsigned int i; + char *group, *event; + struct strlist *sl, *rawlist; + struct str_node *ent; + + rawlist = get_trace_kprobe_event_rawlist(fd); + + sl = strlist__new(false, NULL); + for (i = 0; i < strlist__nr_entries(rawlist); i++) { + ent = strlist__entry(rawlist, i); + parse_trace_kprobe_event(ent->s, &group, &event, NULL); + strlist__add(sl, event); + free(group); + } + + strlist__delete(rawlist); + + return sl; +} + +static int write_trace_kprobe_event(int fd, const char *buf) +{ + int ret; + + ret = write(fd, buf, strlen(buf)); + if (ret <= 0) + die("Failed to create event."); + else + printf("Added new event: %s\n", buf); + + return ret; +} + +static void get_new_event_name(char *buf, size_t len, const char *base, + struct strlist *namelist) +{ + int i, ret; + for (i = 0; i < MAX_EVENT_INDEX; i++) { + ret = e_snprintf(buf, len, "%s_%d", base, i); + if (ret < 0) + die("snprintf() failed: %s", strerror(-ret)); + if (!strlist__has_entry(namelist, buf)) + break; + } + if (i == MAX_EVENT_INDEX) + die("Too many events are on the same function."); +} + +void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) +{ + int i, j, fd; + struct probe_point *pp; + char buf[MAX_CMDLEN]; + char event[64]; + struct strlist *namelist; + + fd = open_kprobe_events(O_RDWR, O_APPEND); + /* Get current event names */ + namelist = get_perf_event_names(fd); + + for (j = 0; j < nr_probes; j++) { + pp = probes + j; + for (i = 0; i < pp->found; i++) { + /* Get an unused new event name */ + get_new_event_name(event, 64, pp->function, namelist); + snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", + pp->retprobe ? 'r' : 'p', + PERFPROBE_GROUP, event, + pp->probes[i]); + write_trace_kprobe_event(fd, buf); + /* Add added event name to namelist */ + strlist__add(namelist, event); + } + } + close(fd); +} diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h new file mode 100644 index 000000000000..0c6fe56fe38a --- /dev/null +++ b/tools/perf/util/probe-event.h @@ -0,0 +1,18 @@ +#ifndef _PROBE_EVENT_H +#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); + +/* Maximum index number of event-name postfix */ +#define MAX_EVENT_INDEX 1024 + +#endif /*_PROBE_EVENT_H */ diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 227043577e06..f24a8cc933d5 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -1,5 +1,3 @@ -#include <string.h> -#include <stdlib.h> #include "string.h" #include "util.h" @@ -127,3 +125,104 @@ out_err: out: return length; } + +/* + * Helper function for splitting a string into an argv-like array. + * originaly copied from lib/argv_split.c + */ +static const char *skip_sep(const char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + + return cp; +} + +static const char *skip_arg(const char *cp) +{ + while (*cp && !isspace(*cp)) + cp++; + + return cp; +} + +static int count_argc(const char *str) +{ + int count = 0; + + while (*str) { + str = skip_sep(str); + if (*str) { + count++; + str = skip_arg(str); + } + } + + return count; +} + +/** + * argv_free - free an argv + * @argv - the argument vector to be freed + * + * Frees an argv and the strings it points to. + */ +void argv_free(char **argv) +{ + char **p; + for (p = argv; *p; p++) + free(*p); + + free(argv); +} + +/** + * argv_split - split a string at whitespace, returning an argv + * @str: the string to be split + * @argcp: returned argument count + * + * Returns an array of pointers to strings which are split out from + * @str. This is performed by strictly splitting on white-space; no + * quote processing is performed. Multiple whitespace characters are + * considered to be a single argument separator. The returned array + * is always NULL-terminated. Returns NULL on memory allocation + * failure. + */ +char **argv_split(const char *str, int *argcp) +{ + int argc = count_argc(str); + char **argv = zalloc(sizeof(*argv) * (argc+1)); + char **argvp; + + if (argv == NULL) + goto out; + + if (argcp) + *argcp = argc; + + argvp = argv; + + while (*str) { + str = skip_sep(str); + + if (*str) { + const char *p = str; + char *t; + + str = skip_arg(str); + + t = strndup(p, str-p); + if (t == NULL) + goto fail; + *argvp++ = t; + } + } + *argvp = NULL; + +out: + return argv; + +fail: + argv_free(argv); + return NULL; +} diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index e50b07f80827..bfecec265a1a 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h @@ -6,6 +6,8 @@ int hex2u64(const char *ptr, u64 *val); char *strxfrchar(char *s, char from, char to); s64 perf_atoll(const char *str); +char **argv_split(const char *str, int *argcp); +void argv_free(char **argv); #define _STR(x) #x #define STR(x) _STR(x) |