#include "perf.h"
#include "builtin.h"
#include "util/util.h"
+#include "util/strlist.h"
#include "util/event.h"
#include "util/debug.h"
+#include "util/debugfs.h"
+#include "util/symbol.h"
+#include "util/thread.h"
#include "util/parse-options.h"
#include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h"
-
-/* Default vmlinux search paths */
-#define NR_SEARCH_PATH 3
-const char *default_search_path[NR_SEARCH_PATH] = {
-"/lib/modules/%s/build/vmlinux", /* Custom build kernel */
-"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
-"/boot/vmlinux-debug-%s", /* Ubuntu */
-};
+#include "util/probe-event.h"
#define MAX_PATH_LEN 256
#define MAX_PROBES 128
-#define MAX_PROBE_ARGS 128
/* Session management structure */
static struct {
- char *vmlinux;
- char *release;
- int need_dwarf;
+ bool need_dwarf;
+ bool list_events;
+ bool force_add;
+ bool show_lines;
int nr_probe;
struct probe_point probes[MAX_PROBES];
- char *events[MAX_PROBES];
+ struct strlist *dellist;
+ struct map_groups kmap_groups;
+ struct map *kmaps[MAP__NR_TYPES];
+ struct line_range line_range;
} session;
-#define semantic_error(msg ...) die("Semantic error :" msg)
-static int parse_probepoint(const struct option *opt __used,
- const char *str, int unset __used)
+/* 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;
- char *arg, *ptr;
struct probe_point *pp = &session.probes[session.nr_probe];
- char **event = &session.events[session.nr_probe];
- int retp = 0;
-
- if (!str) /* The end of probe points */
- return 0;
- eprintf("probe-definition(%d): %s\n", session.nr_probe, str);
+ 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");
- eprintf("argv[%d]=%s\n", argc, argv[argc - 1]);
- }
- } while (*str != '\0');
- if (argc < 2)
- semantic_error("Need event-name and probe-point at least.");
-
- /* Parse the event name */
- if (argv[0][0] == 'r')
- retp = 1;
- else if (argv[0][0] != 'p')
- semantic_error("You must specify 'p'(kprobe) or"
- " 'r'(kretprobe) first.");
- /* TODO: check event name */
- *event = argv[0];
-
- /* Parse probe point */
- arg = argv[1];
- if (arg[0] == '@') {
- /* Source Line */
- arg++;
- ptr = strchr(arg, ':');
- if (!ptr || !isdigit(ptr[1]))
- semantic_error("Line number is required.");
- *ptr++ = '\0';
- if (strlen(arg) == 0)
- semantic_error("No file name.");
- pp->file = strdup(arg);
- pp->line = atoi(ptr);
- if (!pp->file || !pp->line)
- semantic_error("Failed to parse line.");
- eprintf("file:%s line:%d\n", pp->file, pp->line);
- } else {
- /* Function name */
- ptr = strchr(arg, '+');
- if (ptr) {
- if (!isdigit(ptr[1]))
- semantic_error("Offset is required.");
- *ptr++ = '\0';
- pp->offset = atoi(ptr);
- } else
- ptr = arg;
- ptr = strchr(ptr, '@');
- if (ptr) {
- *ptr++ = '\0';
- pp->file = strdup(ptr);
- }
- pp->function = strdup(arg);
- eprintf("symbol:%s file:%s offset:%d\n",
- pp->function, pp->file, pp->offset);
- }
- free(argv[1]);
- if (pp->file)
- session.need_dwarf = 1;
-
- /* Copy arguments */
- pp->nr_args = argc - 2;
- if (pp->nr_args > 0) {
- pp->args = (char **)malloc(sizeof(char *) * pp->nr_args);
- if (!pp->args)
- die("malloc");
- memcpy(pp->args, &argv[2], 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 (retp)
- semantic_error("You can't specify local"
- " variable for kretprobe");
- session.need_dwarf = 1;
- }
+ /* Parse perf-probe event into probe_point */
+ parse_perf_probe_event(str, pp, &session.need_dwarf);
+
+ pr_debug("%d arguments\n", pp->nr_args);
+}
- eprintf("%d arguments\n", pp->nr_args);
+static void parse_probe_event_argv(int argc, const char **argv)
+{
+ int i, len;
+ char *buf;
+
+ /* Bind up rest arguments */
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ buf = zalloc(len + 1);
+ if (!buf)
+ die("Failed to allocate memory for binding arguments.");
+ len = 0;
+ for (i = 0; i < argc; i++)
+ len += sprintf(&buf[len], "%s ", argv[i]);
+ parse_probe_event(buf);
+ free(buf);
+}
+
+static int opt_add_probe_event(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ if (str)
+ parse_probe_event(str);
return 0;
}
-#ifndef NO_LIBDWARF
-static int open_default_vmlinux(void)
+static int opt_del_probe_event(const struct option *opt __used,
+ const char *str, int unset __used)
{
- struct utsname uts;
- char fname[MAX_PATH_LEN];
- int fd, ret, i;
-
- ret = uname(&uts);
- if (ret) {
- eprintf("uname() failed.\n");
- return -errno;
+ if (str) {
+ if (!session.dellist)
+ session.dellist = strlist__new(true, NULL);
+ strlist__add(session.dellist, str);
}
- session.release = uts.release;
- for (i = 0; i < NR_SEARCH_PATH; i++) {
- ret = snprintf(fname, MAX_PATH_LEN,
- default_search_path[i], session.release);
- if (ret >= MAX_PATH_LEN || ret < 0) {
- eprintf("Filename(%d,%s) is too long.\n", i,
- uts.release);
- errno = E2BIG;
- return -E2BIG;
- }
- eprintf("try to open %s\n", fname);
- fd = open(fname, O_RDONLY);
- if (fd >= 0)
- break;
+ return 0;
+}
+
+/* Currently just checking function name from symbol map */
+static void evaluate_probe_point(struct probe_point *pp)
+{
+ struct symbol *sym;
+ sym = map__find_symbol_by_name(session.kmaps[MAP__FUNCTION],
+ pp->function, NULL);
+ if (!sym)
+ die("Kernel symbol \'%s\' not found - probe not added.",
+ pp->function);
+}
+
+#ifndef NO_LIBDWARF
+static int open_vmlinux(void)
+{
+ if (map__load(session.kmaps[MAP__FUNCTION], NULL) < 0) {
+ pr_debug("Failed to load kernel map.\n");
+ return -EINVAL;
}
- return fd;
+ pr_debug("Try to open %s\n",
+ session.kmaps[MAP__FUNCTION]->dso->long_name);
+ return open(session.kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
+}
+
+static int opt_show_lines(const struct option *opt __used,
+ const char *str, int unset __used)
+{
+ if (str)
+ parse_line_range_desc(str, &session.line_range);
+ INIT_LIST_HEAD(&session.line_range.line_list);
+ session.show_lines = true;
+ return 0;
}
#endif
static const char * const probe_usage[] = {
- "perf probe [<options>] -P 'PROBEDEF' [-P 'PROBEDEF' ...]",
+ "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
+ "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
+ "perf probe [<options>] --del '[GROUP:]EVENT' ...",
+ "perf probe --list",
+ "perf probe --line 'LINEDESC'",
NULL
};
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"),
#ifndef NO_LIBDWARF
- OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
- "vmlinux/module pathname"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
#endif
- OPT_CALLBACK('P', "probe", NULL,
+ OPT_BOOLEAN('l', "list", &session.list_events,
+ "list up current probe events"),
+ OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
+ opt_del_probe_event),
+ OPT_CALLBACK('a', "add", NULL,
#ifdef NO_LIBDWARF
- "p|r:[GRP/]NAME FUNC[+OFFS] [ARG ...]",
+ "[EVENT=]FUNC[+OFFS|%return] [ARG ...]",
#else
- "p|r:[GRP/]NAME FUNC[+OFFS][@SRC]|@SRC:LINE [ARG ...]",
+ "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]",
#endif
"probe point definition, where\n"
- "\t\tp:\tkprobe probe\n"
- "\t\tr:\tkretprobe probe\n"
- "\t\tGRP:\tGroup name (optional)\n"
- "\t\tNAME:\tEvent name\n"
+ "\t\tGROUP:\tGroup name (optional)\n"
+ "\t\tEVENT:\tEvent name\n"
"\t\tFUNC:\tFunction name\n"
"\t\tOFFS:\tOffset from function entry (in byte)\n"
+ "\t\t%return:\tPut the probe at function return\n"
#ifdef NO_LIBDWARF
"\t\tARG:\tProbe argument (only \n"
#else
"\t\tSRC:\tSource code path\n"
- "\t\tLINE:\tLine number\n"
+ "\t\tRLN:\tRelative line number from function entry.\n"
+ "\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",
- parse_probepoint),
+ "\t\t\tkprobe-tracer argument format.)\n",
+ opt_add_probe_event),
+ OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
+ " with existing name"),
+#ifndef NO_LIBDWARF
+ OPT_CALLBACK('L', "line", NULL,
+ "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
+ "Show source code lines.", opt_show_lines),
+#endif
OPT_END()
};
-static int write_new_event(int fd, const char *buf)
-{
- int ret;
-
- printf("Adding new event: %s\n", buf);
- ret = write(fd, buf, strlen(buf));
- if (ret <= 0)
- die("failed to create event.");
-
- return ret;
-}
-
-#define MAX_CMDLEN 256
-
-static int synthesize_probepoint(struct probe_point *pp)
+/* Initialize symbol maps for vmlinux */
+static void init_vmlinux(void)
{
- char *buf;
- int i, len, ret;
- pp->probes[0] = buf = (char *)calloc(MAX_CMDLEN, sizeof(char));
- if (!buf)
- die("calloc");
- 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;
+ symbol_conf.sort_by_name = true;
+ if (symbol_conf.vmlinux_name == NULL)
+ symbol_conf.try_vmlinux_path = true;
+ else
+ pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
+ if (symbol__init() < 0)
+ die("Failed to init symbol map.");
+
+ map_groups__init(&session.kmap_groups);
+ if (map_groups__create_kernel_maps(&session.kmap_groups,
+ session.kmaps) < 0)
+ die("Failed to create kernel maps.");
}
int cmd_probe(int argc, const char **argv, const char *prefix __used)
{
- int i, j, fd, ret;
+ int i, 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);
- if (argc || session.nr_probe == 0)
+ PARSE_OPT_STOP_AT_NON_OPTION);
+ if (argc > 0) {
+ if (strcmp(argv[0], "-") == 0) {
+ pr_warning(" Error: '-' is not supported.\n");
+ usage_with_options(probe_usage, options);
+ }
+ parse_probe_event_argv(argc, argv);
+ }
+
+ if ((!session.nr_probe && !session.dellist && !session.list_events &&
+ !session.show_lines))
usage_with_options(probe_usage, options);
-#ifdef NO_LIBDWARF
- if (session.need_dwarf)
- semantic_error("Dwarf-analysis is not supported");
-#endif
+ if (debugfs_valid_mountpoint(debugfs_path) < 0)
+ die("Failed to find debugfs path.");
+
+ if (session.list_events) {
+ if (session.nr_probe != 0 || session.dellist) {
+ pr_warning(" Error: Don't use --list with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
+ }
+ if (session.show_lines) {
+ pr_warning(" Error: Don't use --list with --line.\n");
+ usage_with_options(probe_usage, options);
+ }
+ show_perf_probe_events();
+ return 0;
+ }
- /* Synthesize probes without dwarf */
- for (j = 0; j < session.nr_probe; j++) {
#ifndef NO_LIBDWARF
- if (session.events[j][0] != 'r') {
- session.need_dwarf = 1;
- continue;
+ if (session.show_lines) {
+ if (session.nr_probe != 0 || session.dellist) {
+ pr_warning(" Error: Don't use --line with"
+ " --add/--del.\n");
+ usage_with_options(probe_usage, options);
}
+ init_vmlinux();
+ fd = open_vmlinux();
+ if (fd < 0)
+ die("Could not open debuginfo file.");
+ ret = find_line_range(fd, &session.line_range);
+ if (ret <= 0)
+ die("Source line is not found.\n");
+ close(fd);
+ show_line_range(&session.line_range);
+ return 0;
+ }
#endif
- ret = synthesize_probepoint(&session.probes[j]);
- if (ret == -E2BIG)
- semantic_error("probe point is too long.");
- else if (ret < 0)
- die("snprintf");
+
+ if (session.dellist) {
+ del_trace_kprobe_events(session.dellist);
+ strlist__delete(session.dellist);
+ if (session.nr_probe == 0)
+ return 0;
}
-#ifndef NO_LIBDWARF
- if (!session.need_dwarf)
- goto setup_probes;
+ /* Add probes */
+ init_vmlinux();
- if (session.vmlinux)
- fd = open(session.vmlinux, O_RDONLY);
- else
- fd = open_default_vmlinux();
- if (fd < 0)
- die("vmlinux/module file open");
+ if (session.need_dwarf)
+#ifdef NO_LIBDWARF
+ die("Debuginfo-analysis is not supported");
+#else /* !NO_LIBDWARF */
+ pr_debug("Some probes require debuginfo.\n");
+
+ fd = open_vmlinux();
+ if (fd < 0) {
+ if (session.need_dwarf)
+ die("Could not open debuginfo file.");
+
+ pr_debug("Could not open vmlinux/module file."
+ " Try to use symbols.\n");
+ goto end_dwarf;
+ }
/* Searching probe points */
- for (j = 0; j < session.nr_probe; j++) {
- pp = &session.probes[j];
+ for (i = 0; i < session.nr_probe; i++) {
+ pp = &session.probes[i];
if (pp->found)
continue;
lseek(fd, SEEK_SET, 0);
ret = find_probepoint(fd, pp);
- if (ret <= 0)
- die("No probe point found.\n");
- eprintf("probe event %s found\n", session.events[j]);
+ if (ret > 0)
+ continue;
+ if (ret == 0) { /* No error but failed to find probe point. */
+ synthesize_perf_probe_point(pp);
+ die("Probe point '%s' not found. - probe not added.",
+ pp->probes[0]);
+ }
+ /* Error path */
+ if (session.need_dwarf) {
+ if (ret == -ENOENT)
+ pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n");
+ die("Could not analyze debuginfo.");
+ }
+ pr_debug("An error occurred in debuginfo analysis."
+ " Try to use symbols.\n");
+ break;
}
close(fd);
-setup_probes:
+end_dwarf:
#endif /* !NO_LIBDWARF */
- /* Settng up probe points */
- snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path);
- fd = open(buf, O_WRONLY, O_APPEND);
- if (fd < 0)
- die("kprobe_events open");
- for (j = 0; j < session.nr_probe; j++) {
- pp = &session.probes[j];
- if (pp->found == 1) {
- snprintf(buf, MAX_CMDLEN, "%s %s\n",
- session.events[j], pp->probes[0]);
- write_new_event(fd, buf);
- } else
- for (i = 0; i < pp->found; i++) {
- snprintf(buf, MAX_CMDLEN, "%s%d %s\n",
- session.events[j], i, pp->probes[i]);
- write_new_event(fd, buf);
- }
+ /* Synthesize probes without dwarf */
+ for (i = 0; i < session.nr_probe; i++) {
+ pp = &session.probes[i];
+ if (pp->found) /* This probe is already found. */
+ continue;
+
+ evaluate_probe_point(pp);
+ ret = synthesize_trace_kprobe_event(pp);
+ if (ret == -E2BIG)
+ die("probe point definition becomes too long.");
+ else if (ret < 0)
+ die("Failed to synthesize a probe point.");
}
- close(fd);
+
+ /* Settng up probe points */
+ add_trace_kprobe_events(session.probes, session.nr_probe,
+ session.force_add);
return 0;
}