From 4ea42b181434bfc6a0a18d32214130a242d489bf Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 8 Oct 2009 17:17:38 -0400 Subject: perf: Add perf probe subcommand, a kprobe-event setup helper Add perf probe subcommand that implements a kprobe-event setup helper to the perf command. This allows user to define kprobe events using C expressions (C line numbers, C function names, and C local variables). Usage ----- perf probe [] -P 'PROBEDEF' [-P 'PROBEDEF' ...] -k, --vmlinux vmlinux/module pathname -P, --probe probe point definition, where p: kprobe probe r: kretprobe probe GRP: Group name (optional) NAME: Event name FUNC: Function name OFFS: Offset from function entry (in byte) SRC: Source code path LINE: Line number ARG: Probe argument (local variable name or kprobe-tracer argument format is supported.) Changes in v4: - Add _GNU_SOURCE macro for strndup(). Changes in v3: - Remove -r option because perf always be used for online kernel. - Check malloc/calloc results. Changes in v2: - Check synthesized string length. - Rename perf kprobe to perf probe. - Use spaces for separator and update usage comment. - Check error paths in parse_probepoint(). - Check optimized-out variables. Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Frank Ch. Eigler LKML-Reference: <20091008211737.29299.14784.stgit@dhcp-100-2-132.bos.redhat.com> Signed-off-by: Frederic Weisbecker --- tools/perf/perf.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/perf.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 19fc7feb9d59..f598120c0097 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -295,6 +295,9 @@ static void handle_internal_command(int argc, const char **argv) { "version", cmd_version, 0 }, { "trace", cmd_trace, 0 }, { "sched", cmd_sched, 0 }, +#ifdef SUPPORT_DWARF + { "probe", cmd_probe, 0 }, +#endif }; unsigned int i; static const char ext[] = STRIP_EXTENSION; -- cgit v1.2.3 From 23e8ec0d1c410f2f1d81050ee155db229abb1707 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 7 Oct 2009 18:28:30 -0400 Subject: perf probe: Add perf probe command support without libdwarf Enables 'perf probe' even if libdwarf is not installed. If libdwarf is not found, 'perf probe' just disables dwarf support. Users can use 'perf probe' to set up new events by using kprobe_events format. Signed-off-by: Masami Hiramatsu Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Frank Ch. Eigler LKML-Reference: <20091007222830.1684.25665.stgit@dhcp-100-2-132.bos.redhat.com> Signed-off-by: Frederic Weisbecker --- tools/perf/Makefile | 6 +++--- tools/perf/builtin-probe.c | 42 +++++++++++++++++++++++++++++++++++------- tools/perf/perf.c | 2 -- tools/perf/util/probe-finder.h | 2 ++ 4 files changed, 40 insertions(+), 12 deletions(-) (limited to 'tools/perf/perf.c') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 6dabcf1a4df6..52b1f438e71a 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -385,6 +385,7 @@ BUILTIN_OBJS += builtin-stat.o BUILTIN_OBJS += builtin-timechart.o BUILTIN_OBJS += builtin-top.o BUILTIN_OBJS += builtin-trace.o +BUILTIN_OBJS += builtin-probe.o PERFLIBS = $(LIB_FILE) @@ -420,13 +421,12 @@ ifneq ($(shell sh -c "(echo '\#include '; echo 'int main(void) { Elf * endif ifneq ($(shell sh -c "(echo '\#include '; echo '\#include '; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) - msg := $(warning No libdwarf.h found, disables probe subcommand. Please install libdwarf-dev/libdwarf-devel); + msg := $(warning No libdwarf.h found, disables dwarf support. Please install libdwarf-dev/libdwarf-devel); + BASIC_CFLAGS += -DNO_LIBDWARF else EXTLIBS += -lelf -ldwarf LIB_H += util/probe-finder.h LIB_OBJS += util/probe-finder.o - BUILTIN_OBJS += builtin-probe.o - BASIC_CFLAGS += -DSUPPORT_DWARF endif ifdef NO_DEMANGLE diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 24b64b5cefce..73c883b715cc 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -54,6 +54,7 @@ const char *default_search_path[NR_SEARCH_PATH] = { static struct { char *vmlinux; char *release; + int need_dwarf; int nr_probe; struct probe_point probes[MAX_PROBES]; char *events[MAX_PROBES]; @@ -162,6 +163,8 @@ static int parse_probepoint(const struct option *opt __used, pp->function, pp->file, pp->offset); } free(argv[1]); + if (pp->file) + session.need_dwarf = 1; /* Copy arguments */ pp->nr_args = argc - 2; @@ -173,15 +176,19 @@ static int parse_probepoint(const struct option *opt __used, } /* Ensure return probe has no C argument */ - if (retp) - for (i = 0; i < pp->nr_args; i++) - if (is_c_varname(pp->args[i])) + 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; + } + debug("%d arguments\n", pp->nr_args); return 0; } +#ifndef NO_LIBDWARF static int open_default_vmlinux(void) { struct utsname uts; @@ -209,6 +216,7 @@ static int open_default_vmlinux(void) } return fd; } +#endif static const char * const probe_usage[] = { "perf probe [] -P 'PROBEDEF' [-P 'PROBEDEF' ...]", @@ -216,10 +224,16 @@ static const char * const probe_usage[] = { }; static const struct option options[] = { +#ifndef NO_LIBDWARF OPT_STRING('k', "vmlinux", &session.vmlinux, "file", "vmlinux/module pathname"), +#endif OPT_CALLBACK('P', "probe", NULL, +#ifdef NO_LIBDWARF + "p|r:[GRP/]NAME FUNC[+OFFS] [ARG ...]", +#else "p|r:[GRP/]NAME FUNC[+OFFS][@SRC]|@SRC:LINE [ARG ...]", +#endif "probe point definition, where\n" "\t\tp:\tkprobe probe\n" "\t\tr:\tkretprobe probe\n" @@ -227,9 +241,13 @@ static const struct option options[] = { "\t\tNAME:\tEvent name\n" "\t\tFUNC:\tFunction name\n" "\t\tOFFS:\tOffset from function entry (in byte)\n" +#ifdef NO_LIBDWARF + "\t\tARG:\tProbe argument (only \n" +#else "\t\tSRC:\tSource code path\n" "\t\tLINE:\tLine number\n" "\t\tARG:\tProbe argument (local variable name or\n" +#endif "\t\t\tkprobe-tracer argument format is supported.)\n", parse_probepoint), OPT_END() @@ -279,7 +297,7 @@ error: int cmd_probe(int argc, const char **argv, const char *prefix __used) { - int i, j, fd, ret, need_dwarf = 0; + int i, j, fd, ret; struct probe_point *pp; char buf[MAX_CMDLEN]; @@ -288,12 +306,19 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (argc || session.nr_probe == 0) usage_with_options(probe_usage, options); - /* Synthesize return probes */ +#ifdef NO_LIBDWARF + if (session.need_dwarf) + semantic_error("Dwarf-analysis is not supported"); +#endif + + /* Synthesize probes without dwarf */ for (j = 0; j < session.nr_probe; j++) { +#ifndef NO_LIBDWARF if (session.events[j][0] != 'r') { - need_dwarf = 1; + session.need_dwarf = 1; continue; } +#endif ret = synthesize_probepoint(&session.probes[j]); if (ret == -E2BIG) semantic_error("probe point is too long."); @@ -303,7 +328,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } } - if (!need_dwarf) +#ifndef NO_LIBDWARF + if (!session.need_dwarf) goto setup_probes; if (session.vmlinux) @@ -332,6 +358,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) close(fd); setup_probes: +#endif /* !NO_LIBDWARF */ + /* Settng up probe points */ snprintf(buf, MAX_CMDLEN, "%s/../kprobe_events", debugfs_path); fd = open(buf, O_WRONLY, O_APPEND); diff --git a/tools/perf/perf.c b/tools/perf/perf.c index f598120c0097..c570d177d5c6 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -295,9 +295,7 @@ static void handle_internal_command(int argc, const char **argv) { "version", cmd_version, 0 }, { "trace", cmd_trace, 0 }, { "sched", cmd_sched, 0 }, -#ifdef SUPPORT_DWARF { "probe", cmd_probe, 0 }, -#endif }; unsigned int i; static const char ext[] = STRIP_EXTENSION; diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index af920de64866..306810c32f6b 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h @@ -44,6 +44,7 @@ struct probe_point { char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ }; +#ifndef NO_LIBDWARF extern int find_probepoint(int fd, struct probe_point *pp); #include @@ -64,5 +65,6 @@ struct probe_finder { char *buf; /* Current output buffer */ int len; /* Length of output buffer */ }; +#endif /* NO_LIBDWARF */ #endif /*_PROBE_FINDER_H */ -- cgit v1.2.3 From cfed95a693e1ea5d08b9c9019bc30e448437ee2f Mon Sep 17 00:00:00 2001 From: Vincent Legoll Date: Tue, 13 Oct 2009 10:18:16 +0200 Subject: perf tools: Do not manually count string lengths Use strlen & macros instead of manually counting string lengths as this is error prone and may lend to bugs. Signed-off-by: Vincent Legoll Cc: Linus Torvalds LKML-Reference: <4727185d0910130118m5387058dndb02ac9b384af9f0@mail.gmail.com> Signed-off-by: Ingo Molnar --- tools/perf/perf.c | 16 ++++++++-------- tools/perf/util/cache.h | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'tools/perf/perf.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 19fc7feb9d59..624e62d9d1e0 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -89,8 +89,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) /* * Check remaining flags. */ - if (!prefixcmp(cmd, "--exec-path")) { - cmd += 11; + if (!prefixcmp(cmd, CMD_EXEC_PATH)) { + cmd += strlen(CMD_EXEC_PATH); if (*cmd == '=') perf_set_argv_exec_path(cmd + 1); else { @@ -117,8 +117,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) (*argv)++; (*argc)--; handled++; - } else if (!prefixcmp(cmd, "--perf-dir=")) { - setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1); + } else if (!prefixcmp(cmd, CMD_PERF_DIR)) { + setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--work-tree")) { @@ -131,8 +131,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) *envchanged = 1; (*argv)++; (*argc)--; - } else if (!prefixcmp(cmd, "--work-tree=")) { - setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); + } else if (!prefixcmp(cmd, CMD_WORK_TREE)) { + setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1); if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--debugfs-dir")) { @@ -146,8 +146,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) *envchanged = 1; (*argv)++; (*argc)--; - } else if (!prefixcmp(cmd, "--debugfs-dir=")) { - strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN); + } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { + strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN); debugfs_mntpt[MAXPATHLEN - 1] = '\0'; if (envchanged) *envchanged = 1; diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index f26172c0c919..918eb376abe3 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -5,6 +5,11 @@ #include "strbuf.h" #include "../perf.h" +#define CMD_EXEC_PATH "--exec-path" +#define CMD_PERF_DIR "--perf-dir=" +#define CMD_WORK_TREE "--work-tree=" +#define CMD_DEBUGFS_DIR "--debugfs-dir=" + #define PERF_DIR_ENVIRONMENT "PERF_DIR" #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" -- cgit v1.2.3 From dcba8848d3bc83ec9ee0858b9ae6e4f1c1fa7fa3 Mon Sep 17 00:00:00 2001 From: Hitoshi Mitake Date: Thu, 5 Nov 2009 09:31:36 +0900 Subject: perf bench: Add new subcommand 'bench' to perf.c This patch modifies perf.c for invoking 'bench' subcommand. Signed-off-by: Hitoshi Mitake Cc: Rusty Russell Cc: Peter Zijlstra Cc: Mike Galbraith Cc: Arnaldo Carvalho de Melo Cc: fweisbec@gmail.com Cc: Jiri Kosina LKML-Reference: <1257381097-4743-7-git-send-email-mitake@dcl.info.waseda.ac.jp> Signed-off-by: Ingo Molnar --- tools/perf/perf.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/perf.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 624e62d9d1e0..f90ca5ec83f3 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -288,6 +288,7 @@ static void handle_internal_command(int argc, const char **argv) { "list", cmd_list, 0 }, { "record", cmd_record, 0 }, { "report", cmd_report, 0 }, + { "bench", cmd_bench, 0 }, { "stat", cmd_stat, 0 }, { "timechart", cmd_timechart, 0 }, { "top", cmd_top, 0 }, -- cgit v1.2.3 From 549104f22b3cd4761145eb5fba6ee4d59822da61 Mon Sep 17 00:00:00 2001 From: Clark Williams Date: Sun, 8 Nov 2009 09:03:07 -0600 Subject: perf tools: Modify perf routines to use new debugfs routines modify perf.c get_debugfs_mntpnt() to use the util/debugfs.c debugfs_find_mountpoint() modify util/parse-events.c to use debugfs_valid_mountpoint(). Signed-off-by: Clark Williams Cc: Arnaldo Carvalho de Melo Cc: Peter Zijlstra LKML-Reference: <20091101155720.624cc87e@torg> Signed-off-by: Ingo Molnar --- tools/perf/perf.c | 44 ++++++------------------------------------ tools/perf/util/parse-events.c | 17 ++++------------ 2 files changed, 10 insertions(+), 51 deletions(-) (limited to 'tools/perf/perf.c') diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 624e62d9d1e0..601f403fbdac 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -14,6 +14,7 @@ #include "util/run-command.h" #include "util/parse-events.h" #include "util/string.h" +#include "util/debugfs.h" const char perf_usage_string[] = "perf [--version] [--help] COMMAND [ARGS]"; @@ -382,45 +383,12 @@ static int run_argv(int *argcp, const char ***argv) /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ static void get_debugfs_mntpt(void) { - FILE *file; - char fs_type[100]; - char debugfs[MAXPATHLEN]; + const char *path = debugfs_find_mountpoint(); - /* - * try the standard location - */ - if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { - strcpy(debugfs_mntpt, "/sys/kernel/debug/"); - return; - } - - /* - * try the sane location - */ - if (valid_debugfs_mount("/debug/") == 0) { - strcpy(debugfs_mntpt, "/debug/"); - return; - } - - /* - * give up and parse /proc/mounts - */ - file = fopen("/proc/mounts", "r"); - if (file == NULL) - return; - - while (fscanf(file, "%*s %" - STR(MAXPATHLEN) - "s %99s %*s %*d %*d\n", - debugfs, fs_type) == 2) { - if (strcmp(fs_type, "debugfs") == 0) - break; - } - fclose(file); - if (strcmp(fs_type, "debugfs") == 0) { - strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); - debugfs_mntpt[MAXPATHLEN - 1] = '\0'; - } + if (path) + strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); + else + debugfs_mntpt[0] = '\0'; } int main(int argc, const char **argv) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 31baa5a60365..097938a96d74 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -7,6 +7,7 @@ #include "string.h" #include "cache.h" #include "header.h" +#include "debugfs.h" int nr_counters; @@ -149,16 +150,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) #define MAX_EVENT_LENGTH 512 -int valid_debugfs_mount(const char *debugfs) -{ - struct statfs st_fs; - - if (statfs(debugfs, &st_fs) < 0) - return -ENOENT; - else if (st_fs.f_type != (long) DEBUGFS_MAGIC) - return -ENOENT; - return 0; -} struct tracepoint_path *tracepoint_id_to_path(u64 config) { @@ -171,7 +162,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; - if (valid_debugfs_mount(debugfs_path)) + if (debugfs_valid_mountpoint(debugfs_path)) return NULL; sys_dir = opendir(debugfs_path); @@ -510,7 +501,7 @@ static enum event_result parse_tracepoint_event(const char **strp, char sys_name[MAX_EVENT_LENGTH]; unsigned int sys_length, evt_length; - if (valid_debugfs_mount(debugfs_path)) + if (debugfs_valid_mountpoint(debugfs_path)) return 0; evt_name = strchr(*strp, ':'); @@ -788,7 +779,7 @@ static void print_tracepoint_events(void) char evt_path[MAXPATHLEN]; char dir_path[MAXPATHLEN]; - if (valid_debugfs_mount(debugfs_path)) + if (debugfs_valid_mountpoint(debugfs_path)) return; sys_dir = opendir(debugfs_path); -- cgit v1.2.3 From c34984b2bbc77596c97c333539bffc90d2033178 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Nov 2009 16:32:45 -0200 Subject: perf buildid-list: New plumbing command With this we can list the buildids in a perf.data file so that we can pipe them to other, distro specific tools that from the buildids can figure out separate packages (foo-debuginfo) where we can find the matching symtabs so that perf report can do its job. E.g: [acme@doppio linux-2.6-tip]$ perf buildid-list | head -5 8e08b117e5458ad3f85da16d42d0fc5cd21c5869 520c2387a587cc5acfcf881e27dba1caaeab4b1f ec8dd400904ddfcac8b1c343263a790f977159dc 7caedbca5a6d8ab39a7fe44bd28c07d3e14a3f3f 379bb828fd08859dbea73279f04abefabc95a6a3 [acme@doppio linux-2.6-tip]$ perf buildid-list -v | head -5 8e08b117e5458ad3f85da16d42d0fc5cd21c5869 /sbin/init 520c2387a587cc5acfcf881e27dba1caaeab4b1f /lib64/ld-2.10.1.so ec8dd400904ddfcac8b1c343263a790f977159dc /lib64/libc-2.10.1.so 7caedbca5a6d8ab39a7fe44bd28c07d3e14a3f3f /sbin/udevd 379bb828fd08859dbea73279f04abefabc95a6a3 /lib64/libdl-2.10.1.so [acme@doppio linux-2.6-tip]$ Signed-off-by: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra LKML-Reference: <1258396365-29217-5-git-send-email-acme@infradead.org> Signed-off-by: Ingo Molnar --- tools/perf/Documentation/perf-buildid-list.txt | 34 ++++++++ tools/perf/Makefile | 1 + tools/perf/builtin-buildid-list.c | 116 +++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/command-list.txt | 1 + tools/perf/perf.c | 1 + 6 files changed, 154 insertions(+) create mode 100644 tools/perf/Documentation/perf-buildid-list.txt create mode 100644 tools/perf/builtin-buildid-list.c (limited to 'tools/perf/perf.c') diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt new file mode 100644 index 000000000000..abab34e05576 --- /dev/null +++ b/tools/perf/Documentation/perf-buildid-list.txt @@ -0,0 +1,34 @@ +perf-buildid-list(1) +==================== + +NAME +---- +perf-buildid-list - List the buildids in a perf.data file + +SYNOPSIS +-------- +[verse] +'perf buildid-list ' + +DESCRIPTION +----------- +This command displays the buildids found in a perf.data file, so that other +tools can be used to fetch packages with matching symbol tables for use by +perf report. + +OPTIONS +------- +-i:: +--input=:: + Input file name. (default: perf.data) +-f:: +--force:: + Don't do ownership validation. +-v:: +--verbose:: + Be more verbose, showing the name of the DSOs after the buildids. + +SEE ALSO +-------- +linkperf:perf-record[1], linkperf:perf-top[1], +linkperf:perf-report[1] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index f7cd89622cf4..46a58a81c9ad 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -430,6 +430,7 @@ BUILTIN_OBJS += bench/sched-pipe.o BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-sched.o +BUILTIN_OBJS += builtin-buildid-list.o BUILTIN_OBJS += builtin-list.o BUILTIN_OBJS += builtin-record.o BUILTIN_OBJS += builtin-report.o diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c new file mode 100644 index 000000000000..2e377e1be435 --- /dev/null +++ b/tools/perf/builtin-buildid-list.c @@ -0,0 +1,116 @@ +/* + * builtin-buildid-list.c + * + * Builtin buildid-list command: list buildids in perf.data + * + * Copyright (C) 2009, Red Hat Inc. + * Copyright (C) 2009, Arnaldo Carvalho de Melo + */ +#include "builtin.h" +#include "perf.h" +#include "util/cache.h" +#include "util/data_map.h" +#include "util/debug.h" +#include "util/header.h" +#include "util/parse-options.h" +#include "util/symbol.h" + +static char const *input_name = "perf.data"; +static int force; + +static const char *const buildid_list_usage[] = { + "perf report []", + NULL +}; + +static const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", + "input file name"), + OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), + OPT_BOOLEAN('v', "verbose", &verbose, + "be more verbose (show counter open errors, etc)"), + OPT_END() +}; + +static int perf_file_section__process_buildids(struct perf_file_section *self, + int feat, int fd) +{ + if (feat != HEADER_BUILD_ID) + return 0; + + if (lseek(fd, self->offset, SEEK_SET) < 0) { + pr_warning("Failed to lseek to %Ld offset for buildids!\n", + self->offset); + return -1; + } + + if (perf_header__read_build_ids(fd, self->offset, self->size)) { + pr_warning("Failed to read buildids!\n"); + return -1; + } + + return 0; +} + +static int __cmd_buildid_list(void) +{ + int err = -1; + struct perf_header *header; + struct perf_file_header f_header; + struct stat input_stat; + int input = open(input_name, O_RDONLY); + + if (input < 0) { + pr_err("failed to open file: %s", input_name); + if (!strcmp(input_name, "perf.data")) + pr_err(" (try 'perf record' first)"); + pr_err("\n"); + goto out; + } + + err = fstat(input, &input_stat); + if (err < 0) { + perror("failed to stat file"); + goto out_close; + } + + if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { + pr_err("file %s not owned by current user or root\n", + input_name); + goto out_close; + } + + if (!input_stat.st_size) { + pr_info("zero-sized file, nothing to do!\n"); + goto out_close; + } + + err = -1; + header = perf_header__new(); + if (header == NULL) + goto out_close; + + if (perf_file_header__read(&f_header, header, input) < 0) { + pr_warning("incompatible file format"); + goto out_close; + } + + err = perf_header__process_sections(header, input, + perf_file_section__process_buildids); + + if (err < 0) + goto out_close; + + dsos__fprintf_buildid(stdout); +out_close: + close(input); +out: + return err; +} + +int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) +{ + argc = parse_options(argc, argv, options, buildid_list_usage, 0); + setup_pager(); + return __cmd_buildid_list(); +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index f0cd5b139b7c..e97954a0a3d2 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -16,6 +16,7 @@ extern int check_pager_config(const char *cmd); extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_bench(int argc, const char **argv, const char *prefix); +extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix); extern int cmd_sched(int argc, const char **argv, const char *prefix); extern int cmd_list(int argc, const char **argv, const char *prefix); diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 981c40b9a5e2..d37b16cf18ff 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -4,6 +4,7 @@ # perf-annotate mainporcelain common perf-bench mainporcelain common +perf-buildid-list mainporcelain common perf-list mainporcelain common perf-sched mainporcelain common perf-record mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 8936786b42ea..53359ebb369a 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -287,6 +287,7 @@ static void handle_internal_command(int argc, const char **argv) static struct cmd_struct commands[] = { { "help", cmd_help, 0 }, { "list", cmd_list, 0 }, + { "buildid-list", cmd_buildid_list, 0 }, { "record", cmd_record, 0 }, { "report", cmd_report, 0 }, { "bench", cmd_bench, 0 }, -- cgit v1.2.3 From ba77c9e11111a172c9e8687fe16a6a173a61916f Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 20 Nov 2009 15:53:25 +0800 Subject: perf: Add 'perf kmem' tool This tool is mostly a perf version of kmemtrace-user. The following information is provided by this tool: - the total amount of memory allocated and fragmentation per call-site - the total amount of memory allocated and fragmentation per allocation - total memory allocated and fragmentation in the collected dataset - ... Sample output: # ./perf kmem record ^C # ./perf kmem --stat caller --stat alloc -l 10 ------------------------------------------------------------------------------ Callsite | Total_alloc/Per | Total_req/Per | Hit | Fragmentation ------------------------------------------------------------------------------ 0xc052f37a | 790528/4096 | 790528/4096 | 193 | 0.000% 0xc0541d70 | 524288/4096 | 524288/4096 | 128 | 0.000% 0xc051cc68 | 481600/200 | 481600/200 | 2408 | 0.000% 0xc0572623 | 297444/676 | 297440/676 | 440 | 0.001% 0xc05399f1 | 73476/164 | 73472/164 | 448 | 0.005% 0xc05243bf | 51456/256 | 51456/256 | 201 | 0.000% 0xc0730d0e | 31844/497 | 31808/497 | 64 | 0.113% 0xc0734c4e | 17152/256 | 17152/256 | 67 | 0.000% 0xc0541a6d | 16384/128 | 16384/128 | 128 | 0.000% 0xc059c217 | 13120/40 | 13120/40 | 328 | 0.000% 0xc0501ee6 | 11264/88 | 11264/88 | 128 | 0.000% 0xc04daef0 | 7504/682 | 7128/648 | 11 | 5.011% 0xc04e14a3 | 4216/191 | 4216/191 | 22 | 0.000% 0xc05041ca | 3524/44 | 3520/44 | 80 | 0.114% 0xc0734fa3 | 2104/701 | 1620/540 | 3 | 23.004% 0xc05ec9f1 | 2024/289 | 2016/288 | 7 | 0.395% 0xc06a1999 | 1792/256 | 1792/256 | 7 | 0.000% 0xc0463b9a | 1584/144 | 1584/144 | 11 | 0.000% 0xc0541eb0 | 1024/16 | 1024/16 | 64 | 0.000% 0xc06a19ac | 896/128 | 896/128 | 7 | 0.000% 0xc05721c0 | 772/12 | 768/12 | 64 | 0.518% 0xc054d1e6 | 288/57 | 280/56 | 5 | 2.778% 0xc04b562e | 157/31 | 154/30 | 5 | 1.911% 0xc04b536f | 80/16 | 80/16 | 5 | 0.000% 0xc05855a0 | 64/64 | 36/36 | 1 | 43.750% ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ Alloc Ptr | Total_alloc/Per | Total_req/Per | Hit | Fragmentation ------------------------------------------------------------------------------ 0xda884000 | 1052672/4096 | 1052672/4096 | 257 | 0.000% 0xda886000 | 262144/4096 | 262144/4096 | 64 | 0.000% 0xf60c7c00 | 16512/128 | 16512/128 | 129 | 0.000% 0xf59a4118 | 13120/40 | 13120/40 | 328 | 0.000% 0xdfd4b2c0 | 11264/88 | 11264/88 | 128 | 0.000% 0xf5274600 | 7680/256 | 7680/256 | 30 | 0.000% 0xe8395000 | 5948/594 | 5464/546 | 10 | 8.137% 0xe59c3c00 | 5748/479 | 5712/476 | 12 | 0.626% 0xf4cd1a80 | 3524/44 | 3520/44 | 80 | 0.114% 0xe5bd1600 | 2892/482 | 2856/476 | 6 | 1.245% ... | ... | ... | ... | ... ------------------------------------------------------------------------------ SUMMARY ======= Total bytes requested: 2333626 Total bytes allocated: 2353712 Total bytes wasted on internal fragmentation: 20086 Internal fragmentation: 0.853375% TODO: - show sym+offset in 'callsite' column - show cross node allocation stats - collect more useful stats? - ... Signed-off-by: Li Zefan Acked-by: Pekka Enberg Acked-by: Peter Zijlstra Cc: Frederic Weisbecker Cc: Steven Rostedt Cc: Eduard - Gabriel Munteanu Cc: linux-mm@kvack.org LKML-Reference: <4B064AF5.9060208@cn.fujitsu.com> Signed-off-by: Ingo Molnar --- tools/perf/Makefile | 1 + tools/perf/builtin-kmem.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/perf.c | 27 +-- 4 files changed, 594 insertions(+), 13 deletions(-) create mode 100644 tools/perf/builtin-kmem.c (limited to 'tools/perf/perf.c') diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 3f0666af93de..d7198c54bb69 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -444,6 +444,7 @@ BUILTIN_OBJS += builtin-timechart.o BUILTIN_OBJS += builtin-top.o BUILTIN_OBJS += builtin-trace.o BUILTIN_OBJS += builtin-probe.o +BUILTIN_OBJS += builtin-kmem.o PERFLIBS = $(LIB_FILE) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c new file mode 100644 index 000000000000..f315b052f819 --- /dev/null +++ b/tools/perf/builtin-kmem.c @@ -0,0 +1,578 @@ +#include "builtin.h" +#include "perf.h" + +#include "util/util.h" +#include "util/cache.h" +#include "util/symbol.h" +#include "util/thread.h" +#include "util/header.h" + +#include "util/parse-options.h" +#include "util/trace-event.h" + +#include "util/debug.h" +#include "util/data_map.h" + +#include + +struct alloc_stat; +typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); + +static char const *input_name = "perf.data"; + +static struct perf_header *header; +static u64 sample_type; + +static int alloc_flag; +static int caller_flag; + +sort_fn_t alloc_sort_fn; +sort_fn_t caller_sort_fn; + +static int alloc_lines = -1; +static int caller_lines = -1; + +static char *cwd; +static int cwdlen; + +struct alloc_stat { + union { + struct { + char *name; + u64 call_site; + }; + u64 ptr; + }; + u64 bytes_req; + u64 bytes_alloc; + u32 hit; + + struct rb_node node; +}; + +static struct rb_root root_alloc_stat; +static struct rb_root root_alloc_sorted; +static struct rb_root root_caller_stat; +static struct rb_root root_caller_sorted; + +static unsigned long total_requested, total_allocated; + +struct raw_event_sample { + u32 size; + char data[0]; +}; + +static int +process_comm_event(event_t *event, unsigned long offset, unsigned long head) +{ + struct thread *thread = threads__findnew(event->comm.pid); + + dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", + (void *)(offset + head), + (void *)(long)(event->header.size), + event->comm.comm, event->comm.pid); + + if (thread == NULL || + thread__set_comm(thread, event->comm.comm)) { + dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); + return -1; + } + + return 0; +} + +static void insert_alloc_stat(unsigned long ptr, + int bytes_req, int bytes_alloc) +{ + struct rb_node **node = &root_alloc_stat.rb_node; + struct rb_node *parent = NULL; + struct alloc_stat *data = NULL; + + if (!alloc_flag) + return; + + while (*node) { + parent = *node; + data = rb_entry(*node, struct alloc_stat, node); + + if (ptr > data->ptr) + node = &(*node)->rb_right; + else if (ptr < data->ptr) + node = &(*node)->rb_left; + else + break; + } + + if (data && data->ptr == ptr) { + data->hit++; + data->bytes_req += bytes_req; + data->bytes_alloc += bytes_req; + } else { + data = malloc(sizeof(*data)); + data->ptr = ptr; + data->hit = 1; + data->bytes_req = bytes_req; + data->bytes_alloc = bytes_alloc; + + rb_link_node(&data->node, parent, node); + rb_insert_color(&data->node, &root_alloc_stat); + } +} + +static void insert_caller_stat(unsigned long call_site, + int bytes_req, int bytes_alloc) +{ + struct rb_node **node = &root_caller_stat.rb_node; + struct rb_node *parent = NULL; + struct alloc_stat *data = NULL; + + if (!caller_flag) + return; + + while (*node) { + parent = *node; + data = rb_entry(*node, struct alloc_stat, node); + + if (call_site > data->call_site) + node = &(*node)->rb_right; + else if (call_site < data->call_site) + node = &(*node)->rb_left; + else + break; + } + + if (data && data->call_site == call_site) { + data->hit++; + data->bytes_req += bytes_req; + data->bytes_alloc += bytes_req; + } else { + data = malloc(sizeof(*data)); + data->call_site = call_site; + data->hit = 1; + data->bytes_req = bytes_req; + data->bytes_alloc = bytes_alloc; + + rb_link_node(&data->node, parent, node); + rb_insert_color(&data->node, &root_caller_stat); + } +} + +static void process_alloc_event(struct raw_event_sample *raw, + struct event *event, + int cpu __used, + u64 timestamp __used, + struct thread *thread __used, + int node __used) +{ + unsigned long call_site; + unsigned long ptr; + int bytes_req; + int bytes_alloc; + + ptr = raw_field_value(event, "ptr", raw->data); + call_site = raw_field_value(event, "call_site", raw->data); + bytes_req = raw_field_value(event, "bytes_req", raw->data); + bytes_alloc = raw_field_value(event, "bytes_alloc", raw->data); + + insert_alloc_stat(ptr, bytes_req, bytes_alloc); + insert_caller_stat(call_site, bytes_req, bytes_alloc); + + total_requested += bytes_req; + total_allocated += bytes_alloc; +} + +static void process_free_event(struct raw_event_sample *raw __used, + struct event *event __used, + int cpu __used, + u64 timestamp __used, + struct thread *thread __used) +{ +} + +static void +process_raw_event(event_t *raw_event __used, void *more_data, + int cpu, u64 timestamp, struct thread *thread) +{ + struct raw_event_sample *raw = more_data; + struct event *event; + int type; + + type = trace_parse_common_type(raw->data); + event = trace_find_event(type); + + if (!strcmp(event->name, "kmalloc") || + !strcmp(event->name, "kmem_cache_alloc")) { + process_alloc_event(raw, event, cpu, timestamp, thread, 0); + return; + } + + if (!strcmp(event->name, "kmalloc_node") || + !strcmp(event->name, "kmem_cache_alloc_node")) { + process_alloc_event(raw, event, cpu, timestamp, thread, 1); + return; + } + + if (!strcmp(event->name, "kfree") || + !strcmp(event->name, "kmem_cache_free")) { + process_free_event(raw, event, cpu, timestamp, thread); + return; + } +} + +static int +process_sample_event(event_t *event, unsigned long offset, unsigned long head) +{ + u64 ip = event->ip.ip; + u64 timestamp = -1; + u32 cpu = -1; + u64 period = 1; + void *more_data = event->ip.__more_data; + struct thread *thread = threads__findnew(event->ip.pid); + + if (sample_type & PERF_SAMPLE_TIME) { + timestamp = *(u64 *)more_data; + more_data += sizeof(u64); + } + + if (sample_type & PERF_SAMPLE_CPU) { + cpu = *(u32 *)more_data; + more_data += sizeof(u32); + more_data += sizeof(u32); /* reserved */ + } + + if (sample_type & PERF_SAMPLE_PERIOD) { + period = *(u64 *)more_data; + more_data += sizeof(u64); + } + + dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", + (void *)(offset + head), + (void *)(long)(event->header.size), + event->header.misc, + event->ip.pid, event->ip.tid, + (void *)(long)ip, + (long long)period); + + if (thread == NULL) { + pr_debug("problem processing %d event, skipping it.\n", + event->header.type); + return -1; + } + + dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); + + process_raw_event(event, more_data, cpu, timestamp, thread); + + return 0; +} + +static int sample_type_check(u64 type) +{ + sample_type = type; + + if (!(sample_type & PERF_SAMPLE_RAW)) { + fprintf(stderr, + "No trace sample to read. Did you call perf record " + "without -R?"); + return -1; + } + + return 0; +} + +static struct perf_file_handler file_handler = { + .process_sample_event = process_sample_event, + .process_comm_event = process_comm_event, + .sample_type_check = sample_type_check, +}; + +static int read_events(void) +{ + register_idle_thread(); + register_perf_file_handler(&file_handler); + + return mmap_dispatch_perf_file(&header, input_name, 0, 0, + &cwdlen, &cwd); +} + +static double fragmentation(unsigned long n_req, unsigned long n_alloc) +{ + if (n_alloc == 0) + return 0.0; + else + return 100.0 - (100.0 * n_req / n_alloc); +} + +static void __print_result(struct rb_root *root, int n_lines, int is_caller) +{ + struct rb_node *next; + + printf("\n ------------------------------------------------------------------------------\n"); + if (is_caller) + printf(" Callsite |"); + else + printf(" Alloc Ptr |"); + printf(" Total_alloc/Per | Total_req/Per | Hit | Fragmentation\n"); + printf(" ------------------------------------------------------------------------------\n"); + + next = rb_first(root); + + while (next && n_lines--) { + struct alloc_stat *data; + + data = rb_entry(next, struct alloc_stat, node); + + printf(" %-16p | %8llu/%-6lu | %8llu/%-6lu | %6lu | %8.3f%%\n", + is_caller ? (void *)(unsigned long)data->call_site : + (void *)(unsigned long)data->ptr, + (unsigned long long)data->bytes_alloc, + (unsigned long)data->bytes_alloc / data->hit, + (unsigned long long)data->bytes_req, + (unsigned long)data->bytes_req / data->hit, + (unsigned long)data->hit, + fragmentation(data->bytes_req, data->bytes_alloc)); + + next = rb_next(next); + } + + if (n_lines == -1) + printf(" ... | ... | ... | ... | ... \n"); + + printf(" ------------------------------------------------------------------------------\n"); +} + +static void print_summary(void) +{ + printf("\nSUMMARY\n=======\n"); + printf("Total bytes requested: %lu\n", total_requested); + printf("Total bytes allocated: %lu\n", total_allocated); + printf("Total bytes wasted on internal fragmentation: %lu\n", + total_allocated - total_requested); + printf("Internal fragmentation: %f%%\n", + fragmentation(total_requested, total_allocated)); +} + +static void print_result(void) +{ + if (caller_flag) + __print_result(&root_caller_sorted, caller_lines, 1); + if (alloc_flag) + __print_result(&root_alloc_sorted, alloc_lines, 0); + print_summary(); +} + +static void sort_insert(struct rb_root *root, struct alloc_stat *data, + sort_fn_t sort_fn) +{ + struct rb_node **new = &(root->rb_node); + struct rb_node *parent = NULL; + + while (*new) { + struct alloc_stat *this; + int cmp; + + this = rb_entry(*new, struct alloc_stat, node); + parent = *new; + + cmp = sort_fn(data, this); + + if (cmp > 0) + new = &((*new)->rb_left); + else + new = &((*new)->rb_right); + } + + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); +} + +static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, + sort_fn_t sort_fn) +{ + struct rb_node *node; + struct alloc_stat *data; + + for (;;) { + node = rb_first(root); + if (!node) + break; + + rb_erase(node, root); + data = rb_entry(node, struct alloc_stat, node); + sort_insert(root_sorted, data, sort_fn); + } +} + +static void sort_result(void) +{ + __sort_result(&root_alloc_stat, &root_alloc_sorted, alloc_sort_fn); + __sort_result(&root_caller_stat, &root_caller_sorted, caller_sort_fn); +} + +static int __cmd_kmem(void) +{ + setup_pager(); + read_events(); + sort_result(); + print_result(); + + return 0; +} + +static const char * const kmem_usage[] = { + "perf kmem [] {record}", + NULL +}; + + +static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) +{ + if (l->ptr < r->ptr) + return -1; + else if (l->ptr > r->ptr) + return 1; + return 0; +} + +static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) +{ + if (l->call_site < r->call_site) + return -1; + else if (l->call_site > r->call_site) + return 1; + return 0; +} + +static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) +{ + if (l->bytes_alloc < r->bytes_alloc) + return -1; + else if (l->bytes_alloc > r->bytes_alloc) + return 1; + return 0; +} + +static int parse_sort_opt(const struct option *opt __used, + const char *arg, int unset __used) +{ + sort_fn_t sort_fn; + + if (!arg) + return -1; + + if (strcmp(arg, "ptr") == 0) + sort_fn = ptr_cmp; + else if (strcmp(arg, "call_site") == 0) + sort_fn = callsite_cmp; + else if (strcmp(arg, "bytes") == 0) + sort_fn = bytes_cmp; + else + return -1; + + if (caller_flag > alloc_flag) + caller_sort_fn = sort_fn; + else + alloc_sort_fn = sort_fn; + + return 0; +} + +static int parse_stat_opt(const struct option *opt __used, + const char *arg, int unset __used) +{ + if (!arg) + return -1; + + if (strcmp(arg, "alloc") == 0) + alloc_flag = (caller_flag + 1); + else if (strcmp(arg, "caller") == 0) + caller_flag = (alloc_flag + 1); + else + return -1; + return 0; +} + +static int parse_line_opt(const struct option *opt __used, + const char *arg, int unset __used) +{ + int lines; + + if (!arg) + return -1; + + lines = strtoul(arg, NULL, 10); + + if (caller_flag > alloc_flag) + caller_lines = lines; + else + alloc_lines = lines; + + return 0; +} + +static const struct option kmem_options[] = { + OPT_STRING('i', "input", &input_name, "file", + "input file name"), + OPT_CALLBACK(0, "stat", NULL, "|", + "stat selector, Pass 'alloc' or 'caller'.", + parse_stat_opt), + OPT_CALLBACK('s', "sort", NULL, "key", + "sort by key: ptr, call_site, hit, bytes", + parse_sort_opt), + OPT_CALLBACK('l', "line", NULL, "num", + "show n lins", + parse_line_opt), + OPT_END() +}; + +static const char *record_args[] = { + "record", + "-a", + "-R", + "-M", + "-f", + "-c", "1", + "-e", "kmem:kmalloc", + "-e", "kmem:kmalloc_node", + "-e", "kmem:kfree", + "-e", "kmem:kmem_cache_alloc", + "-e", "kmem:kmem_cache_alloc_node", + "-e", "kmem:kmem_cache_free", +}; + +static int __cmd_record(int argc, const char **argv) +{ + unsigned int rec_argc, i, j; + const char **rec_argv; + + rec_argc = ARRAY_SIZE(record_args) + argc - 1; + rec_argv = calloc(rec_argc + 1, sizeof(char *)); + + for (i = 0; i < ARRAY_SIZE(record_args); i++) + rec_argv[i] = strdup(record_args[i]); + + for (j = 1; j < (unsigned int)argc; j++, i++) + rec_argv[i] = argv[j]; + + return cmd_record(i, rec_argv, NULL); +} + +int cmd_kmem(int argc, const char **argv, const char *prefix __used) +{ + symbol__init(0); + + argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); + + if (argc && !strncmp(argv[0], "rec", 3)) + return __cmd_record(argc, argv); + else if (argc) + usage_with_options(kmem_usage, kmem_options); + + if (!alloc_sort_fn) + alloc_sort_fn = bytes_cmp; + if (!caller_sort_fn) + caller_sort_fn = bytes_cmp; + + return __cmd_kmem(); +} + diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 9b02d85091fe..a3d8bf65f26c 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -28,5 +28,6 @@ extern int cmd_top(int argc, const char **argv, const char *prefix); extern int cmd_trace(int argc, const char **argv, const char *prefix); extern int cmd_version(int argc, const char **argv, const char *prefix); extern int cmd_probe(int argc, const char **argv, const char *prefix); +extern int cmd_kmem(int argc, const char **argv, const char *prefix); #endif diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 89b82acac7d9..cf64049bc9bd 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -285,20 +285,21 @@ static void handle_internal_command(int argc, const char **argv) { const char *cmd = argv[0]; static struct cmd_struct commands[] = { - { "help", cmd_help, 0 }, - { "list", cmd_list, 0 }, { "buildid-list", cmd_buildid_list, 0 }, - { "record", cmd_record, 0 }, - { "report", cmd_report, 0 }, - { "bench", cmd_bench, 0 }, - { "stat", cmd_stat, 0 }, - { "timechart", cmd_timechart, 0 }, - { "top", cmd_top, 0 }, - { "annotate", cmd_annotate, 0 }, - { "version", cmd_version, 0 }, - { "trace", cmd_trace, 0 }, - { "sched", cmd_sched, 0 }, - { "probe", cmd_probe, 0 }, + { "help", cmd_help, 0 }, + { "list", cmd_list, 0 }, + { "record", cmd_record, 0 }, + { "report", cmd_report, 0 }, + { "bench", cmd_bench, 0 }, + { "stat", cmd_stat, 0 }, + { "timechart", cmd_timechart, 0 }, + { "top", cmd_top, 0 }, + { "annotate", cmd_annotate, 0 }, + { "version", cmd_version, 0 }, + { "trace", cmd_trace, 0 }, + { "sched", cmd_sched, 0 }, + { "probe", cmd_probe, 0 }, + { "kmem", cmd_kmem, 0 }, }; unsigned int i; static const char ext[] = STRIP_EXTENSION; -- cgit v1.2.3