From 4ddd32741da87657113d964588ce13ee64b34820 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Nov 2015 11:36:29 -0300 Subject: tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c That will contain more string functions with counterparts, sometimes verbatim copies, in the kernel. Acked-by: Wang Nan Cc: Adrian Hunter Cc: Alexey Dobriyan Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/n/tip-rah6g97kn21vfgmlramorz6o@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 6 ++++++ tools/perf/util/include/linux/string.h | 3 --- tools/perf/util/string.c | 16 ---------------- 3 files changed, 6 insertions(+), 19 deletions(-) delete mode 100644 tools/perf/util/include/linux/string.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 591b3fe3ed49..e2316900f96f 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -21,6 +21,7 @@ libperf-y += parse-events.o libperf-y += perf_regs.o libperf-y += path.o libperf-y += rbtree.o +libperf-y += libstring.o libperf-y += bitmap.o libperf-y += hweight.o libperf-y += run-command.o @@ -138,6 +139,7 @@ $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c CFLAGS_find_next_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_parse-events.o += -Wno-redundant-decls @@ -153,6 +155,10 @@ $(OUTPUT)util/rbtree.o: ../lib/rbtree.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) +$(OUTPUT)util/libstring.o: ../lib/string.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + $(OUTPUT)util/hweight.o: ../lib/hweight.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h deleted file mode 100644 index 6f19c548ecc0..000000000000 --- a/tools/perf/util/include/linux/string.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -void *memdup(const void *src, size_t len); diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index fc8781de62db..7f7e072be746 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -342,22 +342,6 @@ char *rtrim(char *s) return s; } -/** - * memdup - duplicate region of memory - * @src: memory region to duplicate - * @len: memory region length - */ -void *memdup(const void *src, size_t len) -{ - void *p; - - p = malloc(len); - if (p) - memcpy(p, src, len); - - return p; -} - char *asprintf_expr_inout_ints(const char *var, bool in, size_t nints, int *ints) { /* -- cgit v1.2.2 From 361f2b1d1d7231b8685d990b886f599378a4d5a5 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:05 +0000 Subject: perf bpf: Allow BPF program attach to uprobe events This patch adds a new syntax to the BPF object section name to support probing at uprobe event. Now we can use BPF program like this: SEC( "exec=/lib64/libc.so.6;" "libcwrite=__write" ) int libcwrite(void *ctx) { return 1; } Where, in section name of a program, before the main config string, we can use 'key=value' style options. Now the only option key is "exec", for uprobes. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-4-git-send-email-wangnan0@huawei.com [ Changed the separator from \n to ; ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++--- tools/perf/util/bpf-loader.h | 1 + 2 files changed, 115 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 4c50411371db..84169d6f2585 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -109,6 +109,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, free(priv); } +static int +config__exec(const char *value, struct perf_probe_event *pev) +{ + pev->uprobes = true; + pev->target = strdup(value); + if (!pev->target) + return -ENOMEM; + return 0; +} + +static struct { + const char *key; + const char *usage; + const char *desc; + int (*func)(const char *, struct perf_probe_event *); +} bpf_config_terms[] = { + { + .key = "exec", + .usage = "exec=", + .desc = "Set uprobe target", + .func = config__exec, + }, +}; + +static int +do_config(const char *key, const char *value, + struct perf_probe_event *pev) +{ + unsigned int i; + + pr_debug("config bpf program: %s=%s\n", key, value); + for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++) + if (strcmp(key, bpf_config_terms[i].key) == 0) + return bpf_config_terms[i].func(value, pev); + + pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n", + key, value); + + pr_debug("\nHint: Currently valid options are:\n"); + for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++) + pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage, + bpf_config_terms[i].desc); + pr_debug("\n"); + + return -BPF_LOADER_ERRNO__CONFIG_TERM; +} + +static const char * +parse_config_kvpair(const char *config_str, struct perf_probe_event *pev) +{ + char *text = strdup(config_str); + char *sep, *line; + const char *main_str = NULL; + int err = 0; + + if (!text) { + pr_debug("No enough memory: dup config_str failed\n"); + return ERR_PTR(-ENOMEM); + } + + line = text; + while ((sep = strchr(line, ';'))) { + char *equ; + + *sep = '\0'; + equ = strchr(line, '='); + if (!equ) { + pr_warning("WARNING: invalid config in BPF object: %s\n", + line); + pr_warning("\tShould be 'key=value'.\n"); + goto nextline; + } + *equ = '\0'; + + err = do_config(line, equ + 1, pev); + if (err) + break; +nextline: + line = sep + 1; + } + + if (!err) + main_str = config_str + (line - text); + free(text); + + return err ? ERR_PTR(err) : main_str; +} + +static int +parse_config(const char *config_str, struct perf_probe_event *pev) +{ + int err; + const char *main_str = parse_config_kvpair(config_str, pev); + + if (IS_ERR(main_str)) + return PTR_ERR(main_str); + + err = parse_perf_probe_command(main_str, pev); + if (err < 0) { + pr_debug("bpf: '%s' is not a valid config string\n", + config_str); + /* parse failed, don't need clear pev. */ + return -BPF_LOADER_ERRNO__CONFIG; + } + return 0; +} + static int config_bpf_program(struct bpf_program *prog) { @@ -131,13 +238,9 @@ config_bpf_program(struct bpf_program *prog) pev = &priv->pev; pr_debug("bpf: config program '%s'\n", config_str); - err = parse_perf_probe_command(config_str, pev); - if (err < 0) { - pr_debug("bpf: '%s' is not a valid config string\n", - config_str); - err = -BPF_LOADER_ERRNO__CONFIG; + err = parse_config(config_str, pev); + if (err) goto errout; - } if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { pr_debug("bpf: '%s': group for event is set and not '%s'.\n", @@ -340,6 +443,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", + [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string", }; static int @@ -420,6 +524,10 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, int err, char *buf, size_t size) { bpf__strerror_head(err, buf, size); + case BPF_LOADER_ERRNO__CONFIG_TERM: { + scnprintf(buf, size, "%s (add -v to see detail)", emsg); + break; + } bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); bpf__strerror_entry(EACCES, "You need to be root"); bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index 9caf3ae4acf3..d19f5c5d6d74 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -20,6 +20,7 @@ enum bpf_loader_errno { BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */ BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ + BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */ __BPF_LOADER_ERRNO__END, }; -- cgit v1.2.2 From 5dbd16c0c9d17ab1ab2226a5926482c26c0287ed Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:06 +0000 Subject: perf bpf: Allow attaching BPF programs to modules symbols By extending the syntax of BPF object section names, this patch allows users to attach BPF programs to symbols in modules. For example: SEC("module=i915;" "parse_cmds=i915_parse_cmds") int parse_cmds(void *ctx) { return 1; } The implementation is very simple: like what 'perf probe' does, for module, fill 'uprobe' field in 'struct perf_probe_event'. Other parts will be done automatically. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-5-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 84169d6f2585..d0f02ed93804 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -119,6 +119,16 @@ config__exec(const char *value, struct perf_probe_event *pev) return 0; } +static int +config__module(const char *value, struct perf_probe_event *pev) +{ + pev->uprobes = false; + pev->target = strdup(value); + if (!pev->target) + return -ENOMEM; + return 0; +} + static struct { const char *key; const char *usage; @@ -131,6 +141,12 @@ static struct { .desc = "Set uprobe target", .func = config__exec, }, + { + .key = "module", + .usage = "module= ", + .desc = "Set kprobe module", + .func = config__module, + } }; static int -- cgit v1.2.2 From 03e01f568759ddbfdaff892e299758e7771a3478 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:08 +0000 Subject: perf bpf: Allow BPF program config probing options By extending the syntax of BPF object section names, this patch allows users to config probing options like what they can do in 'perf probe'. The error message in 'perf probe' is also updated. Test result: For following BPF file test_probe_glob.c: # cat test_probe_glob.c __attribute__((section("inlines=no;func=SyS_dup?"), used)) int func(void *ctx) { return 1; } char _license[] __attribute__((section("license"), used)) = "GPL"; int _version __attribute__((section("version"), used)) = 0x40300; # # ./perf record -e ./test_probe_glob.c ls / ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] # ./perf evlist perf_bpf_probe:func_1 perf_bpf_probe:func After changing "inlines=no" to "inlines=yes": # ./perf record -e ./test_probe_glob.c ls / ... [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] # ./perf evlist perf_bpf_probe:func_3 perf_bpf_probe:func_2 perf_bpf_probe:func_1 perf_bpf_probe:func Then test 'force': Use following program: # cat test_probe_force.c __attribute__((section("func=sys_write"), used)) int funca(void *ctx) { return 1; } __attribute__((section("force=yes;func=sys_write"), used)) int funcb(void *ctx) { return 1; } char _license[] __attribute__((section("license"), used)) = "GPL"; int _version __attribute__((section("version"), used)) = 0x40300; # # perf record -e ./test_probe_force.c usleep 1 Error: event "func" already exists. Hint: Remove existing event by 'perf probe -d' or force duplicates by 'perf probe -f' or set 'force=yes' in BPF source. event syntax error: './test_probe_force.c' \___ Probe point exist. Try 'perf probe -d "*"' and set 'force=yes' (add -v to see detail) ... Then replace 'force=no' to 'force=yes': # vim test_probe_force.c # perf record -e ./test_probe_force.c usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data ] # perf evlist perf_bpf_probe:func_1 perf_bpf_probe:func # Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-7-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 53 +++++++++++++++++++++++++++++++++++++++++-- tools/perf/util/probe-event.c | 7 ++++-- 2 files changed, 56 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index d0f02ed93804..98f2e5d1a5be 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -7,6 +7,7 @@ #include #include +#include #include "perf.h" #include "debug.h" #include "bpf-loader.h" @@ -129,6 +130,38 @@ config__module(const char *value, struct perf_probe_event *pev) return 0; } +static int +config__bool(const char *value, + bool *pbool, bool invert) +{ + int err; + bool bool_value; + + if (!pbool) + return -EINVAL; + + err = strtobool(value, &bool_value); + if (err) + return err; + + *pbool = invert ? !bool_value : bool_value; + return 0; +} + +static int +config__inlines(const char *value, + struct perf_probe_event *pev __maybe_unused) +{ + return config__bool(value, &probe_conf.no_inlines, true); +} + +static int +config__force(const char *value, + struct perf_probe_event *pev __maybe_unused) +{ + return config__bool(value, &probe_conf.force_add, false); +} + static struct { const char *key; const char *usage; @@ -146,7 +179,19 @@ static struct { .usage = "module= ", .desc = "Set kprobe module", .func = config__module, - } + }, + { + .key = "inlines", + .usage = "inlines=[yes|no] ", + .desc = "Probe at inline symbol", + .func = config__inlines, + }, + { + .key = "force", + .usage = "force=[yes|no] ", + .desc = "Forcibly add events with existing name", + .func = config__force, + }, }; static int @@ -240,6 +285,10 @@ config_bpf_program(struct bpf_program *prog) const char *config_str; int err; + /* Initialize per-program probing setting */ + probe_conf.no_inlines = false; + probe_conf.force_add = false; + config_str = bpf_program__title(prog, false); if (IS_ERR(config_str)) { pr_debug("bpf: unable to get title for program\n"); @@ -544,7 +593,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, scnprintf(buf, size, "%s (add -v to see detail)", emsg); break; } - bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); + bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'"); bpf__strerror_entry(EACCES, "You need to be root"); bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file"); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 03875f9154e7..93996ec4bbe3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -2326,8 +2326,11 @@ static int get_new_event_name(char *buf, size_t len, const char *base, goto out; if (!allow_suffix) { - pr_warning("Error: event \"%s\" already exists. " - "(Use -f to force duplicates.)\n", buf); + pr_warning("Error: event \"%s\" already exists.\n" + " Hint: Remove existing event by 'perf probe -d'\n" + " or force duplicates by 'perf probe -f'\n" + " or set 'force=yes' in BPF source.\n", + buf); ret = -EEXIST; goto out; } -- cgit v1.2.2 From bfc077b4cf106793b30bf942e426ee99f1f4ac44 Mon Sep 17 00:00:00 2001 From: He Kuang Date: Mon, 16 Nov 2015 12:10:12 +0000 Subject: perf bpf: Add prologue for BPF programs for fetching arguments This patch generates a prologue for a BPF program which fetches arguments for it. With this patch, the program can have arguments as follow: SEC("lock_page=__lock_page page->flags") int lock_page(struct pt_regs *ctx, int err, unsigned long flags) { return 1; } This patch passes at most 3 arguments from r3, r4 and r5. r1 is still the ctx pointer. r2 is used to indicate if dereferencing was done successfully. This patch uses r6 to hold ctx (struct pt_regs) and r7 to hold stack pointer for result. Result of each arguments first store on stack: low address BPF_REG_FP - 24 ARG3 BPF_REG_FP - 16 ARG2 BPF_REG_FP - 8 ARG1 BPF_REG_FP high address Then loaded into r3, r4 and r5. The output prologue for offn(...off2(off1(reg)))) should be: r6 <- r1 // save ctx into a callee saved register r7 <- fp r7 <- r7 - stack_offset // pointer to result slot /* load r3 with the offset in pt_regs of 'reg' */ (r7) <- r3 // make slot valid r3 <- r3 + off1 // prepare to read unsafe pointer r2 <- 8 r1 <- r7 // result put onto stack call probe_read // read unsafe pointer jnei r0, 0, err // error checking r3 <- (r7) // read result r3 <- r3 + off2 // prepare to read unsafe pointer r2 <- 8 r1 <- r7 call probe_read jnei r0, 0, err ... /* load r2, r3, r4 from stack */ goto success err: r2 <- 1 /* load r3, r4, r5 with 0 */ goto usercode success: r2 <- 0 usercode: r1 <- r6 // restore ctx // original user code If all of arguments reside in register (dereferencing is not required), gen_prologue_fastpath() will be used to create fast prologue: r3 <- (r1 + offset of reg1) r4 <- (r1 + offset of reg2) r5 <- (r1 + offset of reg3) r2 <- 0 P.S. eBPF calling convention is defined as: * r0 - return value from in-kernel function, and exit value for eBPF program * r1 - r5 - arguments from eBPF program to in-kernel function * r6 - r9 - callee saved registers that in-kernel function will preserve * r10 - read-only frame pointer to access stack Committer note: At least testing if it builds and loads: # cat test_probe_arg.c struct pt_regs; __attribute__((section("lock_page=__lock_page page->flags"), used)) int func(struct pt_regs *ctx, int err, unsigned long flags) { return 1; } char _license[] __attribute__((section("license"), used)) = "GPL"; int _version __attribute__((section("version"), used)) = 0x40300; # perf record -e ./test_probe_arg.c usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.016 MB perf.data ] # perf evlist perf_bpf_probe:lock_page # Signed-off-by: He Kuang Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Wang Nan Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-11-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 + tools/perf/util/bpf-loader.c | 3 + tools/perf/util/bpf-loader.h | 3 + tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-prologue.h | 34 +++ 5 files changed, 496 insertions(+) create mode 100644 tools/perf/util/bpf-prologue.c create mode 100644 tools/perf/util/bpf-prologue.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index e2316900f96f..0513dd525d87 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -89,6 +89,7 @@ libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o +libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o libperf-$(CONFIG_LIBELF) += symbol-elf.o libperf-$(CONFIG_LIBELF) += probe-file.o libperf-$(CONFIG_LIBELF) += probe-event.o diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 98f2e5d1a5be..bd14be438cda 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -509,6 +509,9 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string", + [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", + [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", + [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", }; static int diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index d19f5c5d6d74..a58740b0f31e 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -21,6 +21,9 @@ enum bpf_loader_errno { BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */ + BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */ + BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */ + BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */ __BPF_LOADER_ERRNO__END, }; diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c new file mode 100644 index 000000000000..6cdbee119ceb --- /dev/null +++ b/tools/perf/util/bpf-prologue.c @@ -0,0 +1,455 @@ +/* + * bpf-prologue.c + * + * Copyright (C) 2015 He Kuang + * Copyright (C) 2015 Wang Nan + * Copyright (C) 2015 Huawei Inc. + */ + +#include +#include "perf.h" +#include "debug.h" +#include "bpf-loader.h" +#include "bpf-prologue.h" +#include "probe-finder.h" +#include +#include + +#define BPF_REG_SIZE 8 + +#define JMP_TO_ERROR_CODE -1 +#define JMP_TO_SUCCESS_CODE -2 +#define JMP_TO_USER_CODE -3 + +struct bpf_insn_pos { + struct bpf_insn *begin; + struct bpf_insn *end; + struct bpf_insn *pos; +}; + +static inline int +pos_get_cnt(struct bpf_insn_pos *pos) +{ + return pos->pos - pos->begin; +} + +static int +append_insn(struct bpf_insn new_insn, struct bpf_insn_pos *pos) +{ + if (!pos->pos) + return -BPF_LOADER_ERRNO__PROLOGUE2BIG; + + if (pos->pos + 1 >= pos->end) { + pr_err("bpf prologue: prologue too long\n"); + pos->pos = NULL; + return -BPF_LOADER_ERRNO__PROLOGUE2BIG; + } + + *(pos->pos)++ = new_insn; + return 0; +} + +static int +check_pos(struct bpf_insn_pos *pos) +{ + if (!pos->pos || pos->pos >= pos->end) + return -BPF_LOADER_ERRNO__PROLOGUE2BIG; + return 0; +} + +/* Give it a shorter name */ +#define ins(i, p) append_insn((i), (p)) + +/* + * Give a register name (in 'reg'), generate instruction to + * load register into an eBPF register rd: + * 'ldd target_reg, offset(ctx_reg)', where: + * ctx_reg is pre initialized to pointer of 'struct pt_regs'. + */ +static int +gen_ldx_reg_from_ctx(struct bpf_insn_pos *pos, int ctx_reg, + const char *reg, int target_reg) +{ + int offset = regs_query_register_offset(reg); + + if (offset < 0) { + pr_err("bpf: prologue: failed to get register %s\n", + reg); + return offset; + } + ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos); + + return check_pos(pos); +} + +/* + * Generate a BPF_FUNC_probe_read function call. + * + * src_base_addr_reg is a register holding base address, + * dst_addr_reg is a register holding dest address (on stack), + * result is: + * + * *[dst_addr_reg] = *([src_base_addr_reg] + offset) + * + * Arguments of BPF_FUNC_probe_read: + * ARG1: ptr to stack (dest) + * ARG2: size (8) + * ARG3: unsafe ptr (src) + */ +static int +gen_read_mem(struct bpf_insn_pos *pos, + int src_base_addr_reg, + int dst_addr_reg, + long offset) +{ + /* mov arg3, src_base_addr_reg */ + if (src_base_addr_reg != BPF_REG_ARG3) + ins(BPF_MOV64_REG(BPF_REG_ARG3, src_base_addr_reg), pos); + /* add arg3, #offset */ + if (offset) + ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, offset), pos); + + /* mov arg2, #reg_size */ + ins(BPF_ALU64_IMM(BPF_MOV, BPF_REG_ARG2, BPF_REG_SIZE), pos); + + /* mov arg1, dst_addr_reg */ + if (dst_addr_reg != BPF_REG_ARG1) + ins(BPF_MOV64_REG(BPF_REG_ARG1, dst_addr_reg), pos); + + /* Call probe_read */ + ins(BPF_EMIT_CALL(BPF_FUNC_probe_read), pos); + /* + * Error processing: if read fail, goto error code, + * will be relocated. Target should be the start of + * error processing code. + */ + ins(BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, JMP_TO_ERROR_CODE), + pos); + + return check_pos(pos); +} + +/* + * Each arg should be bare register. Fetch and save them into argument + * registers (r3 - r5). + * + * BPF_REG_1 should have been initialized with pointer to + * 'struct pt_regs'. + */ +static int +gen_prologue_fastpath(struct bpf_insn_pos *pos, + struct probe_trace_arg *args, int nargs) +{ + int i, err = 0; + + for (i = 0; i < nargs; i++) { + err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value, + BPF_PROLOGUE_START_ARG_REG + i); + if (err) + goto errout; + } + + return check_pos(pos); +errout: + return err; +} + +/* + * Slow path: + * At least one argument has the form of 'offset($rx)'. + * + * Following code first stores them into stack, then loads all of then + * to r2 - r5. + * Before final loading, the final result should be: + * + * low address + * BPF_REG_FP - 24 ARG3 + * BPF_REG_FP - 16 ARG2 + * BPF_REG_FP - 8 ARG1 + * BPF_REG_FP + * high address + * + * For each argument (described as: offn(...off2(off1(reg)))), + * generates following code: + * + * r7 <- fp + * r7 <- r7 - stack_offset // Ideal code should initialize r7 using + * // fp before generating args. However, + * // eBPF won't regard r7 as stack pointer + * // if it is generated by minus 8 from + * // another stack pointer except fp. + * // This is why we have to set r7 + * // to fp for each variable. + * r3 <- value of 'reg'-> generated using gen_ldx_reg_from_ctx() + * (r7) <- r3 // skip following instructions for bare reg + * r3 <- r3 + off1 . // skip if off1 == 0 + * r2 <- 8 \ + * r1 <- r7 |-> generated by gen_read_mem() + * call probe_read / + * jnei r0, 0, err ./ + * r3 <- (r7) + * r3 <- r3 + off2 . // skip if off2 == 0 + * r2 <- 8 \ // r2 may be broken by probe_read, so set again + * r1 <- r7 |-> generated by gen_read_mem() + * call probe_read / + * jnei r0, 0, err ./ + * ... + */ +static int +gen_prologue_slowpath(struct bpf_insn_pos *pos, + struct probe_trace_arg *args, int nargs) +{ + int err, i; + + for (i = 0; i < nargs; i++) { + struct probe_trace_arg *arg = &args[i]; + const char *reg = arg->value; + struct probe_trace_arg_ref *ref = NULL; + int stack_offset = (i + 1) * -8; + + pr_debug("prologue: fetch arg %d, base reg is %s\n", + i, reg); + + /* value of base register is stored into ARG3 */ + err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg, + BPF_REG_ARG3); + if (err) { + pr_err("prologue: failed to get offset of register %s\n", + reg); + goto errout; + } + + /* Make r7 the stack pointer. */ + ins(BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP), pos); + /* r7 += -8 */ + ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, stack_offset), pos); + /* + * Store r3 (base register) onto stack + * Ensure fp[offset] is set. + * fp is the only valid base register when storing + * into stack. We are not allowed to use r7 as base + * register here. + */ + ins(BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3, + stack_offset), pos); + + ref = arg->ref; + while (ref) { + pr_debug("prologue: arg %d: offset %ld\n", + i, ref->offset); + err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7, + ref->offset); + if (err) { + pr_err("prologue: failed to generate probe_read function call\n"); + goto errout; + } + + ref = ref->next; + /* + * Load previous result into ARG3. Use + * BPF_REG_FP instead of r7 because verifier + * allows FP based addressing only. + */ + if (ref) + ins(BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3, + BPF_REG_FP, stack_offset), pos); + } + } + + /* Final pass: read to registers */ + for (i = 0; i < nargs; i++) + ins(BPF_LDX_MEM(BPF_DW, BPF_PROLOGUE_START_ARG_REG + i, + BPF_REG_FP, -BPF_REG_SIZE * (i + 1)), pos); + + ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_SUCCESS_CODE), pos); + + return check_pos(pos); +errout: + return err; +} + +static int +prologue_relocate(struct bpf_insn_pos *pos, struct bpf_insn *error_code, + struct bpf_insn *success_code, struct bpf_insn *user_code) +{ + struct bpf_insn *insn; + + if (check_pos(pos)) + return -BPF_LOADER_ERRNO__PROLOGUE2BIG; + + for (insn = pos->begin; insn < pos->pos; insn++) { + struct bpf_insn *target; + u8 class = BPF_CLASS(insn->code); + u8 opcode; + + if (class != BPF_JMP) + continue; + opcode = BPF_OP(insn->code); + if (opcode == BPF_CALL) + continue; + + switch (insn->off) { + case JMP_TO_ERROR_CODE: + target = error_code; + break; + case JMP_TO_SUCCESS_CODE: + target = success_code; + break; + case JMP_TO_USER_CODE: + target = user_code; + break; + default: + pr_err("bpf prologue: internal error: relocation failed\n"); + return -BPF_LOADER_ERRNO__PROLOGUE; + } + + insn->off = target - (insn + 1); + } + return 0; +} + +int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, + struct bpf_insn *new_prog, size_t *new_cnt, + size_t cnt_space) +{ + struct bpf_insn *success_code = NULL; + struct bpf_insn *error_code = NULL; + struct bpf_insn *user_code = NULL; + struct bpf_insn_pos pos; + bool fastpath = true; + int err = 0, i; + + if (!new_prog || !new_cnt) + return -EINVAL; + + if (cnt_space > BPF_MAXINSNS) + cnt_space = BPF_MAXINSNS; + + pos.begin = new_prog; + pos.end = new_prog + cnt_space; + pos.pos = new_prog; + + if (!nargs) { + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), + &pos); + + if (check_pos(&pos)) + goto errout; + + *new_cnt = pos_get_cnt(&pos); + return 0; + } + + if (nargs > BPF_PROLOGUE_MAX_ARGS) { + pr_warning("bpf: prologue: %d arguments are dropped\n", + nargs - BPF_PROLOGUE_MAX_ARGS); + nargs = BPF_PROLOGUE_MAX_ARGS; + } + + /* First pass: validation */ + for (i = 0; i < nargs; i++) { + struct probe_trace_arg_ref *ref = args[i].ref; + + if (args[i].value[0] == '@') { + /* TODO: fetch global variable */ + pr_err("bpf: prologue: global %s%+ld not support\n", + args[i].value, ref ? ref->offset : 0); + return -ENOTSUP; + } + + while (ref) { + /* fastpath is true if all args has ref == NULL */ + fastpath = false; + + /* + * Instruction encodes immediate value using + * s32, ref->offset is long. On systems which + * can't fill long in s32, refuse to process if + * ref->offset too large (or small). + */ +#ifdef __LP64__ +#define OFFSET_MAX ((1LL << 31) - 1) +#define OFFSET_MIN ((1LL << 31) * -1) + if (ref->offset > OFFSET_MAX || + ref->offset < OFFSET_MIN) { + pr_err("bpf: prologue: offset out of bound: %ld\n", + ref->offset); + return -BPF_LOADER_ERRNO__PROLOGUEOOB; + } +#endif + ref = ref->next; + } + } + pr_debug("prologue: pass validation\n"); + + if (fastpath) { + /* If all variables are registers... */ + pr_debug("prologue: fast path\n"); + err = gen_prologue_fastpath(&pos, args, nargs); + if (err) + goto errout; + } else { + pr_debug("prologue: slow path\n"); + + /* Initialization: move ctx to a callee saved register. */ + ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos); + + err = gen_prologue_slowpath(&pos, args, nargs); + if (err) + goto errout; + /* + * start of ERROR_CODE (only slow pass needs error code) + * mov r2 <- 1 // r2 is error number + * mov r3 <- 0 // r3, r4... should be touched or + * // verifier would complain + * mov r4 <- 0 + * ... + * goto usercode + */ + error_code = pos.pos; + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1), + &pos); + + for (i = 0; i < nargs; i++) + ins(BPF_ALU64_IMM(BPF_MOV, + BPF_PROLOGUE_START_ARG_REG + i, + 0), + &pos); + ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE), + &pos); + } + + /* + * start of SUCCESS_CODE: + * mov r2 <- 0 + * goto usercode // skip + */ + success_code = pos.pos; + ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos); + + /* + * start of USER_CODE: + * Restore ctx to r1 + */ + user_code = pos.pos; + if (!fastpath) { + /* + * Only slow path needs restoring of ctx. In fast path, + * register are loaded directly from r1. + */ + ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos); + err = prologue_relocate(&pos, error_code, success_code, + user_code); + if (err) + goto errout; + } + + err = check_pos(&pos); + if (err) + goto errout; + + *new_cnt = pos_get_cnt(&pos); + return 0; +errout: + return err; +} diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h new file mode 100644 index 000000000000..d94cbea12899 --- /dev/null +++ b/tools/perf/util/bpf-prologue.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015, He Kuang + * Copyright (C) 2015, Huawei Inc. + */ +#ifndef __BPF_PROLOGUE_H +#define __BPF_PROLOGUE_H + +#include +#include +#include "probe-event.h" + +#define BPF_PROLOGUE_MAX_ARGS 3 +#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3 +#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2 + +#ifdef HAVE_BPF_PROLOGUE +int bpf__gen_prologue(struct probe_trace_arg *args, int nargs, + struct bpf_insn *new_prog, size_t *new_cnt, + size_t cnt_space); +#else +static inline int +bpf__gen_prologue(struct probe_trace_arg *args __maybe_unused, + int nargs __maybe_unused, + struct bpf_insn *new_prog __maybe_unused, + size_t *new_cnt, + size_t cnt_space __maybe_unused) +{ + if (!new_cnt) + return -EINVAL; + *new_cnt = 0; + return -ENOTSUP; +} +#endif +#endif /* __BPF_PROLOGUE_H */ -- cgit v1.2.2 From a08357d8dc7d3025d1094f727ad1f7e837766f93 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:13 +0000 Subject: perf bpf: Generate prologue for BPF programs This patch generates a prologue for each 'struct probe_trace_event' for fetching arguments for BPF programs. After bpf__probe(), iterate over each program to check whether prologues are required. If none of the 'struct perf_probe_event' programs will attach to have at least one argument, simply skip preprocessor hooking. For those who a prologue is required, call bpf__gen_prologue() and paste the original instruction after the prologue. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-12-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index bd14be438cda..190a1c7f0649 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -5,12 +5,15 @@ * Copyright (C) 2015 Huawei Inc. */ +#include #include #include #include #include "perf.h" #include "debug.h" #include "bpf-loader.h" +#include "bpf-prologue.h" +#include "llvm-utils.h" #include "probe-event.h" #include "probe-finder.h" // for MAX_PROBES #include "llvm-utils.h" @@ -33,6 +36,8 @@ DEFINE_PRINT_FN(debug, 1) struct bpf_prog_priv { struct perf_probe_event pev; + bool need_prologue; + struct bpf_insn *insns_buf; }; static bool libbpf_initialized; @@ -107,6 +112,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, struct bpf_prog_priv *priv = _priv; cleanup_perf_probe_events(&priv->pev, 1); + zfree(&priv->insns_buf); free(priv); } @@ -365,6 +371,102 @@ static int bpf__prepare_probe(void) return err; } +static int +preproc_gen_prologue(struct bpf_program *prog, int n, + struct bpf_insn *orig_insns, int orig_insns_cnt, + struct bpf_prog_prep_result *res) +{ + struct probe_trace_event *tev; + struct perf_probe_event *pev; + struct bpf_prog_priv *priv; + struct bpf_insn *buf; + size_t prologue_cnt = 0; + int err; + + err = bpf_program__get_private(prog, (void **)&priv); + if (err || !priv) + goto errout; + + pev = &priv->pev; + + if (n < 0 || n >= pev->ntevs) + goto errout; + + tev = &pev->tevs[n]; + + buf = priv->insns_buf; + err = bpf__gen_prologue(tev->args, tev->nargs, + buf, &prologue_cnt, + BPF_MAXINSNS - orig_insns_cnt); + if (err) { + const char *title; + + title = bpf_program__title(prog, false); + if (!title) + title = "[unknown]"; + + pr_debug("Failed to generate prologue for program %s\n", + title); + return err; + } + + memcpy(&buf[prologue_cnt], orig_insns, + sizeof(struct bpf_insn) * orig_insns_cnt); + + res->new_insn_ptr = buf; + res->new_insn_cnt = prologue_cnt + orig_insns_cnt; + res->pfd = NULL; + return 0; + +errout: + pr_debug("Internal error in preproc_gen_prologue\n"); + return -BPF_LOADER_ERRNO__PROLOGUE; +} + +static int hook_load_preprocessor(struct bpf_program *prog) +{ + struct perf_probe_event *pev; + struct bpf_prog_priv *priv; + bool need_prologue = false; + int err, i; + + err = bpf_program__get_private(prog, (void **)&priv); + if (err || !priv) { + pr_debug("Internal error when hook preprocessor\n"); + return -BPF_LOADER_ERRNO__INTERNAL; + } + + pev = &priv->pev; + for (i = 0; i < pev->ntevs; i++) { + struct probe_trace_event *tev = &pev->tevs[i]; + + if (tev->nargs > 0) { + need_prologue = true; + break; + } + } + + /* + * Since all tevs don't have argument, we don't need generate + * prologue. + */ + if (!need_prologue) { + priv->need_prologue = false; + return 0; + } + + priv->need_prologue = true; + priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); + if (!priv->insns_buf) { + pr_debug("No enough memory: alloc insns_buf failed\n"); + return -ENOMEM; + } + + err = bpf_program__set_prep(prog, pev->ntevs, + preproc_gen_prologue); + return err; +} + int bpf__probe(struct bpf_object *obj) { int err = 0; @@ -399,6 +501,18 @@ int bpf__probe(struct bpf_object *obj) pr_debug("bpf_probe: failed to apply perf probe events"); goto out; } + + /* + * After probing, let's consider prologue, which + * adds program fetcher to BPF programs. + * + * hook_load_preprocessorr() hooks pre-processor + * to bpf_program, let it generate prologue + * dynamically during loading. + */ + err = hook_load_preprocessor(prog); + if (err) + goto out; } out: return err < 0 ? err : 0; @@ -482,7 +596,11 @@ int bpf__foreach_tev(struct bpf_object *obj, for (i = 0; i < pev->ntevs; i++) { tev = &pev->tevs[i]; - fd = bpf_program__fd(prog); + if (priv->need_prologue) + fd = bpf_program__nth_fd(prog, i); + else + fd = bpf_program__fd(prog); + if (fd < 0) { pr_debug("bpf: failed to get file descriptor\n"); return fd; -- cgit v1.2.2 From d35b32891a61f1d3909bdc5280badf309adc4693 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:47 +0000 Subject: perf bpf: Use same BPF program if arguments are identical This patch allows creating only one BPF program for different 'probe_trace_event'(tev) entries generated by one 'perf_probe_event'(pev) if their prologues are identical. This is done by comparing the argument list of different tev instances, and the maps type of prologue and tev using a mapping array. This patch utilizes qsort to sort the tevs. After sorting, tevs with identical argument lists will be grouped together. Test result: Sample BPF program: #define SEC(NAME) __attribute__((section(NAME), used)) SEC("inlines=no;" "func=SyS_dup? oldfd") int func(void *ctx) { return 1; } It would probe at SyS_dup2 and SyS_dup3, obtaining oldfd as its argument. The following cmdline shows a BPF program being loaded into the kernel by perf: # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog Before this patch: # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog [1] 24858 lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog ... After this patch: # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog [1] 25699 lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog ... Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447749170-175898-3-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 190a1c7f0649..36544e5ece43 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -38,6 +38,8 @@ struct bpf_prog_priv { struct perf_probe_event pev; bool need_prologue; struct bpf_insn *insns_buf; + int nr_types; + int *type_mapping; }; static bool libbpf_initialized; @@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, cleanup_perf_probe_events(&priv->pev, 1); zfree(&priv->insns_buf); + zfree(&priv->type_mapping); free(priv); } @@ -381,7 +384,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n, struct bpf_prog_priv *priv; struct bpf_insn *buf; size_t prologue_cnt = 0; - int err; + int i, err; err = bpf_program__get_private(prog, (void **)&priv); if (err || !priv) @@ -389,10 +392,21 @@ preproc_gen_prologue(struct bpf_program *prog, int n, pev = &priv->pev; - if (n < 0 || n >= pev->ntevs) + if (n < 0 || n >= priv->nr_types) goto errout; - tev = &pev->tevs[n]; + /* Find a tev belongs to that type */ + for (i = 0; i < pev->ntevs; i++) { + if (priv->type_mapping[i] == n) + break; + } + + if (i >= pev->ntevs) { + pr_debug("Internal error: prologue type %d not found\n", n); + return -BPF_LOADER_ERRNO__PROLOGUE; + } + + tev = &pev->tevs[i]; buf = priv->insns_buf; err = bpf__gen_prologue(tev->args, tev->nargs, @@ -423,6 +437,101 @@ errout: return -BPF_LOADER_ERRNO__PROLOGUE; } +/* + * compare_tev_args is reflexive, transitive and antisymmetric. + * I can proof it but this margin is too narrow to contain. + */ +static int compare_tev_args(const void *ptev1, const void *ptev2) +{ + int i, ret; + const struct probe_trace_event *tev1 = + *(const struct probe_trace_event **)ptev1; + const struct probe_trace_event *tev2 = + *(const struct probe_trace_event **)ptev2; + + ret = tev2->nargs - tev1->nargs; + if (ret) + return ret; + + for (i = 0; i < tev1->nargs; i++) { + struct probe_trace_arg *arg1, *arg2; + struct probe_trace_arg_ref *ref1, *ref2; + + arg1 = &tev1->args[i]; + arg2 = &tev2->args[i]; + + ret = strcmp(arg1->value, arg2->value); + if (ret) + return ret; + + ref1 = arg1->ref; + ref2 = arg2->ref; + + while (ref1 && ref2) { + ret = ref2->offset - ref1->offset; + if (ret) + return ret; + + ref1 = ref1->next; + ref2 = ref2->next; + } + + if (ref1 || ref2) + return ref2 ? 1 : -1; + } + + return 0; +} + +/* + * Assign a type number to each tevs in a pev. + * mapping is an array with same slots as tevs in that pev. + * nr_types will be set to number of types. + */ +static int map_prologue(struct perf_probe_event *pev, int *mapping, + int *nr_types) +{ + int i, type = 0; + struct probe_trace_event **ptevs; + + size_t array_sz = sizeof(*ptevs) * pev->ntevs; + + ptevs = malloc(array_sz); + if (!ptevs) { + pr_debug("No ehough memory: alloc ptevs failed\n"); + return -ENOMEM; + } + + pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs); + for (i = 0; i < pev->ntevs; i++) + ptevs[i] = &pev->tevs[i]; + + qsort(ptevs, pev->ntevs, sizeof(*ptevs), + compare_tev_args); + + for (i = 0; i < pev->ntevs; i++) { + int n; + + n = ptevs[i] - pev->tevs; + if (i == 0) { + mapping[n] = type; + pr_debug("mapping[%d]=%d\n", n, type); + continue; + } + + if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0) + mapping[n] = type; + else + mapping[n] = ++type; + + pr_debug("mapping[%d]=%d\n", n, mapping[n]); + } + free(ptevs); + *nr_types = type + 1; + + return 0; +} + static int hook_load_preprocessor(struct bpf_program *prog) { struct perf_probe_event *pev; @@ -462,7 +571,19 @@ static int hook_load_preprocessor(struct bpf_program *prog) return -ENOMEM; } - err = bpf_program__set_prep(prog, pev->ntevs, + priv->type_mapping = malloc(sizeof(int) * pev->ntevs); + if (!priv->type_mapping) { + pr_debug("No enough memory: alloc type_mapping failed\n"); + return -ENOMEM; + } + memset(priv->type_mapping, -1, + sizeof(int) * pev->ntevs); + + err = map_prologue(pev, priv->type_mapping, &priv->nr_types); + if (err) + return err; + + err = bpf_program__set_prep(prog, priv->nr_types, preproc_gen_prologue); return err; } @@ -596,10 +717,13 @@ int bpf__foreach_tev(struct bpf_object *obj, for (i = 0; i < pev->ntevs; i++) { tev = &pev->tevs[i]; - if (priv->need_prologue) - fd = bpf_program__nth_fd(prog, i); - else + if (priv->need_prologue) { + int type = priv->type_mapping[i]; + + fd = bpf_program__nth_fd(prog, type); + } else { fd = bpf_program__fd(prog); + } if (fd < 0) { pr_debug("bpf: failed to get file descriptor\n"); -- cgit v1.2.2 From 05c8d802fa52ef17dbcce21c38b72b4a313eb036 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:12 +0900 Subject: perf probe: Fix to free temporal Dwarf_Frame Since dwarf_cfi_addrframe returns malloc'd Dwarf_Frame object, it has to be freed after it is used. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064011.30709.65674.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 05012bb178d7..1cab05a3831e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -683,21 +683,24 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); if (ret <= 0 || nops == 0) { pf->fb_ops = NULL; + ret = 0; #if _ELFUTILS_PREREQ(0, 142) } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && pf->cfi != NULL) { - Dwarf_Frame *frame; + Dwarf_Frame *frame = NULL; if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { pr_warning("Failed to get call frame on 0x%jx\n", (uintmax_t)pf->addr); - return -ENOENT; + ret = -ENOENT; } + free(frame); #endif } /* Call finder's callback handler */ - ret = pf->callback(sc_die, pf); + if (ret >= 0) + ret = pf->callback(sc_die, pf); /* *pf->fb_ops will be cached in libdw. Don't free it. */ pf->fb_ops = NULL; -- cgit v1.2.2 From 9afcb420d6cfeadf5d872f395061c611536615fb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:20 +0900 Subject: perf machine: Fix machine__findnew_module_map to put registered map Fix machine object to drop the reference to the map object after it inserted it into machine->kmaps. refcnt debugger shows what happened: ---- ==== [2] ==== Unreclaimed map: 0x346f750 Refcount +1 => 1 at ./perf(map__new2+0xb5) [0x4bdea5] ./perf() [0x4b8aaf] ./perf(modules__parse+0xfc) [0x4a9cbc] ./perf() [0x4b83c0] ./perf(machine__create_kernel_maps+0x148) [0x4bb208] ./perf(machine__new_host+0xfa) [0x4bb3fa] ./perf(init_probe_symbol_maps+0x93) [0x5062b3] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f5373899af5] ./perf() [0x4220a9] Refcount +1 => 2 at ./perf(maps__insert+0x9a) [0x4bfd4a] ./perf() [0x4b8acb] ./perf(modules__parse+0xfc) [0x4a9cbc] ./perf() [0x4b83c0] ./perf(machine__create_kernel_maps+0x148) [0x4bb208] ./perf(machine__new_host+0xfa) [0x4bb3fa] ./perf(init_probe_symbol_maps+0x93) [0x5062b3] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f5373899af5] ./perf() [0x4220a9] Refcount -1 => 1 at ./perf(map_groups__exit+0x94) [0x4bea54] ./perf(machine__delete+0x3d) [0x4b91ed] ./perf(exit_probe_symbol_maps+0x28) [0x506358] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f5373899af5] ./perf() [0x4220a9] ---- This pattern clearly shows that the refcnt of the map is acquired twice by map__new2 and maps__insert but released onlu once at map_groups__exit, when we purge its maps rbtree. Since maps__insert already reference counted the map, we have to drop the constructor (map__new2) reference count right after inserting it. These happened in machine__findnew_module_map, as below. ---- # eu-addr2line -e ./perf -f 0x4b8aaf machine__findnew_module_map inlined at util/machine.c:1046 in machine__create_module util/machine.c:582 # eu-addr2line -e ./perf -f 0x4b8acb map_groups__insert inlined at util/machine.c:585 in machine__create_module util/map.h:208 ---- (note that both are at util/machine.c:58X which is machine__findnew_module_map) Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064020.30709.40499.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 8b303ff20289..0487d7795f13 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -585,6 +585,8 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, map_groups__insert(&machine->kmaps, map); + /* Put the map here because map_groups__insert alread got it */ + map__put(map); out: free(m.name); return map; -- cgit v1.2.2 From e96e4078e9a5ea150b3ad9a296440a7976439e4a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:22 +0900 Subject: perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references Fix machine__destroy_kernel_maps() to drop vmlinux_maps references before filling it with NULL. Refcnt debugger shows ==== [1] ==== Unreclaimed map: 0x36b1070 Refcount +1 => 1 at ./perf(map__new2+0xb5) [0x4bdec5] ./perf(machine__create_kernel_maps+0x72) [0x4bb152] ./perf(machine__new_host+0xfa) [0x4bb41a] ./perf(init_probe_symbol_maps+0x93) [0x5062d3] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1fc9fc4af5] ./perf() [0x4220a9] Refcount +1 => 2 at ./perf(maps__insert+0x9a) [0x4bfd6a] ./perf(machine__create_kernel_maps+0xc3) [0x4bb1a3] ./perf(machine__new_host+0xfa) [0x4bb41a] ./perf(init_probe_symbol_maps+0x93) [0x5062d3] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1fc9fc4af5] ./perf() [0x4220a9] Refcount -1 => 1 at ./perf(map_groups__exit+0x94) [0x4bea74] ./perf(machine__delete+0x3d) [0x4b91fd] ./perf(exit_probe_symbol_maps+0x28) [0x506378] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1fc9fc4af5] ./perf() [0x4220a9] map__new2() returns map with refcnt = 1, and also map_groups__insert gets it again in__machine__create_kernel_maps(). machine__destroy_kernel_maps() calls map_groups__remove() to decrement the refcnt, but before decrement it again (corresponding to map__new2), it makes vmlinux_maps[type] = NULL. And this may cause a refcnt leak. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064022.30709.3897.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 0487d7795f13..e9e09bee221c 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -790,6 +790,7 @@ void machine__destroy_kernel_maps(struct machine *machine) kmap->ref_reloc_sym = NULL; } + map__put(machine->vmlinux_maps[type]); machine->vmlinux_maps[type] = NULL; } } -- cgit v1.2.2 From ebe9729c8c3171aa46ad5d7af40acdc29806689d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:24 +0900 Subject: perf machine: Fix to destroy kernel maps when machine exits Actually machine__exit forgot to call machine__destroy_kernel_maps. This fixes some memory leaks on map as below. Without this fix. ---- ./perf probe vfs_read Added new event: probe:vfs_read (on vfs_read) You can now use it in all perf tools, such as: perf record -e probe:vfs_read -aR sleep 1 REFCNT: BUG: Unreclaimed objects found. REFCNT: Total 4 objects are not reclaimed. To see all backtraces, rerun with -v option ---- With this fix. ---- ./perf probe vfs_read Added new event: probe:vfs_read (on vfs_read) You can now use it in all perf tools, such as: perf record -e probe:vfs_read -aR sleep 1 REFCNT: BUG: Unreclaimed objects found. REFCNT: Total 2 objects are not reclaimed. To see all backtraces, rerun with -v option ---- Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064024.30709.43577.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e9e09bee221c..a358771fe9e3 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -122,6 +122,7 @@ void machine__delete_threads(struct machine *machine) void machine__exit(struct machine *machine) { + machine__destroy_kernel_maps(machine); map_groups__exit(&machine->kmaps); dsos__exit(&machine->dsos); machine__exit_vdso(machine); -- cgit v1.2.2 From c4068f51d40df151a661a384ab1309b11d7f012e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 19 Nov 2015 15:04:53 +0900 Subject: perf tools: Make perf_exec_path() always return malloc'd string Since system_path() returns malloc'd string if given path is not an absolute path, perf_exec_path() sometimes returns a static string and sometimes returns a malloc'd string depending on the environment variables or command options. This may cause a memory leak because the caller can not unconditionally free the returned string. This fixes perf_exec_path() and system_path() to always return a malloc'd string, so the caller can always free it. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151119060453.14210.65666.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/exec_cmd.c | 21 +++++++++++---------- tools/perf/util/exec_cmd.h | 5 +++-- tools/perf/util/help.c | 6 ++++-- 3 files changed, 18 insertions(+), 14 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 7adf4ad15d8f..1099e92f5ee1 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -9,17 +9,17 @@ static const char *argv_exec_path; static const char *argv0_path; -const char *system_path(const char *path) +char *system_path(const char *path) { static const char *prefix = PREFIX; struct strbuf d = STRBUF_INIT; if (is_absolute_path(path)) - return path; + return strdup(path); strbuf_addf(&d, "%s/%s", prefix, path); path = strbuf_detach(&d, NULL); - return path; + return (char *)path; } const char *perf_extract_argv0_path(const char *argv0) @@ -52,17 +52,16 @@ void perf_set_argv_exec_path(const char *exec_path) /* Returns the highest-priority, location to look for perf programs. */ -const char *perf_exec_path(void) +char *perf_exec_path(void) { - const char *env; + char *env; if (argv_exec_path) - return argv_exec_path; + return strdup(argv_exec_path); env = getenv(EXEC_PATH_ENVIRONMENT); - if (env && *env) { - return env; - } + if (env && *env) + return strdup(env); return system_path(PERF_EXEC_PATH); } @@ -83,9 +82,11 @@ void setup_path(void) { const char *old_path = getenv("PATH"); struct strbuf new_path = STRBUF_INIT; + char *tmp = perf_exec_path(); - add_path(&new_path, perf_exec_path()); + add_path(&new_path, tmp); add_path(&new_path, argv0_path); + free(tmp); if (old_path) strbuf_addstr(&new_path, old_path); diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index bc4b915963f5..48b4175f1e11 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h @@ -3,10 +3,11 @@ extern void perf_set_argv_exec_path(const char *exec_path); extern const char *perf_extract_argv0_path(const char *path); -extern const char *perf_exec_path(void); extern void setup_path(void); extern int execv_perf_cmd(const char **argv); /* NULL terminated */ extern int execl_perf_cmd(const char *cmd, ...); -extern const char *system_path(const char *path); +/* perf_exec_path and system_path return malloc'd string, caller must free it */ +extern char *perf_exec_path(void); +extern char *system_path(const char *path); #endif /* __PERF_EXEC_CMD_H */ diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 86c37c472263..fa1fc4acb8a4 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -159,7 +159,7 @@ void load_command_list(const char *prefix, struct cmdnames *other_cmds) { const char *env_path = getenv("PATH"); - const char *exec_path = perf_exec_path(); + char *exec_path = perf_exec_path(); if (exec_path) { list_commands_in_dir(main_cmds, exec_path, prefix); @@ -187,6 +187,7 @@ void load_command_list(const char *prefix, sizeof(*other_cmds->names), cmdname_compare); uniq(other_cmds); } + free(exec_path); exclude_cmds(other_cmds, main_cmds); } @@ -203,13 +204,14 @@ void list_commands(const char *title, struct cmdnames *main_cmds, longest = other_cmds->names[i]->len; if (main_cmds->cnt) { - const char *exec_path = perf_exec_path(); + char *exec_path = perf_exec_path(); printf("available %s in '%s'\n", title, exec_path); printf("----------------"); mput_char('-', strlen(title) + strlen(exec_path)); putchar('\n'); pretty_print_string_list(main_cmds, longest); putchar('\n'); + free(exec_path); } if (other_cmds->cnt) { -- cgit v1.2.2 From 8d5c340dfcd48751fdff301bb2a7e3f875652dcb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:27 +0900 Subject: perf tools: Fix to put new map after inserting to map_groups in dso__load_sym Fix dso__load_sym to put the map object which is already insterted to kmaps. Refcnt debugger shows ==== [0] ==== Unreclaimed map: 0x39113e0 Refcount +1 => 1 at ./perf(map__new2+0xb5) [0x4be155] ./perf(dso__load_sym+0xee1) [0x503461] ./perf(dso__load_vmlinux+0xbf) [0x4aa6df] ./perf(dso__load_vmlinux_path+0x8c) [0x4aa83c] ./perf() [0x50528a] ./perf(convert_perf_probe_events+0xd79) [0x50ac29] ./perf() [0x45600f] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f152368baf5] ./perf() [0x4220a9] Refcount +1 => 2 at ./perf(maps__insert+0x9a) [0x4bfffa] ./perf(dso__load_sym+0xf89) [0x503509] ./perf(dso__load_vmlinux+0xbf) [0x4aa6df] ./perf(dso__load_vmlinux_path+0x8c) [0x4aa83c] ./perf() [0x50528a] ./perf(convert_perf_probe_events+0xd79) [0x50ac29] ./perf() [0x45600f] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f152368baf5] ./perf() [0x4220a9] Refcount -1 => 1 at ./perf(map_groups__exit+0x94) [0x4bed04] ./perf(machine__delete+0xb0) [0x4b9300] ./perf(exit_probe_symbol_maps+0x28) [0x506608] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f152368baf5] ./perf() [0x4220a9] This means that the dso__load_sym calls map__new2 and maps_insert, both of them bump the map refcount, but map_groups__exit will drop just one reference. Fix it by dropping the refcount after inserting it into kmaps. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064026.30709.50038.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 475d88d0a1c9..53f19968bfa2 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1042,6 +1042,8 @@ int dso__load_sym(struct dso *dso, struct map *map, } curr_dso->symtab_type = dso->symtab_type; map_groups__insert(kmaps, curr_map); + /* kmaps already got it */ + map__put(curr_map); dsos__add(&map->groups->machine->dsos, curr_dso); dso__set_loaded(curr_dso, map->type); } else -- cgit v1.2.2 From 82de26abdc127172fd7453a61d35a9b33bf4f871 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:31 +0900 Subject: perf tools: Fix __dsos__addnew to put dso after adding it to the list __dsos__addnew should drop the constructor reference to dso after adding it to the list, because __dsos__add() will get a reference that will be kept while it is in the list. This fixes DSO leaks when entries are removed to the list and the refcount never gets to zero. Refcnt debugger shows: ==== [0] ==== Unreclaimed dso: 0x2fccab0 Refcount +1 => 1 at ./perf(dso__new+0x1ff) [0x4a62df] ./perf(__dsos__addnew+0x29) [0x4a6e19] ./perf(dsos__findnew+0xd1) [0x4a7281] ./perf(machine__findnew_kernel+0x27) [0x4a5e17] ./perf() [0x4b8df2] ./perf(machine__create_kernel_maps+0x28) [0x4bb528] ./perf(machine__new_host+0xfa) [0x4bb84a] ./perf(init_probe_symbol_maps+0x93) [0x506713] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f46df132af5] ./perf() [0x4220a9] Refcount +1 => 2 at ./perf(__dsos__addnew+0xfb) [0x4a6eeb] ./perf(dsos__findnew+0xd1) [0x4a7281] ./perf(machine__findnew_kernel+0x27) [0x4a5e17] ./perf() [0x4b8df2] ./perf(machine__create_kernel_maps+0x28) [0x4bb528] ./perf(machine__new_host+0xfa) [0x4bb84a] ./perf(init_probe_symbol_maps+0x93) [0x506713] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f46df132af5] ./perf() [0x4220a9] Refcount +1 => 3 at ./perf(dsos__findnew+0x7e) [0x4a722e] ./perf(machine__findnew_kernel+0x27) [0x4a5e17] ./perf() [0x4b8df2] ./perf(machine__create_kernel_maps+0x28) [0x4bb528] ./perf(machine__new_host+0xfa) [0x4bb84a] ./perf(init_probe_symbol_maps+0x93) [0x506713] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f46df132af5] ./perf() [0x4220a9] [snip] Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064031.30709.81460.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 425df5c86c9c..e8e9a9dbf5e3 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1243,6 +1243,8 @@ struct dso *__dsos__addnew(struct dsos *dsos, const char *name) if (dso != NULL) { __dsos__add(dsos, dso); dso__set_basename(dso); + /* Put dso here because __dsos_add already got it */ + dso__put(dso); } return dso; } -- cgit v1.2.2 From 1154c957607afdf5936ae14e1be27d7ca4e7bd30 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:33 +0900 Subject: perf tools: Fix machine__create_kernel_maps to put kernel dso refcount Fix machine__create_kernel_maps() to put kernel dso because the dso has been gotten via __machine__create_kernel_maps(). Refcnt debugger shows: ==== [0] ==== Unreclaimed dso: 0x3036ab0 Refcount +1 => 1 at ./perf(dso__new+0x1ff) [0x4a62df] ./perf(__dsos__addnew+0x29) [0x4a6e19] ./perf(dsos__findnew+0xd1) [0x4a7181] ./perf(machine__findnew_kernel+0x27) [0x4a5e17] ./perf() [0x4b8cf2] ./perf(machine__create_kernel_maps+0x28) [0x4bb428] ./perf(machine__new_host+0xfa) [0x4bb74a] ./perf(init_probe_symbol_maps+0x93) [0x506613] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7ffa6809eaf5] ./perf() [0x4220a9] [snip] Refcount +1 => 2 at ./perf(dsos__findnew+0x7e) [0x4a712e] ./perf(machine__findnew_kernel+0x27) [0x4a5e17] ./perf() [0x4b8cf2] ./perf(machine__create_kernel_maps+0x28) [0x4bb428] ./perf(machine__new_host+0xfa) [0x4bb74a] ./perf(init_probe_symbol_maps+0x93) [0x506613] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7ffa6809eaf5] ./perf() [0x4220a9] [snip] Refcount -1 => 1 at ./perf(dso__put+0x2f) [0x4a664f] ./perf(machine__delete+0xfe) [0x4b93ee] ./perf(exit_probe_symbol_maps+0x28) [0x5066b8] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7ffa6809eaf5] ./perf() [0x4220a9] Actually, dsos__findnew gets the dso before returning it, so the dso user (in this case machine__create_kernel_maps) has to put the dso after used. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064033.30709.98954.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index a358771fe9e3..0b4a05c14204 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1088,11 +1088,14 @@ int machine__create_kernel_maps(struct machine *machine) struct dso *kernel = machine__get_kernel(machine); const char *name; u64 addr = machine__get_running_kernel_start(machine, &name); - if (!addr) + int ret; + + if (!addr || kernel == NULL) return -1; - if (kernel == NULL || - __machine__create_kernel_maps(machine, kernel) < 0) + ret = __machine__create_kernel_maps(machine, kernel); + dso__put(kernel); + if (ret < 0) return -1; if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { -- cgit v1.2.2 From 566c69c36e6178774dd484ea4a02b76f6bd0ede4 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:35 +0900 Subject: perf machine: Fix machine__findnew_module_map to put dso Fix machine__findnew_module_map to drop the reference to the dso because it is already referenced by both machine__findnew_module_dso() and map__new2(). Refcnt debugger shows: ==== [1] ==== Unreclaimed dso: 0x1ffd980 Refcount +1 => 1 at ./perf(dso__new+0x1ff) [0x4a62df] ./perf(__dsos__addnew+0x29) [0x4a6e19] ./perf() [0x4b8b91] ./perf(modules__parse+0xfc) [0x4a9d5c] ./perf() [0x4b8460] ./perf(machine__create_kernel_maps+0x150) [0x4bb550] ./perf(machine__new_host+0xfa) [0x4bb75a] ./perf(init_probe_symbol_maps+0x93) [0x506623] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1345a8eaf5] ./perf() [0x4220a9] This map_groups__insert(0x4b8b91) already gets a reference to the new dso: ---- eu-addr2line -e ./perf -f 0x4b8b91 map_groups__insert inlined at util/machine.c:586 in machine__create_module util/map.h:207 ---- So this dso refcnt will be released when map_groups gets released. [snip] Refcount +1 => 2 at ./perf(dso__get+0x34) [0x4a65f4] ./perf() [0x4b8b35] ./perf(modules__parse+0xfc) [0x4a9d5c] ./perf() [0x4b8460] ./perf(machine__create_kernel_maps+0x150) [0x4bb550] ./perf(machine__new_host+0xfa) [0x4bb75a] ./perf(init_probe_symbol_maps+0x93) [0x506623] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1345a8eaf5] ./perf() [0x4220a9] Here, machine__findnew_module_dso(0x4b8b35) gets the dso (and stores it in a local variable): ---- # eu-addr2line -e ./perf -f 0x4b8b35 machine__findnew_module_dso inlined at util/machine.c:578 in machine__create_module util/machine.c:514 ---- Refcount +1 => 3 at ./perf(dso__get+0x34) [0x4a65f4] ./perf(map__new2+0x76) [0x4be1c6] ./perf() [0x4b8b4f] ./perf(modules__parse+0xfc) [0x4a9d5c] ./perf() [0x4b8460] ./perf(machine__create_kernel_maps+0x150) [0x4bb550] ./perf(machine__new_host+0xfa) [0x4bb75a] ./perf(init_probe_symbol_maps+0x93) [0x506623] ./perf() [0x455ffa] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f1345a8eaf5] ./perf() [0x4220a9] But also map__new2() gets the dso which will be put when the map is released. So, we have to drop the constructor reference obtained in machine__findnew_module_dso(). Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151118064035.30709.58824.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 0b4a05c14204..7f5071a4d9aa 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -565,7 +565,7 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, const char *filename) { struct map *map = NULL; - struct dso *dso; + struct dso *dso = NULL; struct kmod_path m; if (kmod_path__parse_name(&m, filename)) @@ -589,6 +589,8 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, /* Put the map here because map_groups__insert alread got it */ map__put(map); out: + /* put the dso here, corresponding to machine__findnew_module_dso */ + dso__put(dso); free(m.name); return map; } -- cgit v1.2.2 From 26e779245dd6f5270c0696860438e5c03d0780fd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:37 +0900 Subject: perf report: Support folded callchain mode on --stdio Add new call chain option (-g) 'folded' to print callchains in a line. The callchains are separated by semicolons, and preceded by (absolute) percent values and a space. For example, the following 20 lines can be printed in 3 lines with the folded output mode: $ perf report -g flat --no-children | grep -v ^# | head -20 60.48% swapper [kernel.vmlinux] [k] intel_idle 54.60% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry start_secondary 5.88% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry rest_init start_kernel x86_64_start_reservations x86_64_start_kernel $ perf report -g folded --no-children | grep -v ^# | head -3 60.48% swapper [kernel.vmlinux] [k] intel_idle 54.60% intel_idle;cpuidle_enter_state;cpuidle_enter;call_cpuidle;cpu_startup_entry;start_secondary 5.88% intel_idle;cpuidle_enter_state;cpuidle_enter;call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel This mode is supported only for --stdio now and intended to be used by some scripts like in FlameGraphs[1]. Support for other UI might be added later. [1] http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html Requested-and-Tested-by: Brendan Gregg Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 6 ++++++ tools/perf/util/callchain.h | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 735ad48e1858..08cb220ba5ea 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -44,6 +44,10 @@ static int parse_callchain_mode(const char *value) callchain_param.mode = CHAIN_GRAPH_REL; return 0; } + if (!strncmp(value, "folded", strlen(value))) { + callchain_param.mode = CHAIN_FOLDED; + return 0; + } return -1; } @@ -218,6 +222,7 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, switch (mode) { case CHAIN_FLAT: + case CHAIN_FOLDED: if (rnode->hit < chain->hit) p = &(*p)->rb_left; else @@ -338,6 +343,7 @@ int callchain_register_param(struct callchain_param *param) param->sort = sort_chain_graph_rel; break; case CHAIN_FLAT: + case CHAIN_FOLDED: param->sort = sort_chain_flat; break; case CHAIN_NONE: diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index fce8161e54db..544d99ac169c 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -24,7 +24,7 @@ #define CALLCHAIN_RECORD_HELP CALLCHAIN_HELP RECORD_MODE_HELP RECORD_SIZE_HELP #define CALLCHAIN_REPORT_HELP \ - HELP_PAD "print_type:\tcall graph printing style (graph|flat|fractal|none)\n" \ + HELP_PAD "print_type:\tcall graph printing style (graph|flat|fractal|folded|none)\n" \ HELP_PAD "threshold:\tminimum call graph inclusion threshold ()\n" \ HELP_PAD "print_limit:\tmaximum number of call graph entry ()\n" \ HELP_PAD "order:\t\tcall graph order (caller|callee)\n" \ @@ -43,7 +43,8 @@ enum chain_mode { CHAIN_NONE, CHAIN_FLAT, CHAIN_GRAPH_ABS, - CHAIN_GRAPH_REL + CHAIN_GRAPH_REL, + CHAIN_FOLDED, }; enum chain_order { -- cgit v1.2.2 From 5ab250cafcd884a2638b102239870bddca42ff88 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:39 +0900 Subject: perf callchain: Abstract callchain print function This is a preparation to support for printing other type of callchain value like count or period. Signed-off-by: Namhyung Kim Tested-by: Brendan Gregg Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-4-git-send-email-namhyung@kernel.org [ renamed new _sprintf_ operation to _scnprintf_ ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 29 +++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 4 ++++ 2 files changed, 33 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 08cb220ba5ea..b948bd068966 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -805,6 +805,35 @@ char *callchain_list__sym_name(struct callchain_list *cl, return bf; } +char *callchain_node__scnprintf_value(struct callchain_node *node, + char *bf, size_t bfsize, u64 total) +{ + double percent = 0.0; + u64 period = callchain_cumul_hits(node); + + if (callchain_param.mode == CHAIN_FOLDED) + period = node->hit; + if (total) + percent = period * 100.0 / total; + + scnprintf(bf, bfsize, "%.2f%%", percent); + return bf; +} + +int callchain_node__fprintf_value(struct callchain_node *node, + FILE *fp, u64 total) +{ + double percent = 0.0; + u64 period = callchain_cumul_hits(node); + + if (callchain_param.mode == CHAIN_FOLDED) + period = node->hit; + if (total) + percent = period * 100.0 / total; + + return percent_color_fprintf(fp, "%.2f%%", percent); +} + static void free_callchain_node(struct callchain_node *node) { struct callchain_list *list, *tmp; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 544d99ac169c..060e636e33ab 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -230,6 +230,10 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, char *callchain_list__sym_name(struct callchain_list *cl, char *bf, size_t bfsize, bool show_dso); +char *callchain_node__scnprintf_value(struct callchain_node *node, + char *bf, size_t bfsize, u64 total); +int callchain_node__fprintf_value(struct callchain_node *node, + FILE *fp, u64 total); void free_callchain(struct callchain_root *root); -- cgit v1.2.2 From 5e47f8ff406296bd078716d71283796ca5c6544b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:40 +0900 Subject: perf callchain: Add count fields to struct callchain_node It's to track the count of occurrences of the callchains. Signed-off-by: Namhyung Kim Acked-by: Brendan Gregg Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 10 ++++++++++ tools/perf/util/callchain.h | 7 +++++++ 2 files changed, 17 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index b948bd068966..e390edd31504 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -437,6 +437,8 @@ add_child(struct callchain_node *parent, new->children_hit = 0; new->hit = period; + new->children_count = 0; + new->count = 1; return new; } @@ -484,6 +486,9 @@ split_add_child(struct callchain_node *parent, parent->children_hit = callchain_cumul_hits(new); new->val_nr = parent->val_nr - idx_local; parent->val_nr = idx_local; + new->count = parent->count; + new->children_count = parent->children_count; + parent->children_count = callchain_cumul_counts(new); /* create a new child for the new branch if any */ if (idx_total < cursor->nr) { @@ -494,6 +499,8 @@ split_add_child(struct callchain_node *parent, parent->hit = 0; parent->children_hit += period; + parent->count = 0; + parent->children_count += 1; node = callchain_cursor_current(cursor); new = add_child(parent, cursor, period); @@ -516,6 +523,7 @@ split_add_child(struct callchain_node *parent, rb_insert_color(&new->rb_node_in, &parent->rb_root_in); } else { parent->hit = period; + parent->count = 1; } } @@ -562,6 +570,7 @@ append_chain_children(struct callchain_node *root, inc_children_hit: root->children_hit += period; + root->children_count++; } static int @@ -614,6 +623,7 @@ append_chain(struct callchain_node *root, /* we match 100% of the path, increment the hit */ if (matches == root->val_nr && cursor->pos == cursor->nr) { root->hit += period; + root->count++; return 0; } diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 060e636e33ab..cdb386d9ba02 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -60,6 +60,8 @@ struct callchain_node { struct rb_root rb_root_in; /* input tree of children */ struct rb_root rb_root; /* sorted output tree of children */ unsigned int val_nr; + unsigned int count; + unsigned int children_count; u64 hit; u64 children_hit; }; @@ -145,6 +147,11 @@ static inline u64 callchain_cumul_hits(struct callchain_node *node) return node->hit + node->children_hit; } +static inline unsigned callchain_cumul_counts(struct callchain_node *node) +{ + return node->count + node->children_count; +} + int callchain_register_param(struct callchain_param *param); int callchain_append(struct callchain_root *root, struct callchain_cursor *cursor, -- cgit v1.2.2 From f2af008695e0b54a58b76caecd52af7e6c97fb29 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:41 +0900 Subject: perf report: Add callchain value option Now -g/--call-graph option supports how to display callchain values. Possible values are 'percent', 'period' and 'count'. The percent is same as before and it's the default behavior. The period displays the raw period value rather than the percentage. The count displays the number of occurrences. $ perf report --no-children --stdio -g percent ... 39.93% swapper [kernel.vmlinux] [k] intel_idel | ---intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry | |--28.63%-- start_secondary | --11.30%-- rest_init $ perf report --no-children --show-total-period --stdio -g period ... 39.93% 13018705 swapper [kernel.vmlinux] [k] intel_idel | ---intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry | |--9334403-- start_secondary | --3684302-- rest_init $ perf report --no-children --show-nr-samples --stdio -g count ... 39.93% 80 swapper [kernel.vmlinux] [k] intel_idel | ---intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry | |--57-- start_secondary | --23-- rest_init Signed-off-by: Namhyung Kim Acked-by: Brendan Gregg Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 62 ++++++++++++++++++++++++++++++++++++++------- tools/perf/util/callchain.h | 10 +++++++- tools/perf/util/util.c | 3 ++- 3 files changed, 64 insertions(+), 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e390edd31504..717c58c1da58 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -83,6 +83,23 @@ static int parse_callchain_sort_key(const char *value) return -1; } +static int parse_callchain_value(const char *value) +{ + if (!strncmp(value, "percent", strlen(value))) { + callchain_param.value = CCVAL_PERCENT; + return 0; + } + if (!strncmp(value, "period", strlen(value))) { + callchain_param.value = CCVAL_PERIOD; + return 0; + } + if (!strncmp(value, "count", strlen(value))) { + callchain_param.value = CCVAL_COUNT; + return 0; + } + return -1; +} + static int __parse_callchain_report_opt(const char *arg, bool allow_record_opt) { @@ -106,7 +123,8 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt) if (!parse_callchain_mode(tok) || !parse_callchain_order(tok) || - !parse_callchain_sort_key(tok)) { + !parse_callchain_sort_key(tok) || + !parse_callchain_value(tok)) { /* parsing ok - move on to the next */ try_stack_size = false; goto next; @@ -820,13 +838,27 @@ char *callchain_node__scnprintf_value(struct callchain_node *node, { double percent = 0.0; u64 period = callchain_cumul_hits(node); + unsigned count = callchain_cumul_counts(node); - if (callchain_param.mode == CHAIN_FOLDED) + if (callchain_param.mode == CHAIN_FOLDED) { period = node->hit; - if (total) - percent = period * 100.0 / total; + count = node->count; + } - scnprintf(bf, bfsize, "%.2f%%", percent); + switch (callchain_param.value) { + case CCVAL_PERIOD: + scnprintf(bf, bfsize, "%"PRIu64, period); + break; + case CCVAL_COUNT: + scnprintf(bf, bfsize, "%u", count); + break; + case CCVAL_PERCENT: + default: + if (total) + percent = period * 100.0 / total; + scnprintf(bf, bfsize, "%.2f%%", percent); + break; + } return bf; } @@ -835,13 +867,25 @@ int callchain_node__fprintf_value(struct callchain_node *node, { double percent = 0.0; u64 period = callchain_cumul_hits(node); + unsigned count = callchain_cumul_counts(node); - if (callchain_param.mode == CHAIN_FOLDED) + if (callchain_param.mode == CHAIN_FOLDED) { period = node->hit; - if (total) - percent = period * 100.0 / total; + count = node->count; + } - return percent_color_fprintf(fp, "%.2f%%", percent); + switch (callchain_param.value) { + case CCVAL_PERIOD: + return fprintf(fp, "%"PRIu64, period); + case CCVAL_COUNT: + return fprintf(fp, "%u", count); + case CCVAL_PERCENT: + default: + if (total) + percent = period * 100.0 / total; + return percent_color_fprintf(fp, "%.2f%%", percent); + } + return 0; } static void free_callchain_node(struct callchain_node *node) diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index cdb386d9ba02..47bc0c57f764 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -29,7 +29,8 @@ HELP_PAD "print_limit:\tmaximum number of call graph entry ()\n" \ HELP_PAD "order:\t\tcall graph order (caller|callee)\n" \ HELP_PAD "sort_key:\tcall graph sort key (function|address)\n" \ - HELP_PAD "branch:\t\tinclude last branch info to call graph (branch)\n" + HELP_PAD "branch:\t\tinclude last branch info to call graph (branch)\n" \ + HELP_PAD "value:\t\tcall graph value (percent|period|count)\n" enum perf_call_graph_mode { CALLCHAIN_NONE, @@ -81,6 +82,12 @@ enum chain_key { CCKEY_ADDRESS }; +enum chain_value { + CCVAL_PERCENT, + CCVAL_PERIOD, + CCVAL_COUNT, +}; + struct callchain_param { bool enabled; enum perf_call_graph_mode record_mode; @@ -93,6 +100,7 @@ struct callchain_param { bool order_set; enum chain_key key; bool branch_callstack; + enum chain_value value; }; extern struct callchain_param callchain_param; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 47b1e36c7ea0..75759aebc7b8 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -21,7 +21,8 @@ struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_ABS, .min_percent = 0.5, .order = ORDER_CALLEE, - .key = CCKEY_FUNCTION + .key = CCKEY_FUNCTION, + .value = CCVAL_PERCENT, }; /* -- cgit v1.2.2 From 4b3a3212233a042f48b7b8fedc64933e1ccd8643 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:43 +0900 Subject: perf hists browser: Support flat callchains The flat callchain mode is to print all chains in a single, simple hierarchy so make it easy to see. Currently perf report --tui doesn't show flat callchains properly. With flat callchains, only leaf nodes are added to the final rbtree so it should show entries in parent nodes. To do that, add parent_val list to struct callchain_node and show them along with the (normal) val list. For example, consider following callchains with '-g graph'. $ perf report -g graph - 39.93% swapper [kernel.vmlinux] [k] intel_idle intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle - cpu_startup_entry 28.63% start_secondary - 11.30% rest_init start_kernel x86_64_start_reservations x86_64_start_kernel Before: $ perf report -g flat - 39.93% swapper [kernel.vmlinux] [k] intel_idle 28.63% start_secondary - 11.30% rest_init start_kernel x86_64_start_reservations x86_64_start_kernel After: $ perf report -g flat - 39.93% swapper [kernel.vmlinux] [k] intel_idle - 28.63% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry start_secondary - 11.30% intel_idle cpuidle_enter_state cpuidle_enter call_cpuidle cpu_startup_entry start_kernel x86_64_start_reservations x86_64_start_kernel Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Tested-by: Brendan Gregg Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 2 ++ 2 files changed, 46 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 717c58c1da58..fc3b1e0d09ee 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -387,6 +387,7 @@ create_child(struct callchain_node *parent, bool inherit_children) } new->parent = parent; INIT_LIST_HEAD(&new->val); + INIT_LIST_HEAD(&new->parent_val); if (inherit_children) { struct rb_node *n; @@ -894,6 +895,11 @@ static void free_callchain_node(struct callchain_node *node) struct callchain_node *child; struct rb_node *n; + list_for_each_entry_safe(list, tmp, &node->parent_val, list) { + list_del(&list->list); + free(list); + } + list_for_each_entry_safe(list, tmp, &node->val, list) { list_del(&list->list); free(list); @@ -917,3 +923,41 @@ void free_callchain(struct callchain_root *root) free_callchain_node(&root->node); } + +int callchain_node__make_parent_list(struct callchain_node *node) +{ + struct callchain_node *parent = node->parent; + struct callchain_list *chain, *new; + LIST_HEAD(head); + + while (parent) { + list_for_each_entry_reverse(chain, &parent->val, list) { + new = malloc(sizeof(*new)); + if (new == NULL) + goto out; + *new = *chain; + new->has_children = false; + list_add_tail(&new->list, &head); + } + parent = parent->parent; + } + + list_for_each_entry_safe_reverse(chain, new, &head, list) + list_move_tail(&chain->list, &node->parent_val); + + if (!list_empty(&node->parent_val)) { + chain = list_first_entry(&node->parent_val, struct callchain_list, list); + chain->has_children = rb_prev(&node->rb_node) || rb_next(&node->rb_node); + + chain = list_first_entry(&node->val, struct callchain_list, list); + chain->has_children = false; + } + return 0; + +out: + list_for_each_entry_safe(chain, new, &head, list) { + list_del(&chain->list); + free(chain); + } + return -ENOMEM; +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 47bc0c57f764..6e9b5f2099e1 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -56,6 +56,7 @@ enum chain_order { struct callchain_node { struct callchain_node *parent; struct list_head val; + struct list_head parent_val; struct rb_node rb_node_in; /* to insert nodes in an rbtree */ struct rb_node rb_node; /* to sort nodes in an output tree */ struct rb_root rb_root_in; /* input tree of children */ @@ -251,5 +252,6 @@ int callchain_node__fprintf_value(struct callchain_node *node, FILE *fp, u64 total); void free_callchain(struct callchain_root *root); +int callchain_node__make_parent_list(struct callchain_node *node); #endif /* __PERF_CALLCHAIN_H */ -- cgit v1.2.2 From b26b218a1e9c5815cb8964e180b7fba3cd9bd509 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 17 Nov 2015 16:05:37 +0100 Subject: perf callchain: Move initial entry call into get_entries function Moving initial entry call into get_entries function so all entries processing is on one place. It will be useful for next change that adds ordering logic. Signed-off-by: Jiri Olsa Tested-by: Milian Wolff Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1447772739-18471-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libunwind.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index c83832b555e5..0ae8844fe7a6 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -614,10 +614,22 @@ void unwind__finish_access(struct thread *thread) static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { + u64 val; unw_addr_space_t addr_space; unw_cursor_t c; int ret; + ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); + if (ret) + return ret; + + ret = entry(val, ui->thread, cb, arg); + if (ret) + return -ENOMEM; + + if (--max_stack == 0) + return 0; + addr_space = thread__priv(ui->thread); if (addr_space == NULL) return -1; @@ -640,24 +652,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack) { - u64 ip; struct unwind_info ui = { .sample = data, .thread = thread, .machine = thread->mg->machine, }; - int ret; if (!data->user_regs.regs) return -EINVAL; - ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); - if (ret) - return ret; - - ret = entry(ip, thread, cb, arg); - if (ret) - return -ENOMEM; + if (max_stack <= 0) + return -EINVAL; - return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0; + return get_entries(&ui, cb, arg, max_stack); } -- cgit v1.2.2 From cb1dc22dce6e54dbd1eac213c9216e1aa57084da Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 18 Nov 2015 08:52:47 +0100 Subject: perf callchain: Add order support for libunwind DWARF unwinder As reported by Milian, currently for DWARF unwind (both libdw and libunwind) we display callchain in callee order only. Adding the support to follow callchain order setup to libunwind DWARF unwinder, so we could get following output for report: $ perf record --call-graph dwarf ls ... $ perf report --no-children --stdio 39.26% ls libc-2.21.so [.] __strcoll_l | ---__strcoll_l mpsort_with_tmp mpsort_with_tmp sort_files main __libc_start_main _start 0 $ perf report -g caller --no-children --stdio ... 39.26% ls libc-2.21.so [.] __strcoll_l | ---0 _start __libc_start_main main sort_files mpsort_with_tmp mpsort_with_tmp __strcoll_l Based-on-patch-by: Milian Wolff Reported-and-Tested-by: Milian Wolff Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Wang Nan Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/20151118075247.GA5416@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libunwind.c | 47 ++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 17 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 0ae8844fe7a6..3c258a0e4092 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -615,34 +615,47 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { u64 val; + unw_word_t ips[max_stack]; unw_addr_space_t addr_space; unw_cursor_t c; - int ret; + int ret, i = 0; ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); if (ret) return ret; - ret = entry(val, ui->thread, cb, arg); - if (ret) - return -ENOMEM; + ips[i++] = (unw_word_t) val; - if (--max_stack == 0) - return 0; - - addr_space = thread__priv(ui->thread); - if (addr_space == NULL) - return -1; + /* + * If we need more than one entry, do the DWARF + * unwind itself. + */ + if (max_stack - 1 > 0) { + addr_space = thread__priv(ui->thread); + if (addr_space == NULL) + return -1; + + ret = unw_init_remote(&c, addr_space, ui); + if (ret) + display_error(ret); + + while (!ret && (unw_step(&c) > 0) && i < max_stack) { + unw_get_reg(&c, UNW_REG_IP, &ips[i]); + ++i; + } - ret = unw_init_remote(&c, addr_space, ui); - if (ret) - display_error(ret); + max_stack = i; + } - while (!ret && (unw_step(&c) > 0) && max_stack--) { - unw_word_t ip; + /* + * Display what we got based on the order setup. + */ + for (i = 0; i < max_stack && !ret; i++) { + int j = i; - unw_get_reg(&c, UNW_REG_IP, &ip); - ret = ip ? entry(ip, ui->thread, cb, arg) : 0; + if (callchain_param.order == ORDER_CALLER) + j = max_stack - i - 1; + ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; } return ret; -- cgit v1.2.2 From 8bd508b001629a5d836987d9a0702a6bfc4fc705 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Nov 2015 14:01:19 +0100 Subject: perf callchain: Add order support for libdw DWARF unwinder As reported by Milian, currently for DWARF unwind (both libdw and libunwind) we display callchain in callee order only. Adding the support to follow callchain order setup to libdw DWARF unwinder, so we could get following output for report: $ perf record --call-graph dwarf ls ... $ perf report --no-children --stdio 21.12% ls libc-2.21.so [.] __strcoll_l | ---__strcoll_l mpsort_with_tmp mpsort_with_tmp mpsort_with_tmp sort_files main __libc_start_main _start $ perf report --stdio --no-children -g caller 21.12% ls libc-2.21.so [.] __strcoll_l | ---_start __libc_start_main main sort_files mpsort_with_tmp mpsort_with_tmp mpsort_with_tmp __strcoll_l Reported-and-Tested-by: Milian Wolff Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Jan Kratochvil Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151119130119.GA26617@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libdw.c | 53 ++++++++++++++++++++++++++++++------------ tools/perf/util/unwind-libdw.h | 2 ++ 2 files changed, 40 insertions(+), 15 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 2dcfe9a7c8d0..db8142ba7cb9 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -11,6 +11,7 @@ #include #include "event.h" #include "perf_regs.h" +#include "callchain.h" static char *debuginfo_path; @@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *ui) return __report_module(&al, ip, ui); } +/* + * Store all entries within entries array, + * we will process it after we finish unwind. + */ static int entry(u64 ip, struct unwind_info *ui) { - struct unwind_entry e; + struct unwind_entry *e = &ui->entries[ui->idx++]; struct addr_location al; if (__report_module(&al, ip, ui)) return -1; - e.ip = ip; - e.map = al.map; - e.sym = al.sym; + e->ip = ip; + e->map = al.map; + e->sym = al.sym; pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", al.sym ? al.sym->name : "''", ip, al.map ? al.map->map_ip(al.map, ip) : (u64) 0); - - return ui->cb(&e, ui->arg); + return 0; } static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) @@ -168,7 +172,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct perf_sample *data, int max_stack) { - struct unwind_info ui = { + struct unwind_info *ui, ui_buf = { .sample = data, .thread = thread, .machine = thread->mg->machine, @@ -177,35 +181,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, .max_stack = max_stack, }; Dwarf_Word ip; - int err = -EINVAL; + int err = -EINVAL, i; if (!data->user_regs.regs) return -EINVAL; - ui.dwfl = dwfl_begin(&offline_callbacks); - if (!ui.dwfl) + ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack); + if (!ui) + return -ENOMEM; + + *ui = ui_buf; + + ui->dwfl = dwfl_begin(&offline_callbacks); + if (!ui->dwfl) goto out; err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); if (err) goto out; - err = report_module(ip, &ui); + err = report_module(ip, ui); if (err) goto out; - if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) + if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui)) goto out; - err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); + err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui); - if (err && !ui.max_stack) + if (err && !ui->max_stack) err = 0; + /* + * Display what we got based on the order setup. + */ + for (i = 0; i < ui->idx && !err; i++) { + int j = i; + + if (callchain_param.order == ORDER_CALLER) + j = ui->idx - i - 1; + + err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0; + } + out: if (err) pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); - dwfl_end(ui.dwfl); + dwfl_end(ui->dwfl); + free(ui); return 0; } diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h index 417a1426f3ad..58328669ed16 100644 --- a/tools/perf/util/unwind-libdw.h +++ b/tools/perf/util/unwind-libdw.h @@ -16,6 +16,8 @@ struct unwind_info { unwind_entry_cb_t cb; void *arg; int max_stack; + int idx; + struct unwind_entry entries[]; }; #endif /* __PERF_UNWIND_LIBDW_H */ -- cgit v1.2.2 From 646a6e846c4dc3812c614fd061603b6db5b8d380 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sat, 21 Nov 2015 11:23:55 +0100 Subject: perf callchain: Add missing parent_val initialization Adding missing parent_val callchain_node initialization. It's causing segfault in perf top: $ sudo perf top -g perf: Segmentation fault -------- backtrace -------- free_callchain_node(+0x29) in perf [0x4a4b3e] free_callchain(+0x29) in perf [0x4a5a83] hist_entry__delete(+0x126) in perf [0x4c6649] hists__delete_entry(+0x6e) in perf [0x4c66dc] hists__decay_entries(+0x7d) in perf [0x4c6776] perf_top__sort_new_samples(+0x7c) in perf [0x436a78] hist_browser__run(+0xf2) in perf [0x507760] perf_evsel__hists_browse(+0x1da) in perf [0x507c8d] perf_evlist__tui_browse_hists(+0x3e) in perf [0x5088cf] display_thread_tui(+0x7f) in perf [0x437953] start_thread(+0xc5) in libpthread-2.21.so [0x7f7068fbb555] __clone(+0x6d) in libc-2.21.so [0x7f7066fc3b9d] [0x0] Reported-and-Tested-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Masami Hiramatsu Cc: Wang Nan Fixes: 4b3a3212233a ("perf hists browser: Support flat callchains") Link: http://lkml.kernel.org/r/20151121102355.GA17313@krava.local Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.h | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 6e9b5f2099e1..8ac8f043004c 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -143,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor; static inline void callchain_init(struct callchain_root *root) { INIT_LIST_HEAD(&root->node.val); + INIT_LIST_HEAD(&root->node.parent_val); root->node.parent = NULL; root->node.hit = 0; -- cgit v1.2.2 From 4d3b16269059eee12dc572848191c8e4e7bd24b3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 25 Nov 2015 19:34:32 +0900 Subject: perf probe: Fix to free temporal Dwarf_Frame correctly The commit 05c8d802fa52 ("perf probe: Fix to free temporal Dwarf_Frame") tried to fix the memory leak of Dwarf_Frame, but it released the frame at wrong point. Since the dwarf_frame_cfa(frame, &pf->fb_ops, &nops) can return an address inside the frame data structure to pf->fb_ops, we can not release the frame before using pf->fb_ops. This reverts the commit and releases the frame afterwards (right before returning from call_probe_finder) correctly. Reported-and-Tested-by: Arnaldo Carvalho de Melo Reported-by: Michael Petlan Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Fixes: 05c8d802fa52 ("perf probe: Fix to free temporal Dwarf_Frame") LPU-Reference: 20151125103432.1473.31009.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1cab05a3831e..2be10fb27172 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -654,6 +654,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) { Dwarf_Attribute fb_attr; + Dwarf_Frame *frame = NULL; size_t nops; int ret; @@ -683,26 +684,24 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); if (ret <= 0 || nops == 0) { pf->fb_ops = NULL; - ret = 0; #if _ELFUTILS_PREREQ(0, 142) } else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa && pf->cfi != NULL) { - Dwarf_Frame *frame = NULL; if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { pr_warning("Failed to get call frame on 0x%jx\n", (uintmax_t)pf->addr); - ret = -ENOENT; + free(frame); + return -ENOENT; } - free(frame); #endif } /* Call finder's callback handler */ - if (ret >= 0) - ret = pf->callback(sc_die, pf); + ret = pf->callback(sc_die, pf); - /* *pf->fb_ops will be cached in libdw. Don't free it. */ + /* Since *pf->fb_ops can be a part of frame. we should free it here. */ + free(frame); pf->fb_ops = NULL; return ret; -- cgit v1.2.2 From b49a8fe52626814968b9a9d27d7ad1cadc5532ed Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Nov 2015 16:08:20 +0900 Subject: perf callchain: Honor hide_unresolved If user requested to hide unresolved entries, skip unresolved callchains as well as hist entries. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Kan Liang Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1448521700-32062-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 5 +++++ tools/perf/util/symbol.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 7f5071a4d9aa..f0019b72db48 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1618,6 +1618,8 @@ static int add_callchain_ip(struct thread *thread, } } + if (symbol_conf.hide_unresolved && al.sym == NULL) + return 0; return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); } @@ -1872,6 +1874,9 @@ check_calls: static int unwind_entry(struct unwind_entry *entry, void *arg) { struct callchain_cursor *cursor = arg; + + if (symbol_conf.hide_unresolved && entry->sym == NULL) + return 0; return callchain_cursor_append(cursor, entry->ip, entry->map, entry->sym); } diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index dcd786e364f2..857f707ac12b 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -108,7 +108,8 @@ struct symbol_conf { show_hist_headers, branch_callstack, has_filter, - show_ref_callgraph; + show_ref_callgraph, + hide_unresolved; const char *vmlinux_name, *kallsyms_name, *source_prefix, -- cgit v1.2.2 From 0356218a68551f051998f4fb5074a1eed7a346fe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Nov 2015 16:08:18 +0900 Subject: perf top: Fix freeze on --call-graph flat/folded The callchain rbtree is rebuilt periodically, so it needs to reinitialize the root everytime. Otherwise it can be stuck in the rbtree insertion with stale pointers. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1448521700-32062-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index fc3b1e0d09ee..564377d2bebf 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -290,6 +290,7 @@ static void sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, u64 min_hit, struct callchain_param *param __maybe_unused) { + *rb_root = RB_ROOT; __sort_chain_flat(rb_root, &root->node, min_hit); } -- cgit v1.2.2 From c03d5184f0e92fa696e4b57f54ffc3b19a92f704 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Thu, 26 Nov 2015 03:59:57 +0000 Subject: perf machine: Adjust dso->long_name for offline module Something unexpected may happen if copy statically linked perf to a production environment: # ./perf probe -m ./mymodule.ko my_func [mymodule] with build id 326ab42550ef3d24944f53c817533728367effeb not found, continuing without symbols Failed to find symbol my_func in /home/wangnan/kmodule/mymodule.ko Error: Failed to add events. # ./perf buildid-cache -a ./mymodule.ko # ./perf probe -m ./mymodule.ko my_func Added new event: probe:my_func (on my_func in /home/wangnan/kmodule/mymodule.ko) You can now use it in all perf tools, such as: perf record -e probe:my_func -aR sleep 1 Where: # ldd ./perf not a dynamic executable # strace -e open ./perf probe -m ./mymodule.ko my_func ... open("/home/wangnan/kmodule/mymodule.ko", O_RDONLY) = 3 open("/home/wangnan/kmodule/../lib64/elfutils/libebl_x86_64.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) ... open("/lib64/tls/libebl_x86_64.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/lib64/libebl_x86_64.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/tls/libebl_x86_64.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("/usr/lib64/libebl_x86_64.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) open("[mymodule]", O_RDONLY) = -1 ENOENT (No such file or directory) open("/home/wangnan/.debug/.build-id/32/6ab42550ef3d24944f53c817533728367effeb", O_RDONLY) = -1 ENOENT (No such file or directory) open("[mymodule]", O_RDONLY) = -1 ENOENT (No such file or directory) In the above example, probe fails before we put the module into buildid-cache. However, user would expect it success in both case because perf is able to find probe points actually. The reason is because perf won't utilize module's full path if it failed to open debuginfo. In: convert_to_probe_trace_events -> find_probe_trace_events_from_map -> get_target_map -> kernel_get_module_map -> machine__findnew_module_map -> map_groups__find_by_name map_groups__find_by_name() is able to find the map of that module, but this information is found from /proc/module before it knows the real path of the offline module. Therefore, the map->dso->long_name is set to something like '[mymodule]', which prevent dso__load() find the real path of the module file. In another aspect, if dso__load() can get the offline module through buildid cache, it can read symble table from that ko. Even if debuginfo is not available, 'perf probe' can success if the '.symtab' can be found. This patch improves machine__findnew_module_map(): when dso->long_name is leading with '[' (doesn't find path of module when parsing /proc/modules), fixes it by dso__set_long_name(), so following dso__load() is possible to find the symbol table. This patch won't interfere with buildid matching. Here is the test result: # ./perf probe -m ./mymodule.ko my_func Added new event: probe:my_func (on my_func in /home/wangnan/kmodule/mymodule.ko) You can now use it in all perf tools, such as: perf record -e probe:my_func -aR sleep 1 # ./perf probe -d '*' Removed event: probe:my_func # mv ./mymodule.{ko,.bak} # mv ./moduleb.ko mymodule.ko # ./perf probe -m ./mymodule.ko my_func /home/wangnan/kmodule/mymodule.ko with build id 326ab42550ef3d24944f53c817533728367effeb not found, continuing without symbols Failed to find symbol my_func in /home/wangnan/kmodule/mymodule.ko Error: Failed to add events. # ./perf probe -v -m ./mymodule.ko my_func probe-definition(0): my_func symbol:my_func file:(null) line:0 offset:0 return:0 lazy:(null) 0 arguments Could not open debuginfo. Try to use symbols. symsrc__init: build id mismatch for /home/wangnan/kmodule/mymodule.ko. /home/wangnan/kmodule/mymodule.ko with build id 326ab42550ef3d24944f53c817533728367effeb not found, continuing without symbols Failed to find symbol my_func in /home/wangnan/kmodule/mymodule.ko Error: Failed to add events. Reason: No such file or directory (Code: -2) Signed-off-by: Wang Nan Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448510397-187965-1-git-send-email-wangnan0@huawei.com [ Renamed adjust_dso_long_name() do dso__adjust_kmod_long_name() ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index f0019b72db48..95a7f6087346 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -561,6 +561,24 @@ int machine__process_switch_event(struct machine *machine __maybe_unused, return 0; } +static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename) +{ + const char *dup_filename; + + if (!filename || !dso || !dso->long_name) + return; + if (dso->long_name[0] != '[') + return; + if (!strchr(filename, '/')) + return; + + dup_filename = strdup(filename); + if (!dup_filename) + return; + + dso__set_long_name(dso, filename, true); +} + struct map *machine__findnew_module_map(struct machine *machine, u64 start, const char *filename) { @@ -573,8 +591,15 @@ struct map *machine__findnew_module_map(struct machine *machine, u64 start, map = map_groups__find_by_name(&machine->kmaps, MAP__FUNCTION, m.name); - if (map) + if (map) { + /* + * If the map's dso is an offline module, give dso__load() + * a chance to find the file path of that module by fixing + * long_name. + */ + dso__adjust_kmod_long_name(map->dso, filename); goto out; + } dso = machine__findnew_module_dso(machine, &m, filename); if (dso == NULL) -- cgit v1.2.2 From b2be5451f660e0ee230969cc24121d9e210a91de Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 26 Nov 2015 03:42:32 -0800 Subject: perf tools: Correctly identify anon_hugepage when generating map (v2) When parsing /proc/xxx/maps, the sscanf in perf_event__synthesize_mmap_events truncate the map name at the space in "/anon_hugepage (deleted)". is_anon_memory() then only receives the string "/anon_hugepage" and does not detect it. We change is_anon_memory() to only compare the first part of the string, effectively ignoring if " (deleted)" is there. Signed-off-by: Yannick Brosseau Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: Joshua Zhu Cc: kernel-team@fb.com Link: http://lkml.kernel.org/r/1448538152-2898-1-git-send-email-scientist@fb.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index afc6b56cf749..93d9f1ce3baa 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -26,8 +26,8 @@ const char *map_type__name[MAP__NR_TYPES] = { static inline int is_anon_memory(const char *filename) { return !strcmp(filename, "//anon") || - !strcmp(filename, "/dev/zero (deleted)") || - !strcmp(filename, "/anon_hugepage (deleted)"); + !strncmp(filename, "/dev/zero", sizeof("/dev/zero") - 1) || + !strncmp(filename, "/anon_hugepage", sizeof("/anon_hugepage") - 1); } static inline int is_no_dso_memory(const char *filename) -- cgit v1.2.2 From aac4864727f4b3838ec1c03277bbc47a237b7516 Mon Sep 17 00:00:00 2001 From: Ekaterina Tumanova Date: Wed, 25 Nov 2015 17:32:45 +0100 Subject: perf symbols: Refactor vmlinux_path__init() to ease path additions Refactor vmlinux_path__init() to ease subsequent additions of new vmlinux locations. Signed-off-by: Ekaterina Tumanova Acked-by: Alexander Yarygin Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Christian Borntraeger Cc: David Ahern Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1448469166-61363-2-git-send-email-tumanova@linux.vnet.ibm.com [ Rename vmlinux_path__update() to vmlinux_path__add() ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 64 +++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 31 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index cd08027a6d2c..e2ac6b6676e4 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1860,24 +1860,43 @@ static void vmlinux_path__exit(void) zfree(&vmlinux_path); } +static const char * const vmlinux_paths[] = { + "vmlinux", + "/boot/vmlinux" +}; + +static const char * const vmlinux_paths_upd[] = { + "/boot/vmlinux-%s", + "/usr/lib/debug/boot/vmlinux-%s", + "/lib/modules/%s/build/vmlinux", + "/usr/lib/debug/lib/modules/%s/vmlinux" +}; + +static int vmlinux_path__add(const char *new_entry) +{ + vmlinux_path[vmlinux_path__nr_entries] = strdup(new_entry); + if (vmlinux_path[vmlinux_path__nr_entries] == NULL) + return -1; + ++vmlinux_path__nr_entries; + + return 0; +} + static int vmlinux_path__init(struct perf_env *env) { struct utsname uts; char bf[PATH_MAX]; char *kernel_version; + unsigned int i; - vmlinux_path = malloc(sizeof(char *) * 6); + vmlinux_path = malloc(sizeof(char *) * (ARRAY_SIZE(vmlinux_paths) + + ARRAY_SIZE(vmlinux_paths_upd))); if (vmlinux_path == NULL) return -1; - vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; - vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; + for (i = 0; i < ARRAY_SIZE(vmlinux_paths); i++) + if (vmlinux_path__add(vmlinux_paths[i]) < 0) + goto out_fail; /* only try kernel version if no symfs was given */ if (symbol_conf.symfs[0] != 0) @@ -1892,28 +1911,11 @@ static int vmlinux_path__init(struct perf_env *env) kernel_version = uts.release; } - snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); - vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; - snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", - kernel_version); - vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; - snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); - vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; - snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", - kernel_version); - vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); - if (vmlinux_path[vmlinux_path__nr_entries] == NULL) - goto out_fail; - ++vmlinux_path__nr_entries; + for (i = 0; i < ARRAY_SIZE(vmlinux_paths_upd); i++) { + snprintf(bf, sizeof(bf), vmlinux_paths_upd[i], kernel_version); + if (vmlinux_path__add(bf) < 0) + goto out_fail; + } return 0; -- cgit v1.2.2 From f55ae9540d16a355e61cb57b035aab9e1ae2da28 Mon Sep 17 00:00:00 2001 From: Ekaterina Tumanova Date: Wed, 25 Nov 2015 17:32:46 +0100 Subject: perf symbols: Add the path to vmlinux.debug Currently when debuginfo is separated to vmlinux.debug, it's contents get ignored. Let's change that and add it to the vmlinux_path list. Signed-off-by: Ekaterina Tumanova Acked-by: Alexander Yarygin Acked-by: Jiri Olsa Cc: Adrian Hunter Cc: Christian Borntraeger Cc: David Ahern Cc: Namhyung Kim Cc: Naveen N. Rao Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1448469166-61363-3-git-send-email-tumanova@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e2ac6b6676e4..d51abd2e7865 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1869,7 +1869,8 @@ static const char * const vmlinux_paths_upd[] = { "/boot/vmlinux-%s", "/usr/lib/debug/boot/vmlinux-%s", "/lib/modules/%s/build/vmlinux", - "/usr/lib/debug/lib/modules/%s/vmlinux" + "/usr/lib/debug/lib/modules/%s/vmlinux", + "/usr/lib/debug/boot/vmlinux-%s.debug" }; static int vmlinux_path__add(const char *new_entry) -- cgit v1.2.2 From dcdd184b60c3943fb678dcbaf899a26f845901ad Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 25 Nov 2015 16:36:55 +0100 Subject: perf evlist: Display WEIGHT sample type bit Adding WIEGHT bit_name call to display sample_type properly. $ perf evlist -v cpu/mem-loads/pp: ...SNIP... sample_type: IP|TID|TIME|ADDR|ID|CPU|DATA_SRC|WEIGHT ... Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448465815-27404-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 397fb4ed3c97..0a1f4d9e52fc 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -1192,6 +1192,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value) bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), bit_name(IDENTIFIER), bit_name(REGS_INTR), bit_name(DATA_SRC), + bit_name(WEIGHT), { .name = NULL, } }; #undef bit_name -- cgit v1.2.2 From c4e079494f9258a3d2045779a3c9d6599c773dd9 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Fri, 27 Nov 2015 14:48:09 +0100 Subject: perf buildid-list: Show running kernel build id fix The --kernel option of perf buildid-list tool should show the running kernel buildid. The functionality has been lost during other changes of the related code. The build_id__sprintf() function should return length of the build-id string, but it was the length of the build-id raw data instead. Due to that, some return value checking caused that the final string was not printed out. With this patch the build_id__sprintf() returns the correct value, so the --kernel option works again. Before: # perf buildid-list --kernel # After: # perf buildid-list --kernel 972c1edab5bdc06cc224af45d510af662a3c6972 # Signed-off-by: Michael Petlan Cc: Jiri Olsa Cc: Masami Hiramatsu LPU-Reference: 1448632089.24573.114.camel@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/build-id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 217b5a60e2ab..6a7e273a514a 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -91,7 +91,7 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) bid += 2; } - return raw - build_id; + return (bid - bf) + 1; } int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id) -- cgit v1.2.2 From bae9cc41105b9edd74d68a9636be2ba240e74b9e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Nov 2015 15:54:33 -0300 Subject: perf list: Add support for PERF_COUNT_SW_BPF_OUT When PERF_COUNT_SW_BPF_OUTPUT was added to the kernel we should've added it to tools/perf, where it is used just to list events. This ended up causing a segfault in commands like "perf list stall". Fix it by adding that new software counter. A patch to robustify perf to not segfault when the next counter gets added in the kernel will follow this one. Reported-by: Ingo Molnar Cc: Adrian Hunter Cc: Alexei Starovoitov Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-uya354upi3eprsey6mi5962d@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index e48d9da75707..40ae92a8673c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -124,6 +124,10 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { .symbol = "dummy", .alias = "", }, + [PERF_COUNT_SW_BPF_OUTPUT] = { + .symbol = "bpf-output", + .alias = "", + }, }; #define __PERF_EVENT_FIELD(config, name) \ -- cgit v1.2.2 From e37df6c76cb19971f1228bfaff504d8a3ea6f748 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Nov 2015 16:04:58 -0300 Subject: perf list: Robustify event printing routine When a43eec304259 ("bpf: introduce bpf_perf_event_output() helper") added PERF_COUNT_SW_BPF_OUTPUT we ended up with a new entry in the event_symbols_sw array that wasn't initialized, thus set to NULL, fix print_symbol_events() to check for that case so that we don't crash if this happens again. (gdb) bt #0 __match_glob (ignore_space=false, pat=, str=) at util/string.c:198 #1 strglobmatch (str=, pat=pat@entry=0x7fffffffe61d "stall") at util/string.c:252 #2 0x00000000004993a5 in print_symbol_events (type=1, syms=0x872880 , max=11, name_only=false, event_glob=0x7fffffffe61d "stall") at util/parse-events.c:1615 #3 print_events (event_glob=event_glob@entry=0x7fffffffe61d "stall", name_only=false) at util/parse-events.c:1675 #4 0x000000000042c79e in cmd_list (argc=1, argv=0x7fffffffe390, prefix=) at builtin-list.c:68 #5 0x00000000004788a5 in run_builtin (p=p@entry=0x871758 , argc=argc@entry=2, argv=argv@entry=0x7fffffffe390) at perf.c:370 #6 0x0000000000420ab0 in handle_internal_command (argv=0x7fffffffe390, argc=2) at perf.c:429 #7 run_argv (argv=0x7fffffffe110, argcp=0x7fffffffe11c) at perf.c:473 #8 main (argc=2, argv=0x7fffffffe390) at perf.c:588 (gdb) p event_symbols_sw[PERF_COUNT_SW_BPF_OUTPUT] $4 = {symbol = 0x0, alias = 0x0} (gdb) A patch to robustify perf to not segfault when the next counter gets added in the kernel will follow this one. Reported-by: Ingo Molnar Cc: Adrian Hunter Cc: Alexei Starovoitov Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-57wysblcjfrseb0zg5u7ek10@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 40ae92a8673c..6fc8cd753e1a 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1883,7 +1883,7 @@ restart: for (i = 0; i < max; i++, syms++) { - if (event_glob != NULL && + if (event_glob != NULL && syms->symbol != NULL && !(strglobmatch(syms->symbol, event_glob) || (syms->alias && strglobmatch(syms->alias, event_glob)))) continue; -- cgit v1.2.2 From e72655d97d24fff559b4ab59de791c3741a74c8c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 28 Nov 2015 02:32:38 +0900 Subject: perf hists: Do not skip elided fields when processing samples If user gives a filter, perf marks the corresponding column elided and omits the output. But it should process and aggregates samples using the field, otherwise samples will be aggregated as if the column was not there resulted in incorrect output. For example, I'd like to set a filter on native_write_msr_safe. The original overhead of the function is negligible. $ perf report | grep native_write_msr_safe 0.00% swapper [kernel.vmlinux] native_write_msr_safe 0.00% perf [kernel.vmlinux] native_write_msr_safe However adding -S option gives different output. $ perf report -S native_write_msr_safe --percentage absolute | \ > grep -e swapper -e perf 51.47% swapper [kernel.vmlinux] 4.14% perf [kernel.vmlinux] Since it aggregated samples using comm and dso only. In fact, the above values are same when it sorts with -s comm,dso. $ perf report -s comm,dso | grep -e swapper -e perf 51.47% swapper [kernel.vmlinux] 4.14% perf [kernel.vmlinux] This resulted in TUI failure with -ERANGE since it tries to increase sample hit count for annotation with wrong symbols due to incorrect aggregation. This patch fixes it not to skip elided fields when comparing samples in order to insert them to the hists. Commiter note: After the patch, with a different workloads: # perf report --show-total-period -S native_write_msr_safe --stdio # # symbol: native_write_msr_safe # # Samples: 455 of event 'cycles:pp' # Event count (approx.): 134787489 # # Overhead Period Command Shared Object # ........ ...... ............... ................ # 0.22% 293081 qemu-system-x86 [vmlinux] 0.19% 255914 swapper [vmlinux] 0.00% 2054 Timer [vmlinux] 0.00% 1021 firefox [vmlinux] 0.00% 2 perf [vmlinux] # perf report --show-total-period | grep native_write_msr_safe Failed to open /tmp/perf-14838.map, continuing without symbols 0.22% 293081 qemu-system-x86 [vmlinux] [k] native_write_msr_safe 0.19% 255914 swapper [vmlinux] [k] native_write_msr_safe 0.00% 2054 Timer [vmlinux] [k] native_write_msr_safe 0.00% 1021 firefox [vmlinux] [k] native_write_msr_safe 0.00% 2 perf [vmlinux] [k] native_write_msr_safe # Reported-by: Ingo Molnar Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448645559-31167-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 4fd37d6708cb..6e8e0ee9ec37 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -924,9 +924,6 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { - if (perf_hpp__should_skip(fmt)) - continue; - cmp = fmt->cmp(fmt, left, right); if (cmp) break; @@ -942,9 +939,6 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { - if (perf_hpp__should_skip(fmt)) - continue; - cmp = fmt->collapse(fmt, left, right); if (cmp) break; -- cgit v1.2.2 From 0bb93490170477224f8bd4cc9ce8920517461643 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 27 Nov 2015 08:47:37 +0000 Subject: perf bpf: Rename bpf config to program config Following patches are going to introduce BPF object level configuration to enable setting values into BPF maps. To avoid confusion, this patch renames existing 'config' in bpf-loader.c to 'program config'. Following patches would introduce 'object config'. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448614067-197576-4-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-loader.c | 65 ++++++++++++++++++++++---------------------- tools/perf/util/bpf-loader.h | 2 +- 2 files changed, 33 insertions(+), 34 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 36544e5ece43..540a7efa657e 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -120,7 +120,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, } static int -config__exec(const char *value, struct perf_probe_event *pev) +prog_config__exec(const char *value, struct perf_probe_event *pev) { pev->uprobes = true; pev->target = strdup(value); @@ -130,7 +130,7 @@ config__exec(const char *value, struct perf_probe_event *pev) } static int -config__module(const char *value, struct perf_probe_event *pev) +prog_config__module(const char *value, struct perf_probe_event *pev) { pev->uprobes = false; pev->target = strdup(value); @@ -140,8 +140,7 @@ config__module(const char *value, struct perf_probe_event *pev) } static int -config__bool(const char *value, - bool *pbool, bool invert) +prog_config__bool(const char *value, bool *pbool, bool invert) { int err; bool bool_value; @@ -158,17 +157,17 @@ config__bool(const char *value, } static int -config__inlines(const char *value, - struct perf_probe_event *pev __maybe_unused) +prog_config__inlines(const char *value, + struct perf_probe_event *pev __maybe_unused) { - return config__bool(value, &probe_conf.no_inlines, true); + return prog_config__bool(value, &probe_conf.no_inlines, true); } static int -config__force(const char *value, - struct perf_probe_event *pev __maybe_unused) +prog_config__force(const char *value, + struct perf_probe_event *pev __maybe_unused) { - return config__bool(value, &probe_conf.force_add, false); + return prog_config__bool(value, &probe_conf.force_add, false); } static struct { @@ -176,58 +175,58 @@ static struct { const char *usage; const char *desc; int (*func)(const char *, struct perf_probe_event *); -} bpf_config_terms[] = { +} bpf_prog_config_terms[] = { { .key = "exec", .usage = "exec=", .desc = "Set uprobe target", - .func = config__exec, + .func = prog_config__exec, }, { .key = "module", .usage = "module= ", .desc = "Set kprobe module", - .func = config__module, + .func = prog_config__module, }, { .key = "inlines", .usage = "inlines=[yes|no] ", .desc = "Probe at inline symbol", - .func = config__inlines, + .func = prog_config__inlines, }, { .key = "force", .usage = "force=[yes|no] ", .desc = "Forcibly add events with existing name", - .func = config__force, + .func = prog_config__force, }, }; static int -do_config(const char *key, const char *value, - struct perf_probe_event *pev) +do_prog_config(const char *key, const char *value, + struct perf_probe_event *pev) { unsigned int i; pr_debug("config bpf program: %s=%s\n", key, value); - for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++) - if (strcmp(key, bpf_config_terms[i].key) == 0) - return bpf_config_terms[i].func(value, pev); + for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) + if (strcmp(key, bpf_prog_config_terms[i].key) == 0) + return bpf_prog_config_terms[i].func(value, pev); - pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n", + pr_debug("BPF: ERROR: invalid program config option: %s=%s\n", key, value); - pr_debug("\nHint: Currently valid options are:\n"); - for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++) - pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage, - bpf_config_terms[i].desc); + pr_debug("\nHint: Valid options are:\n"); + for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) + pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage, + bpf_prog_config_terms[i].desc); pr_debug("\n"); - return -BPF_LOADER_ERRNO__CONFIG_TERM; + return -BPF_LOADER_ERRNO__PROGCONF_TERM; } static const char * -parse_config_kvpair(const char *config_str, struct perf_probe_event *pev) +parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) { char *text = strdup(config_str); char *sep, *line; @@ -253,7 +252,7 @@ parse_config_kvpair(const char *config_str, struct perf_probe_event *pev) } *equ = '\0'; - err = do_config(line, equ + 1, pev); + err = do_prog_config(line, equ + 1, pev); if (err) break; nextline: @@ -268,10 +267,10 @@ nextline: } static int -parse_config(const char *config_str, struct perf_probe_event *pev) +parse_prog_config(const char *config_str, struct perf_probe_event *pev) { int err; - const char *main_str = parse_config_kvpair(config_str, pev); + const char *main_str = parse_prog_config_kvpair(config_str, pev); if (IS_ERR(main_str)) return PTR_ERR(main_str); @@ -312,7 +311,7 @@ config_bpf_program(struct bpf_program *prog) pev = &priv->pev; pr_debug("bpf: config program '%s'\n", config_str); - err = parse_config(config_str, pev); + err = parse_prog_config(config_str, pev); if (err) goto errout; @@ -750,7 +749,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", - [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string", + [ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string", [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", @@ -834,7 +833,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, int err, char *buf, size_t size) { bpf__strerror_head(err, buf, size); - case BPF_LOADER_ERRNO__CONFIG_TERM: { + case BPF_LOADER_ERRNO__PROGCONF_TERM: { scnprintf(buf, size, "%s (add -v to see detail)", emsg); break; } diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index a58740b0f31e..6fdc0457e2b6 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -20,7 +20,7 @@ enum bpf_loader_errno { BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */ BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ - BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */ + BPF_LOADER_ERRNO__PROGCONF_TERM,/* Invalid program config term in config string */ BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */ BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */ BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */ -- cgit v1.2.2 From 5dcf16df3ce48b2e4f798b1a11b5de2fc3cfd73a Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 7 Dec 2015 02:36:25 +0000 Subject: perf machine: Pass correct string to dso__adjust_kmod_long_name There's a mistake in dso__adjust_kmod_long_name() that it use strdup() to dup the new long_name of a dso, but passes the original string to dso__set_long_name(). Which causes random crash during cleanup. Signed-off-by: Wang Nan Reviewed-by: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Fixes: c03d5184f0e9 ("perf machine: Adjust dso->long_name for offline module") Link: http://lkml.kernel.org/r/1449455785-42020-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 95a7f6087346..bfc289c73c22 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -576,7 +576,7 @@ static void dso__adjust_kmod_long_name(struct dso *dso, const char *filename) if (!dup_filename) return; - dso__set_long_name(dso, filename, true); + dso__set_long_name(dso, dup_filename, true); } struct map *machine__findnew_module_map(struct machine *machine, u64 start, -- cgit v1.2.2 From 5cd95fc3f8d84a8bb256838fa3b6b59e9095eaa2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:40 +0100 Subject: perf evsel: Use event maps directly in perf_evsel__enable All events now share proper cpu and thread maps. There's no need to pass those maps from evlist, it's safe to use evsel maps for enabling event. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 5 ++++- tools/perf/util/evsel.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0a1f4d9e52fc..3a9b5068667d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -981,8 +981,11 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, return -1; } -int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) +int perf_evsel__enable(struct perf_evsel *evsel) { + int nthreads = thread_map__nr(evsel->threads); + int ncpus = cpu_map__nr(evsel->cpus); + return perf_evsel__run_ioctl(evsel, ncpus, nthreads, PERF_EVENT_IOC_ENABLE, 0); diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0e49bd742c63..a721592a3200 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -227,7 +227,7 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, const char *op, const char *filter); int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); -int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); +int perf_evsel__enable(struct perf_evsel *evsel); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); -- cgit v1.2.2 From e98a4cbb01e0ba1110eba5166a425b3eab9b2244 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:41 +0100 Subject: perf evsel: Introduce disable() method Adding perf_evsel__disable function to have complement for perf_evsel__enable function. Both will be used in following patch to factor perf_evlist__(enable|disable). Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 10 ++++++++++ tools/perf/util/evsel.h | 1 + 2 files changed, 11 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 3a9b5068667d..47f033089349 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -991,6 +991,16 @@ int perf_evsel__enable(struct perf_evsel *evsel) 0); } +int perf_evsel__disable(struct perf_evsel *evsel) +{ + int nthreads = thread_map__nr(evsel->threads); + int ncpus = cpu_map__nr(evsel->cpus); + + return perf_evsel__run_ioctl(evsel, ncpus, nthreads, + PERF_EVENT_IOC_DISABLE, + 0); +} + int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) { if (ncpus == 0 || nthreads == 0) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a721592a3200..5ded1fc0341e 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -228,6 +228,7 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel); +int perf_evsel__disable(struct perf_evsel *evsel); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); -- cgit v1.2.2 From 3e27c92081131738fa4d7dd71673aa6e8c24866d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:42 +0100 Subject: perf evlist: Factor perf_evlist__(enable|disable) functions Use perf_evsel__(enable|disable) functions in perf_evlist__(enable|disable) functions in order to centralize ioctl enable/disable calls. This way we eliminate 2 places calling directly ioctl. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d1392194a9a9..d1b6c206bb93 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -336,20 +336,12 @@ static int perf_evlist__nr_threads(struct perf_evlist *evlist, void perf_evlist__disable(struct perf_evlist *evlist) { - int cpu, thread; struct perf_evsel *pos; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads; - for (cpu = 0; cpu < nr_cpus; cpu++) { - evlist__for_each(evlist, pos) { - if (!perf_evsel__is_group_leader(pos) || !pos->fd) - continue; - nr_threads = perf_evlist__nr_threads(evlist, pos); - for (thread = 0; thread < nr_threads; thread++) - ioctl(FD(pos, cpu, thread), - PERF_EVENT_IOC_DISABLE, 0); - } + evlist__for_each(evlist, pos) { + if (!perf_evsel__is_group_leader(pos) || !pos->fd) + continue; + perf_evsel__disable(pos); } evlist->enabled = false; @@ -357,20 +349,12 @@ void perf_evlist__disable(struct perf_evlist *evlist) void perf_evlist__enable(struct perf_evlist *evlist) { - int cpu, thread; struct perf_evsel *pos; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads; - for (cpu = 0; cpu < nr_cpus; cpu++) { - evlist__for_each(evlist, pos) { - if (!perf_evsel__is_group_leader(pos) || !pos->fd) - continue; - nr_threads = perf_evlist__nr_threads(evlist, pos); - for (thread = 0; thread < nr_threads; thread++) - ioctl(FD(pos, cpu, thread), - PERF_EVENT_IOC_ENABLE, 0); - } + evlist__for_each(evlist, pos) { + if (!perf_evsel__is_group_leader(pos) || !pos->fd) + continue; + perf_evsel__enable(pos); } evlist->enabled = true; -- cgit v1.2.2 From cfef25b8daf7e4b49c84e174a904af9d89dc7c46 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 6 Dec 2015 23:07:13 +0000 Subject: perf annotate: ARM support Add basic support to parse ARM assembly. This: * enables perf to correctly show the disassembly, rather than chopping some constants off at the '#' (which is not a comment character on ARM). * allows perf to identify ARM instructions that branch to other parts within the same function, thereby properly annotating them. * allows perf to identify function calls, allowing called functions to be followed in the annotated view. Signed-off-by: Russell King Cc: Peter Zijlstra Cc: Will Deacon Link: http://lkml.kernel.org/n/tip-owp1uj0nmcgfrlppfyeetuyf@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 1dd1949b0e79..b795b6994144 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -65,6 +65,11 @@ static int call__parse(struct ins_operands *ops) name++; +#ifdef __arm__ + if (strchr(name, '+')) + return -1; +#endif + tok = strchr(name, '>'); if (tok == NULL) return -1; @@ -246,7 +251,11 @@ static int mov__parse(struct ins_operands *ops) return -1; target = ++s; +#ifdef __arm__ + comment = strchr(s, ';'); +#else comment = strchr(s, '#'); +#endif if (comment != NULL) s = comment - 1; @@ -354,6 +363,20 @@ static struct ins instructions[] = { { .name = "addq", .ops = &mov_ops, }, { .name = "addw", .ops = &mov_ops, }, { .name = "and", .ops = &mov_ops, }, +#ifdef __arm__ + { .name = "b", .ops = &jump_ops, }, // might also be a call + { .name = "bcc", .ops = &jump_ops, }, + { .name = "bcs", .ops = &jump_ops, }, + { .name = "beq", .ops = &jump_ops, }, + { .name = "bge", .ops = &jump_ops, }, + { .name = "bgt", .ops = &jump_ops, }, + { .name = "bhi", .ops = &jump_ops, }, + { .name = "bl", .ops = &call_ops, }, + { .name = "blt", .ops = &jump_ops, }, + { .name = "bls", .ops = &jump_ops, }, + { .name = "blx", .ops = &call_ops, }, + { .name = "bne", .ops = &jump_ops, }, +#endif { .name = "bts", .ops = &mov_ops, }, { .name = "call", .ops = &call_ops, }, { .name = "callq", .ops = &call_ops, }, -- cgit v1.2.2 From bae32b50ea96ca0f8702ea55e62095e8cc4745e2 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:20 +0900 Subject: perf tools: Fix map_groups__clone to put cloned map Fix map_groups__clone to put cloned map after inserting it to the map_groups. Refcnt debugger shows: ---- ==== [0] ==== Unreclaimed map: 0x2a27ee0 Refcount +1 => 1 at ./perf(map_groups__clone+0x8d) [0x4bb7ed] ./perf(thread__fork+0xbe) [0x4c1f9e] ./perf(machine__process_fork_event+0x216) [0x4b79a6] ./perf(perf_event__synthesize_threads+0x38b) [0x48135b] ./perf(cmd_top+0xdc6) [0x43cb76] ./perf() [0x477223] ./perf(main+0x617) [0x422077] /lib64/libc.so.6(__libc_start_main+0xf0) [0x7ff806af8fe0] ./perf() [0x4221ed] Refcount +1 => 2 at ./perf(map_groups__clone+0x128) [0x4bb888] ./perf(thread__fork+0xbe) [0x4c1f9e] ./perf(machine__process_fork_event+0x216) [0x4b79a6] ./perf(perf_event__synthesize_threads+0x38b) [0x48135b] ./perf(cmd_top+0xdc6) [0x43cb76] ./perf() [0x477223] ./perf(main+0x617) [0x422077] /lib64/libc.so.6(__libc_start_main+0xf0) [0x7ff806af8fe0] ./perf() [0x4221ed] Refcount -1 => 1 at ./perf(map_groups__exit+0x87) [0x4ba757] ./perf(map_groups__put+0x68) [0x4ba9a8] ./perf(thread__put+0x8b) [0x4c1aeb] ./perf(machine__delete_threads+0x81) [0x4b48f1] ./perf(perf_session__delete+0x4f) [0x4be63f] ./perf(cmd_top+0x1094) [0x43ce44] ./perf() [0x477223] ./perf(main+0x617) [0x422077] /lib64/libc.so.6(__libc_start_main+0xf0) [0x7ff806af8fe0] ./perf() [0x4221ed] ---- This shows map_groups__clone get the map twice and put it when map_groups__exit. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021120.10245.95388.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 93d9f1ce3baa..7b1c720976fc 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -742,6 +742,7 @@ int map_groups__clone(struct map_groups *mg, if (new == NULL) goto out_unlock; map_groups__insert(mg, new); + map__put(new); } err = 0; -- cgit v1.2.2 From 17577decb2ddae28f5a449ddb79cf0ed3e2312c5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:29 +0900 Subject: perf hists: Fix hists_evsel to release hists Since hists__init doesn't set the destructor of hists_evsel (which is an extended evsel structure), when hists_evsel is released, the extended part of the hists_evsel is not deleted (note that the hists_evsel object itself is freed). This fixes it to add a destructor for hists__evsel and to set it up. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021129.10245.28710.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6e8e0ee9ec37..565ea3549894 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1567,6 +1567,13 @@ static int hists_evsel__init(struct perf_evsel *evsel) return 0; } +static void hists_evsel__exit(struct perf_evsel *evsel) +{ + struct hists *hists = evsel__hists(evsel); + + hists__delete_entries(hists); +} + /* * XXX We probably need a hists_evsel__exit() to free the hist_entries * stored in the rbtree... @@ -1575,7 +1582,8 @@ static int hists_evsel__init(struct perf_evsel *evsel) int hists__init(void) { int err = perf_evsel__object_config(sizeof(struct hists_evsel), - hists_evsel__init, NULL); + hists_evsel__init, + hists_evsel__exit); if (err) fputs("FATAL ERROR: Couldn't setup hists class\n", stderr); -- cgit v1.2.2 From d91130e90a005876b488b6d52b743149d95b4a59 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:31 +0900 Subject: perf tools: Fix maps__fixup_overlappings to put used maps Since the __map_groups__insert got the given map, we don't need to keep it. So put the maps. Refcnt debugger shows that map_groups__fixup_overlappings() got a map twice but the group released it just once. This pattern usually indicates the leak happens in caller site. ---- ==== [0] ==== Unreclaimed map@0x39d3ae0 Refcount +1 => 1 at ./perf(map_groups__fixup_overlappings+0x335) [0x4c1865] ./perf(thread__insert_map+0x30) [0x4c8e00] ./perf(machine__process_mmap2_event+0x106) [0x4bd876] ./perf() [0x4c378e] ./perf() [0x4c4393] ./perf(perf_session__process_events+0x38a) [0x4c654a] ./perf(cmd_record+0xe24) [0x42fc94] ./perf() [0x47b745] ./perf(main+0x617) [0x422547] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2eca2deaf5] ./perf() [0x4226bd] Refcount +1 => 2 at ./perf(map_groups__fixup_overlappings+0x3c5) [0x4c18f5] ./perf(thread__insert_map+0x30) [0x4c8e00] ./perf(machine__process_mmap2_event+0x106) [0x4bd876] ./perf() [0x4c378e] ./perf() [0x4c4393] ./perf(perf_session__process_events+0x38a) [0x4c654a] ./perf(cmd_record+0xe24) [0x42fc94] ./perf() [0x47b745] ./perf(main+0x617) [0x422547] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2eca2deaf5] ./perf() [0x4226bd] Refcount -1 => 1 at ./perf(map_groups__exit+0x92) [0x4c0962] ./perf(map_groups__put+0x60) [0x4c0bc0] ./perf(thread__put+0x90) [0x4c8a40] ./perf(machine__delete_threads+0x7e) [0x4bad9e] ./perf(perf_session__delete+0x4f) [0x4c499f] ./perf(cmd_record+0xb6d) [0x42f9dd] ./perf() [0x47b745] ./perf(main+0x617) [0x422547] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2eca2deaf5] ./perf() [0x4226bd] ---- Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021131.10245.41485.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/map.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 7b1c720976fc..171b6d10a04b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -691,6 +691,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp __map_groups__insert(pos->groups, before); if (verbose >= 2) map__fprintf(before, fp); + map__put(before); } if (map->end < pos->end) { @@ -705,6 +706,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp __map_groups__insert(pos->groups, after); if (verbose >= 2) map__fprintf(after, fp); + map__put(after); } put_map: map__put(pos); -- cgit v1.2.2 From cc1121ab9687d660cc02f50b1a4974112f87a8e6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:33 +0900 Subject: perf machine: Fix machine.vmlinux_maps to make sure to clear the old one Fix machine.vmlinux_maps to make sure to clear the old one if it is renewal. This can leak the previous maps on the vmlinux_maps because those are just overwritten. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021133.10245.93730.stgit@localhost.localdomain [ Simplified the memset, same end result ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index bfc289c73c22..f5882b8c8db9 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -44,6 +44,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) machine->comm_exec = false; machine->kernel_start = 0; + memset(machine->vmlinux_maps, 0, sizeof(machine->vmlinux_maps)); + machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) return -ENOMEM; @@ -770,6 +772,9 @@ int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) enum map_type type; u64 start = machine__get_running_kernel_start(machine, NULL); + /* In case of renewal the kernel map, destroy previous one */ + machine__destroy_kernel_maps(machine); + for (type = 0; type < MAP__NR_TYPES; ++type) { struct kmap *kmap; struct map *map; -- cgit v1.2.2 From 5191d887681dd34ba3993a438d5746378952885a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:35 +0900 Subject: perf tools: Fix write_numa_topology to put cpu_map instead of free Fix write_numa_topology to put cpu_map instead of free because cpu_map is managed based on refcnt. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021135.10245.79046.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 43838003c1a1..5ac7bdb0dff7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -724,7 +724,7 @@ static int write_numa_topology(int fd, struct perf_header *h __maybe_unused, done: free(buf); fclose(fp); - free(node_map); + cpu_map__put(node_map); return ret; } -- cgit v1.2.2 From de7cf7cadca3a3c32c1f1dbf4593a54f236e2dcf Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:43 -0600 Subject: perf tools: Remove unused pager_use_color variable Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/e540c61b3068761181db6d9b1b3411990bafdb2f.1449548395.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 - tools/perf/util/cache.h | 1 - tools/perf/util/color.c | 2 +- tools/perf/util/environment.c | 8 -------- 4 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 tools/perf/util/environment.c (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 0513dd525d87..62392ab234f8 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -6,7 +6,6 @@ libperf-y += config.o libperf-y += ctype.o libperf-y += db-export.o libperf-y += env.o -libperf-y += environment.o libperf-y += event.o libperf-y += evlist.o libperf-y += evsel.o diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index c861373aaed3..4c2b76499dd5 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -31,7 +31,6 @@ extern const char *perf_config_dirname(const char *, const char *); /* pager.c */ extern void setup_pager(void); extern int pager_in_use(void); -extern int pager_use_color; char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 9b9565416f90..e5fb88bab9e1 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -24,7 +24,7 @@ int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) auto_color: if (stdout_is_tty < 0) stdout_is_tty = isatty(1); - if (stdout_is_tty || (pager_in_use() && pager_use_color)) { + if (stdout_is_tty || pager_in_use()) { char *term = getenv("TERM"); if (term && strcmp(term, "dumb")) return 1; diff --git a/tools/perf/util/environment.c b/tools/perf/util/environment.c deleted file mode 100644 index 7405123692f1..000000000000 --- a/tools/perf/util/environment.c +++ /dev/null @@ -1,8 +0,0 @@ -/* - * We put all the perf config variables in this same object - * file, so that programs can link against the config parser - * without having to link against all the rest of perf. - */ -#include "cache.h" - -int pager_use_color = 1; -- cgit v1.2.2 From 1fe143c5f928e3d117355ce2655bac0eb80c1aa3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:42 -0600 Subject: perf tools: Move term functions out of util.c The term functions are needed by help.c which is going to be moved into a separate library. Move them out of util.c and into their own file. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/9a39c854dd156b55ebda57e427594c9a59dcb40f.1449548395.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 + tools/perf/util/term.c | 35 +++++++++++++++++++++++++++++++++++ tools/perf/util/term.h | 10 ++++++++++ tools/perf/util/util.c | 34 ---------------------------------- tools/perf/util/util.h | 4 +--- 5 files changed, 47 insertions(+), 37 deletions(-) create mode 100644 tools/perf/util/term.c create mode 100644 tools/perf/util/term.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 62392ab234f8..65fef5951c7d 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -86,6 +86,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-pt.o libperf-$(CONFIG_AUXTRACE) += intel-bts.o libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o +libperf-y += term.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o diff --git a/tools/perf/util/term.c b/tools/perf/util/term.c new file mode 100644 index 000000000000..90b47d8aa19c --- /dev/null +++ b/tools/perf/util/term.c @@ -0,0 +1,35 @@ +#include "util.h" + +void get_term_dimensions(struct winsize *ws) +{ + char *s = getenv("LINES"); + + if (s != NULL) { + ws->ws_row = atoi(s); + s = getenv("COLUMNS"); + if (s != NULL) { + ws->ws_col = atoi(s); + if (ws->ws_row && ws->ws_col) + return; + } + } +#ifdef TIOCGWINSZ + if (ioctl(1, TIOCGWINSZ, ws) == 0 && + ws->ws_row && ws->ws_col) + return; +#endif + ws->ws_row = 25; + ws->ws_col = 80; +} + +void set_term_quiet_input(struct termios *old) +{ + struct termios tc; + + tcgetattr(0, old); + tc = *old; + tc.c_lflag &= ~(ICANON | ECHO); + tc.c_cc[VMIN] = 0; + tc.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &tc); +} diff --git a/tools/perf/util/term.h b/tools/perf/util/term.h new file mode 100644 index 000000000000..2c06a61846a1 --- /dev/null +++ b/tools/perf/util/term.h @@ -0,0 +1,10 @@ +#ifndef __PERF_TERM_H +#define __PERF_TERM_H + +struct termios; +struct winsize; + +void get_term_dimensions(struct winsize *ws); +void set_term_quiet_input(struct termios *old); + +#endif /* __PERF_TERM_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 75759aebc7b8..07da970a62a3 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -355,40 +355,6 @@ void sighandler_dump_stack(int sig) exit(sig); } -void get_term_dimensions(struct winsize *ws) -{ - char *s = getenv("LINES"); - - if (s != NULL) { - ws->ws_row = atoi(s); - s = getenv("COLUMNS"); - if (s != NULL) { - ws->ws_col = atoi(s); - if (ws->ws_row && ws->ws_col) - return; - } - } -#ifdef TIOCGWINSZ - if (ioctl(1, TIOCGWINSZ, ws) == 0 && - ws->ws_row && ws->ws_col) - return; -#endif - ws->ws_row = 25; - ws->ws_col = 80; -} - -void set_term_quiet_input(struct termios *old) -{ - struct termios tc; - - tcgetattr(0, old); - tc = *old; - tc.c_lflag &= ~(ICANON | ECHO); - tc.c_cc[VMIN] = 0; - tc.c_cc[VTIME] = 0; - tcsetattr(0, TCSANOW, &tc); -} - int parse_nsec_time(const char *str, u64 *ptime) { u64 time_sec, time_nsec; diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index dcc659017976..150858f3b4f0 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -282,9 +283,6 @@ void sighandler_dump_stack(int sig); extern unsigned int page_size; extern int cacheline_size; -void get_term_dimensions(struct winsize *ws); -void set_term_quiet_input(struct termios *old); - struct parse_tag { char tag; int mult; -- cgit v1.2.2 From 2bdb2c2729d2ba2f2f90b729d04254308096c5a0 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:46 -0600 Subject: perf tools: Save cmdline arguments earlier perf_env__set_cmdline() only saves the arguments the first time it's called. It doesn't need to be called every time the options and suboptions are parsed. Instead it can just be called once. This also has the advantage of making the option parsing code less perf-specific so it can be moved out to a library. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/19b76a5aa1b688bd635bd65d80bbc103a978d75e.1449548395.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/env.c | 9 --------- tools/perf/util/parse-options.c | 2 -- 2 files changed, 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index 6af4f7c36820..7dd5939dea2e 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -25,15 +25,6 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]) { int i; - /* - * If env->cmdline_argv has already been set, do not override it. This allows - * a command to set the cmdline, parse args and then call another - * builtin function that implements a command -- e.g, cmd_kvm calling - * cmd_record. - */ - if (env->cmdline_argv != NULL) - return 0; - /* do not include NULL termination */ env->cmdline_argv = calloc(argc, sizeof(char *)); if (env->cmdline_argv == NULL) diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 9fca09296eb3..d09aff983581 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -501,8 +501,6 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o { struct parse_opt_ctx_t ctx; - perf_env__set_cmdline(&perf_env, argc, argv); - /* build usage string if it's not provided */ if (subcommands && !usagestr[0]) { struct strbuf buf = STRBUF_INIT; -- cgit v1.2.2 From 0a4bb5da957b83ece8b4723c5bac7a5d29fbfb33 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:48 -0600 Subject: perf tools: Move cmd_version() to builtin-version.c Move cmd_version() to its own file so that help.c can be moved to a library. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/e908b1b68f20ab6d8d33941d5571c23110622e60.1449548395.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/help.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index fa1fc4acb8a4..929c93f2c333 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -332,10 +332,3 @@ const char *help_unknown_cmd(const char *cmd) exit(1); } - -int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused, - const char *prefix __maybe_unused) -{ - printf("perf version %s\n", perf_version_string); - return 0; -} -- cgit v1.2.2 From 7ecb48fde39e1d61ab8aff95581dcdfb572bcc28 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:58 +0900 Subject: perf thread_map: Free strlist on constructor error path Signed-off-by: Namhyung Kim Reviewed-by: Josh Poimboeuf Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449716459-23004-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/thread_map.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 6ec3c5ca438f..371fb28fe5b1 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -304,6 +304,7 @@ out: out_free_threads: zfree(&threads); + strlist__delete(slist); goto out; } -- cgit v1.2.2 From 3f86eb6b0771d785099c91354838d3f8d8126630 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:59 +0900 Subject: perf tools: Get rid of exit_browser() from usage_with_options() Since all of its users call before setup_browser(), there's no need to call exit_browser() inside of the function. Signed-off-by: Namhyung Kim Reviewed-by: Josh Poimboeuf Tested-by: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449716459-23004-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index d09aff983581..de3290b47db1 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -766,7 +766,6 @@ int usage_with_options_internal(const char * const *usagestr, void usage_with_options(const char * const *usagestr, const struct option *opts) { - exit_browser(false); usage_with_options_internal(usagestr, opts, 0, NULL); exit(129); } @@ -776,8 +775,6 @@ void usage_with_options_msg(const char * const *usagestr, { va_list ap; - exit_browser(false); - va_start(ap, fmt); strbuf_addv(&error_buf, fmt, ap); va_end(ap); -- cgit v1.2.2 From 61fa0e94ca6ab62db5e095a5528150bf9962196d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 16:53:20 +0900 Subject: perf top: Delete half-processed hist entries when exit After sample processing is done, hist entries are in both of hists->entries and hists->entries_in (or hists->entries_collapsed). So I guess perf report does not have leaks on hists. But for perf top, it's possible to have half-processed entries which are only in hists->entries_in. Eventually they will go to the hists->entries and get freed but they cannot be deleted by current hists__delete_entries(). This patch adds hists__delete_all_entries function to delete those entries. Signed-off-by: Namhyung Kim Tested-and-Acked-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1449734015-9148-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 565ea3549894..56e97f5af598 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -270,6 +270,8 @@ static void hists__delete_entry(struct hists *hists, struct hist_entry *he) if (sort__need_collapse) rb_erase(&he->rb_node_in, &hists->entries_collapsed); + else + rb_erase(&he->rb_node_in, hists->entries_in); --hists->nr_entries; if (!he->filtered) @@ -1567,11 +1569,33 @@ static int hists_evsel__init(struct perf_evsel *evsel) return 0; } +static void hists__delete_remaining_entries(struct rb_root *root) +{ + struct rb_node *node; + struct hist_entry *he; + + while (!RB_EMPTY_ROOT(root)) { + node = rb_first(root); + rb_erase(node, root); + + he = rb_entry(node, struct hist_entry, rb_node_in); + hist_entry__delete(he); + } +} + +static void hists__delete_all_entries(struct hists *hists) +{ + hists__delete_entries(hists); + hists__delete_remaining_entries(&hists->entries_in_array[0]); + hists__delete_remaining_entries(&hists->entries_in_array[1]); + hists__delete_remaining_entries(&hists->entries_collapsed); +} + static void hists_evsel__exit(struct perf_evsel *evsel) { struct hists *hists = evsel__hists(evsel); - hists__delete_entries(hists); + hists__delete_all_entries(hists); } /* -- cgit v1.2.2 From 9d8b172f29ac0e5d1923d348e395e9643625ef7f Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:23 +0900 Subject: perf tools: Make perf_session__register_idle_thread drop the refcount Note that since the thread was already inserted to the session list, it will be released when the session is released. Also, in perf_session__register_idle_thread() failure path, the thread should be put before returning. Refcnt debugger shows that the perf_session__register_idle_thread gets the returned thread, but the caller (__cmd_top) does not put the returned idle thread. ---- ==== [0] ==== Unreclaimed thread@0x24e6240 Refcount +1 => 0 at ./perf(thread__new+0xe5) [0x4c8a75] ./perf(machine__findnew_thread+0x9a) [0x4bbdba] ./perf(perf_session__register_idle_thread+0x28) [0x4c63c8] ./perf(cmd_top+0xd7d) [0x43cf6d] ./perf() [0x47ba35] ./perf(main+0x617) [0x4225b7] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f06027c5af5] ./perf() [0x42272d] Refcount +1 => 1 at ./perf(thread__get+0x2c) [0x4c8bcc] ./perf(machine__findnew_thread+0xee) [0x4bbe0e] ./perf(perf_session__register_idle_thread+0x28) [0x4c63c8] ./perf(cmd_top+0xd7d) [0x43cf6d] ./perf() [0x47ba35] ./perf(main+0x617) [0x4225b7] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f06027c5af5] ./perf() [0x42272d] Refcount +1 => 2 at ./perf(thread__get+0x2c) [0x4c8bcc] ./perf(machine__findnew_thread+0x112) [0x4bbe32] ./perf(perf_session__register_idle_thread+0x28) [0x4c63c8] ./perf(cmd_top+0xd7d) [0x43cf6d] ./perf() [0x47ba35] ./perf(main+0x617) [0x4225b7] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f06027c5af5] ./perf() [0x42272d] ---- Signed-off-by: Masami Hiramatsu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021122.10245.69707.stgit@localhost.localdomain [ Drop the refcount in perf_session__register_idle_thread() ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 11 +++++++---- tools/perf/util/session.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c35ffdd360fe..9774686525b4 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1311,17 +1311,20 @@ struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) return machine__findnew_thread(&session->machines.host, -1, pid); } -struct thread *perf_session__register_idle_thread(struct perf_session *session) +int perf_session__register_idle_thread(struct perf_session *session) { struct thread *thread; + int err = 0; thread = machine__findnew_thread(&session->machines.host, 0, 0); if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); - thread = NULL; + err = -1; } - return thread; + /* machine__findnew_thread() got the thread, so put it */ + thread__put(thread); + return err; } static void perf_session__warn_about_errors(const struct perf_session *session) @@ -1676,7 +1679,7 @@ int perf_session__process_events(struct perf_session *session) u64 size = perf_data_file__size(session->file); int err; - if (perf_session__register_idle_thread(session) == NULL) + if (perf_session__register_idle_thread(session) < 0) return -ENOMEM; if (!perf_data_file__is_pipe(session->file)) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 3e900c0efc73..5f792e35d4c1 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -89,7 +89,7 @@ struct machine *perf_session__findnew_machine(struct perf_session *session, pid_ } struct thread *perf_session__findnew(struct perf_session *session, pid_t pid); -struct thread *perf_session__register_idle_thread(struct perf_session *session); +int perf_session__register_idle_thread(struct perf_session *session); size_t perf_session__fprintf(struct perf_session *session, FILE *fp); -- cgit v1.2.2 From e7a7865cc0da306542db0b9205cb0a467f59e33d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:18 +0900 Subject: perf symbols: Fix dso__load_sym to put dso Fix dso__load_sym to put dso because dsos__add already got it. Refcnt debugger explain the problem: ---- ==== [0] ==== Unreclaimed dso: 0x19dd200 Refcount +1 => 1 at ./perf(dso__new+0x1ff) [0x4a62df] ./perf(dso__load_sym+0xe89) [0x503509] ./perf(dso__load_vmlinux+0xbf) [0x4aa77f] ./perf(dso__load_vmlinux_path+0x8c) [0x4aa8dc] ./perf() [0x50539a] ./perf(convert_perf_probe_events+0xd79) [0x50ad39] ./perf() [0x45600f] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f74dd0efaf5] ./perf() [0x4220a9] Refcount +1 => 2 at ./perf(dso__get+0x34) [0x4a65f4] ./perf(map__new2+0x76) [0x4be216] ./perf(dso__load_sym+0xee1) [0x503561] ./perf(dso__load_vmlinux+0xbf) [0x4aa77f] ./perf(dso__load_vmlinux_path+0x8c) [0x4aa8dc] ./perf() [0x50539a] ./perf(convert_perf_probe_events+0xd79) [0x50ad39] ./perf() [0x45600f] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f74dd0efaf5] ./perf() [0x4220a9] Refcount +1 => 3 at ./perf(dsos__add+0xf3) [0x4a6bc3] ./perf(dso__load_sym+0xfc1) [0x503641] ./perf(dso__load_vmlinux+0xbf) [0x4aa77f] ./perf(dso__load_vmlinux_path+0x8c) [0x4aa8dc] ./perf() [0x50539a] ./perf(convert_perf_probe_events+0xd79) [0x50ad39] ./perf() [0x45600f] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f74dd0efaf5] ./perf() [0x4220a9] Refcount -1 => 2 at ./perf(dso__put+0x2f) [0x4a664f] ./perf(map_groups__exit+0xb9) [0x4bee29] ./perf(machine__delete+0xb0) [0x4b93d0] ./perf(exit_probe_symbol_maps+0x28) [0x506718] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f74dd0efaf5] ./perf() [0x4220a9] Refcount -1 => 1 at ./perf(dso__put+0x2f) [0x4a664f] ./perf(machine__delete+0xfe) [0x4b941e] ./perf(exit_probe_symbol_maps+0x28) [0x506718] ./perf() [0x45628a] ./perf(cmd_probe+0x6c) [0x4566bc] ./perf() [0x47abc5] ./perf(main+0x610) [0x421f90] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f74dd0efaf5] ./perf() [0x4220a9] ---- So, in the dso__load_sym, dso is gotten 3 times, by dso__new, map__new2, and dsos__add. The last 2 is actually released by map_groups and machine__delete correspondingly. However, the first reference by dso__new, is never released. Committer note: Changed the place where the reference count is dropped to: Fix it by dropping it right after creating curr_map, since we know that either that operation failed and we need to drop the dso refcount or that it succeed and we have it referenced via curr_map->dso. Then only drop the curr_map refcount after we call dsos__add() to make sure we hold a reference to it via curr_map->dso. Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021118.10245.49869.stgit@localhost.localdomain Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 53f19968bfa2..562b8ebeae5b 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -1026,8 +1026,8 @@ int dso__load_sym(struct dso *dso, struct map *map, curr_dso->long_name_len = dso->long_name_len; curr_map = map__new2(start, curr_dso, map->type); + dso__put(curr_dso); if (curr_map == NULL) { - dso__put(curr_dso); goto out_elf_end; } if (adjust_kernel_syms) { @@ -1042,9 +1042,14 @@ int dso__load_sym(struct dso *dso, struct map *map, } curr_dso->symtab_type = dso->symtab_type; map_groups__insert(kmaps, curr_map); + /* + * Add it before we drop the referece to curr_map, + * i.e. while we still are sure to have a reference + * to this DSO via curr_map->dso. + */ + dsos__add(&map->groups->machine->dsos, curr_dso); /* kmaps already got it */ map__put(curr_map); - dsos__add(&map->groups->machine->dsos, curr_dso); dso__set_loaded(curr_dso, map->type); } else curr_dso = curr_map->dso; -- cgit v1.2.2 From 26812d466b2633d0c772fe3aca954129f150d3cb Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:39 +0000 Subject: perf data: Add u32_hex data type Add hexadecimal u32 to base data type, which is useful for raw output because raw data is u32 aligned. Signed-off-by: Wang Nan Cc: David S. Miller Cc: Alexei Starovoitov Cc: Brendan Gregg Cc: David S. Miller Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1449541544-67621-12-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-bt.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 5bfc1198ab46..34cd1e4039d3 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -63,6 +63,7 @@ struct ctf_writer { struct bt_ctf_field_type *s32; struct bt_ctf_field_type *u32; struct bt_ctf_field_type *string; + struct bt_ctf_field_type *u32_hex; struct bt_ctf_field_type *u64_hex; }; struct bt_ctf_field_type *array[6]; @@ -982,6 +983,7 @@ do { \ CREATE_INT_TYPE(cw->data.u64, 64, false, false); CREATE_INT_TYPE(cw->data.s32, 32, true, false); CREATE_INT_TYPE(cw->data.u32, 32, false, false); + CREATE_INT_TYPE(cw->data.u32_hex, 32, false, true); CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true); cw->data.string = bt_ctf_field_type_string_create(); -- cgit v1.2.2 From 93b0ba3c60da89043ce2b9f601cd2b3da408903b Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:44 +0000 Subject: perf tools: Clear struct machine during machine__init() There are so many test cases use stack allocated 'struct machine'. Including: test__hists_link test__hists_filter test__mmap_thread_lookup test__thread_mg_share test__hists_output test__hists_cumulate Also, in non-test code (for example, machine__new_host()) there are code use 'malloc()' to alloc struct machine. These are dangerous operations, cause some tests fail or hung in machines__exit(). For example, in machines__exit -> machine__destroy_kernel_maps -> map_groups__remove -> maps__remove -> pthread_rwlock_wrlock a incorrectly initialized lock causes unintended behavior. This patch memset(0) that structure in machine__init() to ensure all fields in 'struct machine' are initialized to zero. Signed-off-by: Wang Nan Cc: Jiri Olsa Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1449541544-67621-17-git-send-email-wangnan0@huawei.com [ Use memset, see 'man bzero' ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index f5882b8c8db9..1407d5107480 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -25,6 +25,7 @@ static void dsos__init(struct dsos *dsos) int machine__init(struct machine *machine, const char *root_dir, pid_t pid) { + memset(machine, 0, sizeof(*machine)); map_groups__init(&machine->kmaps, machine); RB_CLEAR_NODE(&machine->rb_node); dsos__init(&machine->dsos); -- cgit v1.2.2 From 9daddf66a37708ec7182a7058f159166d12c9812 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Dec 2015 16:43:57 -0300 Subject: perf tools: Use same signal handling strategy as 'record' I.e. don't exit with the signal number, instead set the signal handler to the default one and then raise it again. Noticed while trying to dump the stack at segfaults in the 'perf test' forked process used to run each test, that inspects signal info at each test. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-5x5r176wnoqxi5p6id05wv9w@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 07da970a62a3..aff0cfd83662 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -352,7 +352,8 @@ void sighandler_dump_stack(int sig) { psignal(sig, "perf"); dump_stack(); - exit(sig); + signal(sig, SIG_DFL); + raise(sig); } int parse_nsec_time(const char *str, u64 *ptime) -- cgit v1.2.2 From abd828688407eb86044f1bc9e5133c55d7597596 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Dec 2015 19:11:23 -0300 Subject: perf thread: Fix reference count initial state We should always return from thread__new(), the constructor, with the object with a reference count of one, so that: struct thread *thread = thread__new(); thread__put(thread); Will call thread__delete(). If any reference is made to that 'thread' variable, it better use thread__get(thread) to hold a reference. We were returning with thread->refcnt set to zero, fix it and some cases where thread__delete() was being called, which were not a problem because just one reference was being used, now that we set it to 1, use thread__put() instead. Reported-by: Masami Hiramatsu Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-4b9mkuk66to4ecckpmpvqx6s@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/intel-pt.c | 4 ++-- tools/perf/util/machine.c | 19 ++++++++++++------- tools/perf/util/thread.c | 10 ++++++++-- 3 files changed, 22 insertions(+), 11 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 97f963a3dcb9..81a2eb77ba7f 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -1744,7 +1744,7 @@ static void intel_pt_free(struct perf_session *session) auxtrace_heap__free(&pt->heap); intel_pt_free_events(session); session->auxtrace = NULL; - thread__delete(pt->unknown_thread); + thread__put(pt->unknown_thread); free(pt); } @@ -2153,7 +2153,7 @@ int intel_pt_process_auxtrace_info(union perf_event *event, return 0; err_delete_thread: - thread__delete(pt->unknown_thread); + thread__zput(pt->unknown_thread); err_free_queues: intel_pt_log_disable(); auxtrace_queues__free(&pt->queues); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 1407d5107480..ad79297c76c8 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -352,13 +352,18 @@ static void machine__update_thread_pid(struct machine *machine, } th->mg = map_groups__get(leader->mg); - +out_put: + thread__put(leader); return; - out_err: pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid); + goto out_put; } +/* + * Caller must eventually drop thread->refcnt returned with a successfull + * lookup/new thread inserted. + */ static struct thread *____machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid, bool create) @@ -376,7 +381,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, if (th != NULL) { if (th->tid == tid) { machine__update_thread_pid(machine, th, pid); - return th; + return thread__get(th); } machine->last_match = NULL; @@ -389,7 +394,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, if (th->tid == tid) { machine->last_match = th; machine__update_thread_pid(machine, th, pid); - return th; + return thread__get(th); } if (tid < th->tid) @@ -417,7 +422,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine, if (thread__init_map_groups(th, machine)) { rb_erase_init(&th->rb_node, &machine->threads); RB_CLEAR_NODE(&th->rb_node); - thread__delete(th); + thread__put(th); return NULL; } /* @@ -441,7 +446,7 @@ struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, struct thread *th; pthread_rwlock_wrlock(&machine->threads_lock); - th = thread__get(__machine__findnew_thread(machine, pid, tid)); + th = __machine__findnew_thread(machine, pid, tid); pthread_rwlock_unlock(&machine->threads_lock); return th; } @@ -451,7 +456,7 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid, { struct thread *th; pthread_rwlock_rdlock(&machine->threads_lock); - th = thread__get(____machine__findnew_thread(machine, pid, tid, false)); + th = ____machine__findnew_thread(machine, pid, tid, false); pthread_rwlock_unlock(&machine->threads_lock); return th; } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 0a9ae8014729..dfd00c6dad6e 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -19,8 +19,10 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine) thread->mg = map_groups__new(machine); } else { leader = __machine__findnew_thread(machine, pid, pid); - if (leader) + if (leader) { thread->mg = map_groups__get(leader->mg); + thread__put(leader); + } } return thread->mg ? 0 : -1; @@ -53,7 +55,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) goto err_thread; list_add(&comm->list, &thread->comm_list); - atomic_set(&thread->refcnt, 0); + atomic_set(&thread->refcnt, 1); RB_CLEAR_NODE(&thread->rb_node); } @@ -95,6 +97,10 @@ struct thread *thread__get(struct thread *thread) void thread__put(struct thread *thread) { if (thread && atomic_dec_and_test(&thread->refcnt)) { + /* + * Remove it from the dead_threads list, as last reference + * is gone. + */ list_del_init(&thread->node); thread__delete(thread); } -- cgit v1.2.2 From bd0f889536f80630c1c4a414f2de90744d2c87d0 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 11 Dec 2015 16:12:24 -0800 Subject: perf evsel: Disable branch flags/cycles for --callgraph lbr [The kernel patch needed for this is in tip now (b16a5b52eb9 perf/x86: Add option to disable ...) So this user tools patch to make use of it should be merged now] Automatically disable collecting branch flags and cycles with --call-graph lbr. This allows avoiding a bunch of extra MSR reads in the PMI on Skylake. When the kernel doesn't support the new flags they are automatically cleared in the fallback code. v2: Switch to use branch_sample_type instead of sample_type. Adjust description. Fix the fallback logic. Signed-off-by: Andi Kleen Acked-by: Jiri Olsa Link: http://lkml.kernel.org/r/1449879144-29074-1-git-send-email-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 47f033089349..544e4400de13 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -36,6 +36,7 @@ static struct { bool cloexec; bool clockid; bool clockid_wrong; + bool lbr_flags; } perf_missing_features; static clockid_t clockid; @@ -574,7 +575,9 @@ perf_evsel__config_callgraph(struct perf_evsel *evsel, } else { perf_evsel__set_sample_bit(evsel, BRANCH_STACK); attr->branch_sample_type = PERF_SAMPLE_BRANCH_USER | - PERF_SAMPLE_BRANCH_CALL_STACK; + PERF_SAMPLE_BRANCH_CALL_STACK | + PERF_SAMPLE_BRANCH_NO_CYCLES | + PERF_SAMPLE_BRANCH_NO_FLAGS; } } else pr_warning("Cannot use LBR callstack with branch stack. " @@ -1337,6 +1340,9 @@ fallback_missing_features: evsel->attr.mmap2 = 0; if (perf_missing_features.exclude_guest) evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; + if (perf_missing_features.lbr_flags) + evsel->attr.branch_sample_type &= ~(PERF_SAMPLE_BRANCH_NO_FLAGS | + PERF_SAMPLE_BRANCH_NO_CYCLES); retry_sample_id: if (perf_missing_features.sample_id_all) evsel->attr.sample_id_all = 0; @@ -1455,6 +1461,12 @@ try_fallback: } else if (!perf_missing_features.sample_id_all) { perf_missing_features.sample_id_all = true; goto retry_sample_id; + } else if (!perf_missing_features.lbr_flags && + (evsel->attr.branch_sample_type & + (PERF_SAMPLE_BRANCH_NO_CYCLES | + PERF_SAMPLE_BRANCH_NO_FLAGS))) { + perf_missing_features.lbr_flags = true; + goto fallback_missing_features; } out_close: -- cgit v1.2.2 From 32a56bd438ab3023d08874e2770aa0675364b8ab Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:07 -0600 Subject: perf tools: Create pager.h Move the 'pager' function prototypes into a new pager.h so that the pager code can be moved out to a library. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/ba7c316474dd6bfc047e5c6dc4dcab39a982caf5.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cache.h | 5 +---- tools/perf/util/pager.h | 7 +++++++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 tools/perf/util/pager.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 4c2b76499dd5..9ca4a58f160d 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -4,6 +4,7 @@ #include #include "util.h" #include "strbuf.h" +#include "pager.h" #include "../perf.h" #include "../ui/ui.h" @@ -28,10 +29,6 @@ extern int perf_config_bool(const char *, const char *); extern int config_error_nonbool(const char *); extern const char *perf_config_dirname(const char *, const char *); -/* pager.c */ -extern void setup_pager(void); -extern int pager_in_use(void); - char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); diff --git a/tools/perf/util/pager.h b/tools/perf/util/pager.h new file mode 100644 index 000000000000..2794a83b7e85 --- /dev/null +++ b/tools/perf/util/pager.h @@ -0,0 +1,7 @@ +#ifndef __PERF_PAGER_H +#define __PERF_PAGER_H + +extern void setup_pager(void); +extern int pager_in_use(void); + +#endif /* __PERF_PAGER_H */ -- cgit v1.2.2 From a871a775172ac586b76199fd158e2843971bd052 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:08 -0600 Subject: perf tools: Remove check for unused PERF_PAGER_IN_USE PERF_PAGER_IN_USE doesn't seem to be used anywhere, so let's remove it. This will also make it easier to move pager.c into a separate library. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/ed9e8370db9811746dc590544cf48c36dcfb1731.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pager.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 53ef006a951c..7dcbef64b609 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -85,11 +85,5 @@ void setup_pager(void) int pager_in_use(void) { - const char *env; - - if (spawned_pager) - return 1; - - env = getenv("PERF_PAGER_IN_USE"); - return env ? perf_config_bool("PERF_PAGER_IN_USE", env) : 0; + return spawned_pager; } -- cgit v1.2.2 From 5feaac248a46dd5f9876c4ae45c4bbbde5472e90 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:09 -0600 Subject: perf tools: Move help_unknown_cmd() to its own file help_unknown_cmd() is quite perf-specific because it relies on some perf_config*() functions. Move it and its supporting functions out into a separate file so that help.c can be moved to a library. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/562d918bcaaf340c1ae3e47586b3f0ae33b9918b.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 + tools/perf/util/help-unknown-cmd.c | 103 +++++++++++++++++++++++++++++++++++ tools/perf/util/help-unknown-cmd.h | 0 tools/perf/util/help.c | 107 ++----------------------------------- tools/perf/util/help.h | 3 ++ 5 files changed, 110 insertions(+), 104 deletions(-) create mode 100644 tools/perf/util/help-unknown-cmd.c create mode 100644 tools/perf/util/help-unknown-cmd.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 65fef5951c7d..99b3dae57806 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -87,6 +87,7 @@ libperf-$(CONFIG_AUXTRACE) += intel-bts.o libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-y += term.o +libperf-y += help-unknown-cmd.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c new file mode 100644 index 000000000000..a0820f16f511 --- /dev/null +++ b/tools/perf/util/help-unknown-cmd.c @@ -0,0 +1,103 @@ +#include "cache.h" +#include "help.h" +#include "../builtin.h" +#include "levenshtein.h" + +static int autocorrect; +static struct cmdnames aliases; + +static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "help.autocorrect")) + autocorrect = perf_config_int(var,value); + /* Also use aliases for command lookup */ + if (!prefixcmp(var, "alias.")) + add_cmdname(&aliases, var + 6, strlen(var + 6)); + + return perf_default_config(var, value, cb); +} + +static int levenshtein_compare(const void *p1, const void *p2) +{ + const struct cmdname *const *c1 = p1, *const *c2 = p2; + const char *s1 = (*c1)->name, *s2 = (*c2)->name; + int l1 = (*c1)->len; + int l2 = (*c2)->len; + return l1 != l2 ? l1 - l2 : strcmp(s1, s2); +} + +static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) +{ + unsigned int i; + + ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); + + for (i = 0; i < old->cnt; i++) + cmds->names[cmds->cnt++] = old->names[i]; + zfree(&old->names); + old->cnt = 0; +} + +const char *help_unknown_cmd(const char *cmd) +{ + unsigned int i, n = 0, best_similarity = 0; + struct cmdnames main_cmds, other_cmds; + + memset(&main_cmds, 0, sizeof(main_cmds)); + memset(&other_cmds, 0, sizeof(main_cmds)); + memset(&aliases, 0, sizeof(aliases)); + + perf_config(perf_unknown_cmd_config, NULL); + + load_command_list("perf-", &main_cmds, &other_cmds); + + add_cmd_list(&main_cmds, &aliases); + add_cmd_list(&main_cmds, &other_cmds); + qsort(main_cmds.names, main_cmds.cnt, + sizeof(main_cmds.names), cmdname_compare); + uniq(&main_cmds); + + if (main_cmds.cnt) { + /* This reuses cmdname->len for similarity index */ + for (i = 0; i < main_cmds.cnt; ++i) + main_cmds.names[i]->len = + levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); + + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), levenshtein_compare); + + best_similarity = main_cmds.names[0]->len; + n = 1; + while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) + ++n; + } + + if (autocorrect && n == 1) { + const char *assumed = main_cmds.names[0]->name; + + main_cmds.names[0] = NULL; + clean_cmdnames(&main_cmds); + fprintf(stderr, "WARNING: You called a perf program named '%s', " + "which does not exist.\n" + "Continuing under the assumption that you meant '%s'\n", + cmd, assumed); + if (autocorrect > 0) { + fprintf(stderr, "in %0.1f seconds automatically...\n", + (float)autocorrect/10.0); + poll(NULL, 0, autocorrect * 100); + } + return assumed; + } + + fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); + + if (main_cmds.cnt && best_similarity < 6) { + fprintf(stderr, "\nDid you mean %s?\n", + n < 2 ? "this": "one of these"); + + for (i = 0; i < n; i++) + fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); + } + + exit(1); +} diff --git a/tools/perf/util/help-unknown-cmd.h b/tools/perf/util/help-unknown-cmd.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 929c93f2c333..8d74f7d05674 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -1,9 +1,7 @@ #include "cache.h" #include "../builtin.h" #include "exec_cmd.h" -#include "levenshtein.h" #include "help.h" -#include void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) { @@ -17,7 +15,7 @@ void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) cmds->names[cmds->cnt++] = ent; } -static void clean_cmdnames(struct cmdnames *cmds) +void clean_cmdnames(struct cmdnames *cmds) { unsigned int i; @@ -28,14 +26,14 @@ static void clean_cmdnames(struct cmdnames *cmds) cmds->alloc = 0; } -static int cmdname_compare(const void *a_, const void *b_) +int cmdname_compare(const void *a_, const void *b_) { struct cmdname *a = *(struct cmdname **)a_; struct cmdname *b = *(struct cmdname **)b_; return strcmp(a->name, b->name); } -static void uniq(struct cmdnames *cmds) +void uniq(struct cmdnames *cmds) { unsigned int i, j; @@ -233,102 +231,3 @@ int is_in_cmdlist(struct cmdnames *c, const char *s) return 1; return 0; } - -static int autocorrect; -static struct cmdnames aliases; - -static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) -{ - if (!strcmp(var, "help.autocorrect")) - autocorrect = perf_config_int(var,value); - /* Also use aliases for command lookup */ - if (!prefixcmp(var, "alias.")) - add_cmdname(&aliases, var + 6, strlen(var + 6)); - - return perf_default_config(var, value, cb); -} - -static int levenshtein_compare(const void *p1, const void *p2) -{ - const struct cmdname *const *c1 = p1, *const *c2 = p2; - const char *s1 = (*c1)->name, *s2 = (*c2)->name; - int l1 = (*c1)->len; - int l2 = (*c2)->len; - return l1 != l2 ? l1 - l2 : strcmp(s1, s2); -} - -static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) -{ - unsigned int i; - - ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc); - - for (i = 0; i < old->cnt; i++) - cmds->names[cmds->cnt++] = old->names[i]; - zfree(&old->names); - old->cnt = 0; -} - -const char *help_unknown_cmd(const char *cmd) -{ - unsigned int i, n = 0, best_similarity = 0; - struct cmdnames main_cmds, other_cmds; - - memset(&main_cmds, 0, sizeof(main_cmds)); - memset(&other_cmds, 0, sizeof(main_cmds)); - memset(&aliases, 0, sizeof(aliases)); - - perf_config(perf_unknown_cmd_config, NULL); - - load_command_list("perf-", &main_cmds, &other_cmds); - - add_cmd_list(&main_cmds, &aliases); - add_cmd_list(&main_cmds, &other_cmds); - qsort(main_cmds.names, main_cmds.cnt, - sizeof(main_cmds.names), cmdname_compare); - uniq(&main_cmds); - - if (main_cmds.cnt) { - /* This reuses cmdname->len for similarity index */ - for (i = 0; i < main_cmds.cnt; ++i) - main_cmds.names[i]->len = - levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4); - - qsort(main_cmds.names, main_cmds.cnt, - sizeof(*main_cmds.names), levenshtein_compare); - - best_similarity = main_cmds.names[0]->len; - n = 1; - while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len) - ++n; - } - - if (autocorrect && n == 1) { - const char *assumed = main_cmds.names[0]->name; - - main_cmds.names[0] = NULL; - clean_cmdnames(&main_cmds); - fprintf(stderr, "WARNING: You called a perf program named '%s', " - "which does not exist.\n" - "Continuing under the assumption that you meant '%s'\n", - cmd, assumed); - if (autocorrect > 0) { - fprintf(stderr, "in %0.1f seconds automatically...\n", - (float)autocorrect/10.0); - poll(NULL, 0, autocorrect * 100); - } - return assumed; - } - - fprintf(stderr, "perf: '%s' is not a perf-command. See 'perf --help'.\n", cmd); - - if (main_cmds.cnt && best_similarity < 6) { - fprintf(stderr, "\nDid you mean %s?\n", - n < 2 ? "this": "one of these"); - - for (i = 0; i < n; i++) - fprintf(stderr, "\t%s\n", main_cmds.names[i]->name); - } - - exit(1); -} diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 7f5c6dedd714..14851b0e44f5 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h @@ -20,6 +20,9 @@ void load_command_list(const char *prefix, struct cmdnames *main_cmds, struct cmdnames *other_cmds); void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); +void clean_cmdnames(struct cmdnames *cmds); +int cmdname_compare(const void *a, const void *b); +void uniq(struct cmdnames *cmds); /* Here we require that excludes is a sorted list. */ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); int is_in_cmdlist(struct cmdnames *c, const char *s); -- cgit v1.2.2 From 408cf34c176e1832bc2f9f68033a55a765484f93 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:12 -0600 Subject: perf tools: Convert parse-options.c internal functions to static Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/c027b5f47ec1055077f5650edb1c7ad37c191e6c.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.c | 18 +++++++++--------- tools/perf/util/parse-options.h | 9 --------- 2 files changed, 9 insertions(+), 18 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index de3290b47db1..14b2bee81e75 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -360,8 +360,8 @@ static void check_typos(const char *arg, const struct option *options) } } -void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags) +static void parse_options_start(struct parse_opt_ctx_t *ctx, + int argc, const char **argv, int flags) { memset(ctx, 0, sizeof(*ctx)); ctx->argc = argc - 1; @@ -378,9 +378,9 @@ static int usage_with_options_internal(const char * const *, const struct option *, int, struct parse_opt_ctx_t *); -int parse_options_step(struct parse_opt_ctx_t *ctx, - const struct option *options, - const char * const usagestr[]) +static int parse_options_step(struct parse_opt_ctx_t *ctx, + const struct option *options, + const char * const usagestr[]) { int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); int excl_short_opt = 1; @@ -489,7 +489,7 @@ exclusive: return PARSE_OPT_HELP; } -int parse_options_end(struct parse_opt_ctx_t *ctx) +static int parse_options_end(struct parse_opt_ctx_t *ctx) { memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); ctx->out[ctx->cpidx + ctx->argc] = NULL; @@ -717,9 +717,9 @@ static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx return false; } -int usage_with_options_internal(const char * const *usagestr, - const struct option *opts, int full, - struct parse_opt_ctx_t *ctx) +static int usage_with_options_internal(const char * const *usagestr, + const struct option *opts, int full, + struct parse_opt_ctx_t *ctx) { struct option *ordered; diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index a8e407bc251e..dd1236d49903 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -195,15 +195,6 @@ extern int parse_options_usage(const char * const *usagestr, const char *optstr, bool short_opt); -extern void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags); - -extern int parse_options_step(struct parse_opt_ctx_t *ctx, - const struct option *options, - const char * const usagestr[]); - -extern int parse_options_end(struct parse_opt_ctx_t *ctx); - /*----- some often used options -----*/ extern int parse_opt_abbrev_cb(const struct option *, const char *, int); -- cgit v1.2.2 From 48e1cab1ba4db84fbc26379b887ba94a180347fe Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 14 Dec 2015 10:39:22 +0000 Subject: perf tools: Make options always available, even if required libs not linked This patch keeps options of perf builtins same in all conditions. If one option is disabled because of compiling options, users should be notified. Masami suggested another implementation in [1] that, by adding a OPTION_NEXT_DEPENDS option before those options in the 'struct option' array, options parser knows an option is disabled. However, in some cases this array is reordered (options__order()). In addition, in parse-option.c that array is const, so we can't simply merge information in decorator option into the affacted option. This patch chooses a simpler implementation that, introducing a set_option_nobuild() function and two option parsing flags. Builtins with such options should call set_option_nobuild() before option parsing. The complexity of this patch is because we want some of options can be skipped safely. In this case their arguments should also be consumed. Options in 'perf record' and 'perf probe' are fixed in this patch. [1] http://lkml.kernel.org/g/50399556C9727B4D88A595C8584AAB3752627CD4@GSjpTKYDCembx32.service.hitachi.net Test result: Normal case: # ./perf probe --vmlinux /tmp/vmlinux sys_write Added new event: probe:sys_write (on sys_write) You can now use it in all perf tools, such as: perf record -e probe:sys_write -aR sleep 1 Build with NO_DWARF=1: # ./perf probe -L sys_write Error: switch `L' is not available because NO_DWARF=1 Usage: perf probe [] 'PROBEDEF' ['PROBEDEF' ...] or: perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...] or: perf probe [] --del '[GROUP:]EVENT' ... or: perf probe --list [GROUP:]EVENT ... or: perf probe [] --funcs -L, --line Show source code lines. (not built-in because NO_DWARF=1) # ./perf probe -k /tmp/vmlinux sys_write Warning: switch `k' is being ignored because NO_DWARF=1 Added new event: probe:sys_write (on sys_write) You can now use it in all perf tools, such as: perf record -e probe:sys_write -aR sleep 1 # ./perf probe --vmlinux /tmp/vmlinux sys_write Warning: option `vmlinux' is being ignored because NO_DWARF=1 Added new event: [SNIP] # ./perf probe -l Usage: perf probe [] 'PROBEDEF' ['PROBEDEF' ...] or: perf probe [] --add 'PROBEDEF' [--add 'PROBEDEF' ...] ... -k, --vmlinux vmlinux pathname (not built-in because NO_DWARF=1) -L, --line Show source code lines. (not built-in because NO_DWARF=1) ... -V, --vars Show accessible variables on PROBEDEF (not built-in because NO_DWARF=1) --externs Show external variables too (with --vars only) (not built-in because NO_DWARF=1) --no-inlines Don't search inlined functions (not built-in because NO_DWARF=1) --range Show variables location range in scope (with --vars only) (not built-in because NO_DWARF=1) Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Alexei Starovoitov Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1450089563-122430-14-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.c | 118 +++++++++++++++++++++++++++++++++++++--- tools/perf/util/parse-options.h | 5 ++ 2 files changed, 114 insertions(+), 9 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 14b2bee81e75..0ad1384783dd 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -18,20 +18,34 @@ static int opterror(const struct option *opt, const char *reason, int flags) return error("option `%s' %s", opt->long_name, reason); } +static void optwarning(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + warning("switch `%c' %s", opt->short_name, reason); + else if (flags & OPT_UNSET) + warning("option `no-%s' %s", opt->long_name, reason); + else + warning("option `%s' %s", opt->long_name, reason); +} + static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, int flags, const char **arg) { + const char *res; + if (p->opt) { - *arg = p->opt; + res = p->opt; p->opt = NULL; } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || **(p->argv + 1) == '-')) { - *arg = (const char *)opt->defval; + res = (const char *)opt->defval; } else if (p->argc > 1) { p->argc--; - *arg = *++p->argv; + res = *++p->argv; } else return opterror(opt, "requires a value", flags); + if (arg) + *arg = res; return 0; } @@ -91,6 +105,64 @@ static int get_value(struct parse_opt_ctx_t *p, } } + if (opt->flags & PARSE_OPT_NOBUILD) { + char reason[128]; + bool noarg = false; + + err = snprintf(reason, sizeof(reason), + opt->flags & PARSE_OPT_CANSKIP ? + "is being ignored because %s " : + "is not available because %s", + opt->build_opt); + reason[sizeof(reason) - 1] = '\0'; + + if (err < 0) + strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? + "is being ignored" : + "is not available", + sizeof(reason)); + + if (!(opt->flags & PARSE_OPT_CANSKIP)) + return opterror(opt, reason, flags); + + err = 0; + if (unset) + noarg = true; + if (opt->flags & PARSE_OPT_NOARG) + noarg = true; + if (opt->flags & PARSE_OPT_OPTARG && !p->opt) + noarg = true; + + switch (opt->type) { + case OPTION_BOOLEAN: + case OPTION_INCR: + case OPTION_BIT: + case OPTION_SET_UINT: + case OPTION_SET_PTR: + case OPTION_END: + case OPTION_ARGUMENT: + case OPTION_GROUP: + noarg = true; + break; + case OPTION_CALLBACK: + case OPTION_STRING: + case OPTION_INTEGER: + case OPTION_UINTEGER: + case OPTION_LONG: + case OPTION_U64: + default: + break; + } + + if (!noarg) + err = get_arg(p, opt, flags, NULL); + if (err) + return err; + + optwarning(opt, reason, flags); + return 0; + } + switch (opt->type) { case OPTION_BIT: if (unset) @@ -645,6 +717,10 @@ static void print_option_help(const struct option *opts, int full) pad = USAGE_OPTS_WIDTH; } fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + if (opts->flags & PARSE_OPT_NOBUILD) + fprintf(stderr, "%*s(not built-in because %s)\n", + USAGE_OPTS_WIDTH + USAGE_GAP, "", + opts->build_opt); } static int option__cmp(const void *va, const void *vb) @@ -848,15 +924,39 @@ int parse_opt_verbosity_cb(const struct option *opt, return 0; } -void set_option_flag(struct option *opts, int shortopt, const char *longopt, - int flag) +static struct option * +find_option(struct option *opts, int shortopt, const char *longopt) { for (; opts->type != OPTION_END; opts++) { if ((shortopt && opts->short_name == shortopt) || (opts->long_name && longopt && - !strcmp(opts->long_name, longopt))) { - opts->flags |= flag; - break; - } + !strcmp(opts->long_name, longopt))) + return opts; } + return NULL; +} + +void set_option_flag(struct option *opts, int shortopt, const char *longopt, + int flag) +{ + struct option *opt = find_option(opts, shortopt, longopt); + + if (opt) + opt->flags |= flag; + return; +} + +void set_option_nobuild(struct option *opts, int shortopt, + const char *longopt, + const char *build_opt, + bool can_skip) +{ + struct option *opt = find_option(opts, shortopt, longopt); + + if (!opt) + return; + + opt->flags |= PARSE_OPT_NOBUILD; + opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; + opt->build_opt = build_opt; } diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index dd1236d49903..1231960d6148 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -41,6 +41,8 @@ enum parse_opt_option_flags { PARSE_OPT_DISABLED = 32, PARSE_OPT_EXCLUSIVE = 64, PARSE_OPT_NOEMPTY = 128, + PARSE_OPT_NOBUILD = 256, + PARSE_OPT_CANSKIP = 512, }; struct option; @@ -96,6 +98,7 @@ struct option { void *value; const char *argh; const char *help; + const char *build_opt; int flags; parse_opt_cb *callback; @@ -217,4 +220,6 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); extern const char *parse_options_fix_filename(const char *prefix, const char *file); void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); +void set_option_nobuild(struct option *opts, int shortopt, const char *longopt, + const char *build_opt, bool can_skip); #endif /* __PERF_PARSE_OPTIONS_H */ -- cgit v1.2.2 From ce99091730c92bf560712baa0696ea5a461b1fe8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:33 -0600 Subject: perf tools: Move strlcpy() from perf to tools/lib/string.c strlcpy() will be needed by the subcmd library. Move it to the shared tools/lib/string.c file which can be used by other tools. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/71e2804b973bf39ad3d3b9be10f99f2ea630be46.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cache.h | 7 ++----- tools/perf/util/path.c | 18 ------------------ 2 files changed, 2 insertions(+), 23 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 9ca4a58f160d..d723ecb9b959 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -8,6 +8,8 @@ #include "../perf.h" #include "../ui/ui.h" +#include + #define CMD_EXEC_PATH "--exec-path" #define CMD_PERF_DIR "--perf-dir=" #define CMD_WORK_TREE "--work-tree=" @@ -67,9 +69,4 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifndef __UCLIBC__ -/* Matches the libc/libbsd function attribute so we declare this unconditionally: */ -extern size_t strlcpy(char *dest, const char *src, size_t size); -#endif - #endif /* __PERF_CACHE_H */ diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index 5d13cb45b317..3654d964e49d 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,24 +22,6 @@ static const char *get_perf_dir(void) return "."; } -/* - * If libc has strlcpy() then that version will override this - * implementation: - */ -size_t __weak strlcpy(char *dest, const char *src, size_t size) -{ - size_t ret = strlen(src); - - if (size) { - size_t len = (ret >= size) ? size - 1 : ret; - - memcpy(dest, src, len); - dest[len] = '\0'; - } - - return ret; -} - static char *get_pathname(void) { static char pathname_array[4][PATH_MAX]; -- cgit v1.2.2 From 24a88bdd05d5de32f3a56a4dcc5070c97d4a514f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:34 -0600 Subject: perf tools: Document the fact that parse_options*() may exit Generally, calling exit() from a library is bad practice. Eventually these functions might be redesigned so that they don't exit. For now, just document the fact that they do. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/97b1af06cc3b18dd0f49e655d6d659eaa64ecde5.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 1231960d6148..d1544069c7c0 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -152,6 +152,9 @@ struct option { /* parse_options() will filter out the processed options and leave the * non-option argments in argv[]. * Returns the number of arguments left in argv[]. + * + * NOTE: parse_options() and parse_options_subcommand() may call exit() in the + * case of an error (or for 'special' options like --list-cmds or --list-opts). */ extern int parse_options(int argc, const char **argv, const struct option *options, -- cgit v1.2.2 From 096d35585b4fce7d3ee9b8b34314f39f49491ab1 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:35 -0600 Subject: perf tools: Provide subcmd configuration at runtime Create init functions for exec_cmd.c and pager.c. This allows their configuration to be specified at runtime so they can be split out into a separate library which can be used by other programs. Their configuration is stored in a shared subcmd_config struct. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/21f5f6b38da72c985a8dcfa185700d03e7eecd1d.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 2 +- tools/perf/util/cache.h | 1 + tools/perf/util/exec_cmd.c | 23 ++++++++++++++++------- tools/perf/util/exec_cmd.h | 3 +++ tools/perf/util/pager.c | 8 +++++++- tools/perf/util/pager.h | 2 ++ tools/perf/util/parse-options.c | 4 +++- tools/perf/util/subcmd-config.c | 11 +++++++++++ tools/perf/util/subcmd-config.h | 14 ++++++++++++++ 9 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 tools/perf/util/subcmd-config.c create mode 100644 tools/perf/util/subcmd-config.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 99b3dae57806..196beefa16a9 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -88,6 +88,7 @@ libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-y += term.o libperf-y += help-unknown-cmd.o +libperf-y += subcmd-config.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o @@ -113,7 +114,6 @@ libperf-$(CONFIG_ZLIB) += zlib.o libperf-$(CONFIG_LZMA) += lzma.o CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" -CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="BUILD_STR($(prefix_SQ))" $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c $(call rule_mkdir) diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index d723ecb9b959..fc6a745d2ec6 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -21,6 +21,7 @@ #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" #define PERF_TRACEFS_ENVIRONMENT "PERF_TRACEFS_DIR" +#define PERF_PAGER_ENVIRONMENT "PERF_PAGER" typedef int (*config_fn_t)(const char *, const char *, void *); extern int perf_default_config(const char *, const char *, void *); diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 1099e92f5ee1..b935e4ce62a2 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -1,6 +1,7 @@ #include "cache.h" #include "exec_cmd.h" #include "quote.h" +#include "subcmd-config.h" #include @@ -9,15 +10,23 @@ static const char *argv_exec_path; static const char *argv0_path; +void exec_cmd_init(const char *exec_name, const char *prefix, + const char *exec_path, const char *exec_path_env) +{ + subcmd_config.exec_name = exec_name; + subcmd_config.prefix = prefix; + subcmd_config.exec_path = exec_path; + subcmd_config.exec_path_env = exec_path_env; +} + char *system_path(const char *path) { - static const char *prefix = PREFIX; struct strbuf d = STRBUF_INIT; if (is_absolute_path(path)) return strdup(path); - strbuf_addf(&d, "%s/%s", prefix, path); + strbuf_addf(&d, "%s/%s", subcmd_config.prefix, path); path = strbuf_detach(&d, NULL); return (char *)path; } @@ -47,7 +56,7 @@ void perf_set_argv_exec_path(const char *exec_path) /* * Propagate this setting to external programs. */ - setenv(EXEC_PATH_ENVIRONMENT, exec_path, 1); + setenv(subcmd_config.exec_path_env, exec_path, 1); } @@ -59,11 +68,11 @@ char *perf_exec_path(void) if (argv_exec_path) return strdup(argv_exec_path); - env = getenv(EXEC_PATH_ENVIRONMENT); + env = getenv(subcmd_config.exec_path_env); if (env && *env) return strdup(env); - return system_path(PERF_EXEC_PATH); + return system_path(subcmd_config.exec_path); } static void add_path(struct strbuf *out, const char *path) @@ -107,7 +116,7 @@ static const char **prepare_perf_cmd(const char **argv) ; /* just counting */ nargv = malloc(sizeof(*nargv) * (argc + 2)); - nargv[0] = "perf"; + nargv[0] = subcmd_config.exec_name; for (argc = 0; argv[argc]; argc++) nargv[argc + 1] = argv[argc]; nargv[argc + 1] = NULL; @@ -118,7 +127,7 @@ int execv_perf_cmd(const char **argv) { const char **nargv = prepare_perf_cmd(argv); /* execvp() can only ever return if it fails */ - execvp("perf", (char **)nargv); + execvp(subcmd_config.exec_name, (char **)nargv); free(nargv); return -1; diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index 48b4175f1e11..fd4434e48d21 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h @@ -1,6 +1,9 @@ #ifndef __PERF_EXEC_CMD_H #define __PERF_EXEC_CMD_H +extern void exec_cmd_init(const char *exec_name, const char *prefix, + const char *exec_path, const char *exec_path_env); + extern void perf_set_argv_exec_path(const char *exec_path); extern const char *perf_extract_argv0_path(const char *path); extern void setup_path(void); diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index 7dcbef64b609..d5ef62eaa413 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -1,6 +1,7 @@ #include "cache.h" #include "run-command.h" #include "sigchain.h" +#include "subcmd-config.h" /* * This is split up from the rest of git so that we can do @@ -9,6 +10,11 @@ static int spawned_pager; +void pager_init(const char *pager_env) +{ + subcmd_config.pager_env = pager_env; +} + static void pager_preexec(void) { /* @@ -46,7 +52,7 @@ static void wait_for_pager_signal(int signo) void setup_pager(void) { - const char *pager = getenv("PERF_PAGER"); + const char *pager = getenv(subcmd_config.pager_env); if (!isatty(1)) return; diff --git a/tools/perf/util/pager.h b/tools/perf/util/pager.h index 2794a83b7e85..d6a591a4c017 100644 --- a/tools/perf/util/pager.h +++ b/tools/perf/util/pager.h @@ -1,6 +1,8 @@ #ifndef __PERF_PAGER_H #define __PERF_PAGER_H +extern void pager_init(const char *pager_env); + extern void setup_pager(void); extern int pager_in_use(void); diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 0ad1384783dd..da4ba21cad21 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -2,6 +2,7 @@ #include "parse-options.h" #include "cache.h" #include "header.h" +#include "subcmd-config.h" #include #define OPT_SHORT 1 @@ -577,7 +578,8 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o if (subcommands && !usagestr[0]) { struct strbuf buf = STRBUF_INIT; - strbuf_addf(&buf, "perf %s [] {", argv[0]); + strbuf_addf(&buf, "%s %s [] {", + subcmd_config.exec_name, argv[0]); for (int i = 0; subcommands[i]; i++) { if (i) strbuf_addstr(&buf, "|"); diff --git a/tools/perf/util/subcmd-config.c b/tools/perf/util/subcmd-config.c new file mode 100644 index 000000000000..d017c728bd1b --- /dev/null +++ b/tools/perf/util/subcmd-config.c @@ -0,0 +1,11 @@ +#include "subcmd-config.h" + +#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED" + +struct subcmd_config subcmd_config = { + .exec_name = UNDEFINED, + .prefix = UNDEFINED, + .exec_path = UNDEFINED, + .exec_path_env = UNDEFINED, + .pager_env = UNDEFINED, +}; diff --git a/tools/perf/util/subcmd-config.h b/tools/perf/util/subcmd-config.h new file mode 100644 index 000000000000..cc8514030b5c --- /dev/null +++ b/tools/perf/util/subcmd-config.h @@ -0,0 +1,14 @@ +#ifndef __PERF_SUBCMD_CONFIG_H +#define __PERF_SUBCMD_CONFIG_H + +struct subcmd_config { + const char *exec_name; + const char *prefix; + const char *exec_path; + const char *exec_path_env; + const char *pager_env; +}; + +extern struct subcmd_config subcmd_config; + +#endif /* __PERF_SUBCMD_CONFIG_H */ -- cgit v1.2.2 From 901421a5bdf605d24c278825cdd032cd6038bcb8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:36 -0600 Subject: perf tools: Remove subcmd dependencies on strbuf Introduce and use new astrcat() and astrcatf() functions which replace the strbuf functionality for subcmd. For now they duplicate strbuf's die-on-allocation-error policy. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/957d207e1254406fa11fc2e405e75a7e405aad8f.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/exec_cmd.c | 27 +++++++++++++------------- tools/perf/util/help.c | 14 ++++++-------- tools/perf/util/parse-options.c | 42 +++++++++++++++++++++-------------------- tools/perf/util/subcmd-util.h | 24 +++++++++++++++++++++++ 4 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 tools/perf/util/subcmd-util.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index b935e4ce62a2..65d86dcaa984 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -4,6 +4,7 @@ #include "subcmd-config.h" #include +#include "subcmd-util.h" #define MAX_ARGS 32 @@ -21,14 +22,14 @@ void exec_cmd_init(const char *exec_name, const char *prefix, char *system_path(const char *path) { - struct strbuf d = STRBUF_INIT; + char *buf = NULL; if (is_absolute_path(path)) return strdup(path); - strbuf_addf(&d, "%s/%s", subcmd_config.prefix, path); - path = strbuf_detach(&d, NULL); - return (char *)path; + astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); + + return buf; } const char *perf_extract_argv0_path(const char *argv0) @@ -75,22 +76,22 @@ char *perf_exec_path(void) return system_path(subcmd_config.exec_path); } -static void add_path(struct strbuf *out, const char *path) +static void add_path(char **out, const char *path) { if (path && *path) { if (is_absolute_path(path)) - strbuf_addstr(out, path); + astrcat(out, path); else - strbuf_addstr(out, make_nonrelative_path(path)); + astrcat(out, make_nonrelative_path(path)); - strbuf_addch(out, PATH_SEP); + astrcat(out, ":"); } } void setup_path(void) { const char *old_path = getenv("PATH"); - struct strbuf new_path = STRBUF_INIT; + char *new_path = NULL; char *tmp = perf_exec_path(); add_path(&new_path, tmp); @@ -98,13 +99,13 @@ void setup_path(void) free(tmp); if (old_path) - strbuf_addstr(&new_path, old_path); + astrcat(&new_path, old_path); else - strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin"); + astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); - setenv("PATH", new_path.buf, 1); + setenv("PATH", new_path, 1); - strbuf_release(&new_path); + free(new_path); } static const char **prepare_perf_cmd(const char **argv) diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8d74f7d05674..8e5e0ce3870e 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -2,6 +2,7 @@ #include "../builtin.h" #include "exec_cmd.h" #include "help.h" +#include "subcmd-util.h" void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) { @@ -119,8 +120,7 @@ static void list_commands_in_dir(struct cmdnames *cmds, int prefix_len; DIR *dir = opendir(path); struct dirent *de; - struct strbuf buf = STRBUF_INIT; - int len; + char *buf = NULL; if (!dir) return; @@ -128,8 +128,7 @@ static void list_commands_in_dir(struct cmdnames *cmds, prefix = "perf-"; prefix_len = strlen(prefix); - strbuf_addf(&buf, "%s/", path); - len = buf.len; + astrcatf(&buf, "%s/", path); while ((de = readdir(dir)) != NULL) { int entlen; @@ -137,9 +136,8 @@ static void list_commands_in_dir(struct cmdnames *cmds, if (prefixcmp(de->d_name, prefix)) continue; - strbuf_setlen(&buf, len); - strbuf_addstr(&buf, de->d_name); - if (!is_executable(buf.buf)) + astrcat(&buf, de->d_name); + if (!is_executable(buf)) continue; entlen = strlen(de->d_name) - prefix_len; @@ -149,7 +147,7 @@ static void list_commands_in_dir(struct cmdnames *cmds, add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); - strbuf_release(&buf); + free(buf); } void load_command_list(const char *prefix, diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index da4ba21cad21..c1da2a53ed4e 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -1,4 +1,5 @@ #include "util.h" +#include "subcmd-util.h" #include "parse-options.h" #include "cache.h" #include "header.h" @@ -8,7 +9,7 @@ #define OPT_SHORT 1 #define OPT_UNSET 2 -static struct strbuf error_buf = STRBUF_INIT; +char *error_buf; static int opterror(const struct option *opt, const char *reason, int flags) { @@ -576,19 +577,18 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o /* build usage string if it's not provided */ if (subcommands && !usagestr[0]) { - struct strbuf buf = STRBUF_INIT; + char *buf = NULL; + + astrcatf(&buf, "%s %s [] {", subcmd_config.exec_name, argv[0]); - strbuf_addf(&buf, "%s %s [] {", - subcmd_config.exec_name, argv[0]); for (int i = 0; subcommands[i]; i++) { if (i) - strbuf_addstr(&buf, "|"); - strbuf_addstr(&buf, subcommands[i]); + astrcat(&buf, "|"); + astrcat(&buf, subcommands[i]); } - strbuf_addstr(&buf, "}"); + astrcat(&buf, "}"); - usagestr[0] = strdup(buf.buf); - strbuf_release(&buf); + usagestr[0] = buf; } parse_options_start(&ctx, argc, argv, flags); @@ -613,13 +613,11 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o putchar('\n'); exit(130); default: /* PARSE_OPT_UNKNOWN */ - if (ctx.argv[0][1] == '-') { - strbuf_addf(&error_buf, "unknown option `%s'", - ctx.argv[0] + 2); - } else { - strbuf_addf(&error_buf, "unknown switch `%c'", - *ctx.opt); - } + if (ctx.argv[0][1] == '-') + astrcatf(&error_buf, "unknown option `%s'", + ctx.argv[0] + 2); + else + astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); usage_with_options(usagestr, options); } @@ -806,9 +804,9 @@ static int usage_with_options_internal(const char * const *usagestr, setup_pager(); - if (strbuf_avail(&error_buf)) { - fprintf(stderr, " Error: %s\n", error_buf.buf); - strbuf_release(&error_buf); + if (error_buf) { + fprintf(stderr, " Error: %s\n", error_buf); + zfree(&error_buf); } fprintf(stderr, "\n Usage: %s\n", *usagestr++); @@ -852,11 +850,15 @@ void usage_with_options_msg(const char * const *usagestr, const struct option *opts, const char *fmt, ...) { va_list ap; + char *tmp = error_buf; va_start(ap, fmt); - strbuf_addv(&error_buf, fmt, ap); + if (vasprintf(&error_buf, fmt, ap) == -1) + die("vasprintf failed"); va_end(ap); + free(tmp); + usage_with_options_internal(usagestr, opts, 0, NULL); exit(129); } diff --git a/tools/perf/util/subcmd-util.h b/tools/perf/util/subcmd-util.h new file mode 100644 index 000000000000..98fb9f9270eb --- /dev/null +++ b/tools/perf/util/subcmd-util.h @@ -0,0 +1,24 @@ +#ifndef __PERF_SUBCMD_UTIL_H +#define __PERF_SUBCMD_UTIL_H + +#include + +#define astrcatf(out, fmt, ...) \ +({ \ + char *tmp = *(out); \ + if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \ + die("asprintf failed"); \ + free(tmp); \ +}) + +static inline void astrcat(char **out, const char *add) +{ + char *tmp = *out; + + if (asprintf(out, "%s%s", tmp ?: "", add) == -1) + die("asprintf failed"); + + free(tmp); +} + +#endif /* __PERF_SUBCMD_UTIL_H */ -- cgit v1.2.2 From 46113a54be53aea50a4f5926b87e86e2e66c4266 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:37 -0600 Subject: perf tools: Remove 'perf' from subcmd function and variable names In preparation for moving exec_cmd.c and run-command.c out of perf and into a library, remove 'perf' from all the symbol names. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/bc3ee82b40b8f396b644fa49e0f7260ce442635b.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/exec_cmd.c | 20 ++++++++++---------- tools/perf/util/exec_cmd.h | 12 ++++++------ tools/perf/util/help.c | 4 ++-- tools/perf/util/run-command.c | 6 +++--- tools/perf/util/run-command.h | 4 ++-- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 65d86dcaa984..701111ac7699 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -32,7 +32,7 @@ char *system_path(const char *path) return buf; } -const char *perf_extract_argv0_path(const char *argv0) +const char *extract_argv0_path(const char *argv0) { const char *slash; @@ -51,7 +51,7 @@ const char *perf_extract_argv0_path(const char *argv0) return argv0; } -void perf_set_argv_exec_path(const char *exec_path) +void set_argv_exec_path(const char *exec_path) { argv_exec_path = exec_path; /* @@ -61,8 +61,8 @@ void perf_set_argv_exec_path(const char *exec_path) } -/* Returns the highest-priority, location to look for perf programs. */ -char *perf_exec_path(void) +/* Returns the highest-priority location to look for subprograms. */ +char *get_argv_exec_path(void) { char *env; @@ -92,7 +92,7 @@ void setup_path(void) { const char *old_path = getenv("PATH"); char *new_path = NULL; - char *tmp = perf_exec_path(); + char *tmp = get_argv_exec_path(); add_path(&new_path, tmp); add_path(&new_path, argv0_path); @@ -108,7 +108,7 @@ void setup_path(void) free(new_path); } -static const char **prepare_perf_cmd(const char **argv) +static const char **prepare_exec_cmd(const char **argv) { int argc; const char **nargv; @@ -124,8 +124,8 @@ static const char **prepare_perf_cmd(const char **argv) return nargv; } -int execv_perf_cmd(const char **argv) { - const char **nargv = prepare_perf_cmd(argv); +int execv_cmd(const char **argv) { + const char **nargv = prepare_exec_cmd(argv); /* execvp() can only ever return if it fails */ execvp(subcmd_config.exec_name, (char **)nargv); @@ -135,7 +135,7 @@ int execv_perf_cmd(const char **argv) { } -int execl_perf_cmd(const char *cmd,...) +int execl_cmd(const char *cmd,...) { int argc; const char *argv[MAX_ARGS + 1]; @@ -155,5 +155,5 @@ int execl_perf_cmd(const char *cmd,...) return error("too many args to run %s", cmd); argv[argc] = NULL; - return execv_perf_cmd(argv); + return execv_cmd(argv); } diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index fd4434e48d21..f1bd3436ad5f 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h @@ -4,13 +4,13 @@ extern void exec_cmd_init(const char *exec_name, const char *prefix, const char *exec_path, const char *exec_path_env); -extern void perf_set_argv_exec_path(const char *exec_path); -extern const char *perf_extract_argv0_path(const char *path); +extern void set_argv_exec_path(const char *exec_path); +extern const char *extract_argv0_path(const char *path); extern void setup_path(void); -extern int execv_perf_cmd(const char **argv); /* NULL terminated */ -extern int execl_perf_cmd(const char *cmd, ...); -/* perf_exec_path and system_path return malloc'd string, caller must free it */ -extern char *perf_exec_path(void); +extern int execv_cmd(const char **argv); /* NULL terminated */ +extern int execl_cmd(const char *cmd, ...); +/* get_argv_exec_path and system_path return malloc'd string, caller must free it */ +extern char *get_argv_exec_path(void); extern char *system_path(const char *path); #endif /* __PERF_EXEC_CMD_H */ diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8e5e0ce3870e..303a347ee234 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -155,7 +155,7 @@ void load_command_list(const char *prefix, struct cmdnames *other_cmds) { const char *env_path = getenv("PATH"); - char *exec_path = perf_exec_path(); + char *exec_path = get_argv_exec_path(); if (exec_path) { list_commands_in_dir(main_cmds, exec_path, prefix); @@ -200,7 +200,7 @@ void list_commands(const char *title, struct cmdnames *main_cmds, longest = other_cmds->names[i]->len; if (main_cmds->cnt) { - char *exec_path = perf_exec_path(); + char *exec_path = get_argv_exec_path(); printf("available %s in '%s'\n", title, exec_path); printf("----------------"); mput_char('-', strlen(title) + strlen(exec_path)); diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index 34622b53e733..910c0f6479f4 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c @@ -112,8 +112,8 @@ int start_command(struct child_process *cmd) } if (cmd->preexec_cb) cmd->preexec_cb(); - if (cmd->perf_cmd) { - execv_perf_cmd(cmd->argv); + if (cmd->exec_cmd) { + execv_cmd(cmd->argv); } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } @@ -207,7 +207,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd, memset(cmd, 0, sizeof(*cmd)); cmd->argv = argv; cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd->perf_cmd = opt & RUN_PERF_CMD ? 1 : 0; + cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; } diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h index 1ef264d5069c..cf7d655ee2a3 100644 --- a/tools/perf/util/run-command.h +++ b/tools/perf/util/run-command.h @@ -41,7 +41,7 @@ struct child_process { unsigned no_stdin:1; unsigned no_stdout:1; unsigned no_stderr:1; - unsigned perf_cmd:1; /* if this is to be perf sub-command */ + unsigned exec_cmd:1; /* if this is to be external sub-command */ unsigned stdout_to_stderr:1; void (*preexec_cb)(void); }; @@ -51,7 +51,7 @@ int finish_command(struct child_process *); int run_command(struct child_process *); #define RUN_COMMAND_NO_STDIN 1 -#define RUN_PERF_CMD 2 /*If this is to be perf sub-command */ +#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */ #define RUN_COMMAND_STDOUT_TO_STDERR 4 int run_command_v_opt(const char **argv, int opt); -- cgit v1.2.2 From 2f4ce5ec1d447beb42143a9653716a2ab025161e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:38 -0600 Subject: perf tools: Finalize subcmd independence For the files that will be moved to the subcmd library, remove all their perf-specific includes and duplicate any needed functionality. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/6e12946f0f26ce4d543d34db68d9dae3c8551cb9.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/exec_cmd.c | 64 ++++++++++++++++++++++++++++++++---- tools/perf/util/help.c | 47 +++++++++++++++++++++++--- tools/perf/util/help.h | 4 ++- tools/perf/util/pager.c | 7 +++- tools/perf/util/parse-options.c | 73 +++++++++++++++++++++++++---------------- tools/perf/util/parse-options.h | 2 +- tools/perf/util/run-command.c | 16 ++++++--- tools/perf/util/run-command.h | 2 ++ tools/perf/util/sigchain.c | 3 +- tools/perf/util/subcmd-util.h | 67 +++++++++++++++++++++++++++++++++++++ tools/perf/util/util.h | 14 -------- 11 files changed, 237 insertions(+), 62 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 701111ac7699..e7f9ed7943e3 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c @@ -1,12 +1,17 @@ -#include "cache.h" -#include "exec_cmd.h" -#include "quote.h" -#include "subcmd-config.h" - +#include +#include +#include +#include +#include #include +#include +#include #include "subcmd-util.h" +#include "exec_cmd.h" +#include "subcmd-config.h" #define MAX_ARGS 32 +#define PATH_MAX 4096 static const char *argv_exec_path; static const char *argv0_path; @@ -20,6 +25,49 @@ void exec_cmd_init(const char *exec_name, const char *prefix, subcmd_config.exec_path_env = exec_path_env; } +#define is_dir_sep(c) ((c) == '/') + +static int is_absolute_path(const char *path) +{ + return path[0] == '/'; +} + +static const char *get_pwd_cwd(void) +{ + static char cwd[PATH_MAX + 1]; + char *pwd; + struct stat cwd_stat, pwd_stat; + if (getcwd(cwd, PATH_MAX) == NULL) + return NULL; + pwd = getenv("PWD"); + if (pwd && strcmp(pwd, cwd)) { + stat(cwd, &cwd_stat); + if (!stat(pwd, &pwd_stat) && + pwd_stat.st_dev == cwd_stat.st_dev && + pwd_stat.st_ino == cwd_stat.st_ino) { + strlcpy(cwd, pwd, PATH_MAX); + } + } + return cwd; +} + +static const char *make_nonrelative_path(const char *path) +{ + static char buf[PATH_MAX + 1]; + + if (is_absolute_path(path)) { + if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } else { + const char *cwd = get_pwd_cwd(); + if (!cwd) + die("Cannot determine the current working directory"); + if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) + die("Too long path: %.*s", 60, path); + } + return buf; +} + char *system_path(const char *path) { char *buf = NULL; @@ -151,8 +199,10 @@ int execl_cmd(const char *cmd,...) break; } va_end(param); - if (MAX_ARGS <= argc) - return error("too many args to run %s", cmd); + if (MAX_ARGS <= argc) { + fprintf(stderr, " Error: too many args to run %s\n", cmd); + return -1; + } argv[argc] = NULL; return execv_cmd(argv); diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 303a347ee234..8169480066c6 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -1,8 +1,15 @@ -#include "cache.h" -#include "../builtin.h" -#include "exec_cmd.h" -#include "help.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "subcmd-util.h" +#include "help.h" +#include "exec_cmd.h" void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) { @@ -70,6 +77,28 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) cmds->cnt = cj; } +static void get_term_dimensions(struct winsize *ws) +{ + char *s = getenv("LINES"); + + if (s != NULL) { + ws->ws_row = atoi(s); + s = getenv("COLUMNS"); + if (s != NULL) { + ws->ws_col = atoi(s); + if (ws->ws_row && ws->ws_col) + return; + } + } +#ifdef TIOCGWINSZ + if (ioctl(1, TIOCGWINSZ, ws) == 0 && + ws->ws_row && ws->ws_col) + return; +#endif + ws->ws_row = 25; + ws->ws_col = 80; +} + static void pretty_print_string_list(struct cmdnames *cmds, int longest) { int cols = 1, rows; @@ -113,6 +142,14 @@ static int is_executable(const char *name) return st.st_mode & S_IXUSR; } +static int has_extension(const char *filename, const char *ext) +{ + size_t len = strlen(filename); + size_t extlen = strlen(ext); + + return len > extlen && !memcmp(filename + len - extlen, ext, extlen); +} + static void list_commands_in_dir(struct cmdnames *cmds, const char *path, const char *prefix) @@ -168,7 +205,7 @@ void load_command_list(const char *prefix, char *paths, *path, *colon; path = paths = strdup(env_path); while (1) { - if ((colon = strchr(path, PATH_SEP))) + if ((colon = strchr(path, ':'))) *colon = 0; if (!exec_path || strcmp(path, exec_path)) list_commands_in_dir(other_cmds, path, prefix); diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 14851b0e44f5..096c8bc45cd7 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h @@ -1,12 +1,14 @@ #ifndef __PERF_HELP_H #define __PERF_HELP_H +#include + struct cmdnames { size_t alloc; size_t cnt; struct cmdname { size_t len; /* also used for similarity index in help.c */ - char name[FLEX_ARRAY]; + char name[]; } **names; }; diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c index d5ef62eaa413..d50f3b58606b 100644 --- a/tools/perf/util/pager.c +++ b/tools/perf/util/pager.c @@ -1,4 +1,9 @@ -#include "cache.h" +#include +#include +#include +#include +#include +#include "pager.h" #include "run-command.h" #include "sigchain.h" #include "subcmd-config.h" diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index c1da2a53ed4e..981bb4481fd5 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -1,10 +1,14 @@ -#include "util.h" +#include +#include +#include +#include +#include +#include +#include #include "subcmd-util.h" #include "parse-options.h" -#include "cache.h" -#include "header.h" #include "subcmd-config.h" -#include +#include "pager.h" #define OPT_SHORT 1 #define OPT_UNSET 2 @@ -14,20 +18,29 @@ char *error_buf; static int opterror(const struct option *opt, const char *reason, int flags) { if (flags & OPT_SHORT) - return error("switch `%c' %s", opt->short_name, reason); - if (flags & OPT_UNSET) - return error("option `no-%s' %s", opt->long_name, reason); - return error("option `%s' %s", opt->long_name, reason); + fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); + else if (flags & OPT_UNSET) + fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); + else + fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); + + return -1; +} + +static const char *skip_prefix(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; } static void optwarning(const struct option *opt, const char *reason, int flags) { if (flags & OPT_SHORT) - warning("switch `%c' %s", opt->short_name, reason); + fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); else if (flags & OPT_UNSET) - warning("option `no-%s' %s", opt->long_name, reason); + fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); else - warning("option `%s' %s", opt->long_name, reason); + fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); } static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, @@ -71,11 +84,11 @@ static int get_value(struct parse_opt_ctx_t *p, if (((flags & OPT_SHORT) && p->excl_opt->short_name) || p->excl_opt->long_name == NULL) { - scnprintf(msg, sizeof(msg), "cannot be used with switch `%c'", - p->excl_opt->short_name); + snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", + p->excl_opt->short_name); } else { - scnprintf(msg, sizeof(msg), "cannot be used with %s", - p->excl_opt->long_name); + snprintf(msg, sizeof(msg), "cannot be used with %s", + p->excl_opt->long_name); } opterror(opt, msg, flags); return -3; @@ -401,14 +414,16 @@ match: return get_value(p, options, flags); } - if (ambiguous_option) - return error("Ambiguous option: %s " - "(could be --%s%s or --%s%s)", - arg, - (ambiguous_flags & OPT_UNSET) ? "no-" : "", - ambiguous_option->long_name, - (abbrev_flags & OPT_UNSET) ? "no-" : "", - abbrev_option->long_name); + if (ambiguous_option) { + fprintf(stderr, + " Error: Ambiguous option: %s (could be --%s%s or --%s%s)", + arg, + (ambiguous_flags & OPT_UNSET) ? "no-" : "", + ambiguous_option->long_name, + (abbrev_flags & OPT_UNSET) ? "no-" : "", + abbrev_option->long_name); + return -1; + } if (abbrev_option) return get_value(p, abbrev_option, abbrev_flags); return -2; @@ -420,7 +435,7 @@ static void check_typos(const char *arg, const struct option *options) return; if (!prefixcmp(arg, "no-")) { - error ("did you mean `--%s` (with two dashes ?)", arg); + fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); exit(129); } @@ -428,7 +443,7 @@ static void check_typos(const char *arg, const struct option *options) if (!options->long_name) continue; if (!prefixcmp(options->long_name, arg)) { - error ("did you mean `--%s` (with two dashes ?)", arg); + fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); exit(129); } } @@ -746,16 +761,18 @@ static int option__cmp(const void *va, const void *vb) static struct option *options__order(const struct option *opts) { - int nr_opts = 0; + int nr_opts = 0, len; const struct option *o = opts; struct option *ordered; for (o = opts; o->type != OPTION_END; o++) ++nr_opts; - ordered = memdup(opts, sizeof(*o) * (nr_opts + 1)); - if (ordered == NULL) + len = sizeof(*o) * (nr_opts + 1); + ordered = malloc(len); + if (!ordered) goto out; + memcpy(ordered, opts, len); qsort(ordered, nr_opts, sizeof(*o), option__cmp); out: diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index d1544069c7c0..dec893f10477 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -1,8 +1,8 @@ #ifndef __PERF_PARSE_OPTIONS_H #define __PERF_PARSE_OPTIONS_H -#include #include +#include enum parse_opt_type { /* special types */ diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index 910c0f6479f4..fed37d6ae070 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c @@ -1,7 +1,15 @@ -#include "cache.h" +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" #include "run-command.h" #include "exec_cmd.h" -#include "debug.h" + +#define STRERR_BUFSIZE 128 static inline void close_pair(int fd[2]) { @@ -164,8 +172,8 @@ static int wait_or_whine(pid_t pid) if (waiting < 0) { if (errno == EINTR) continue; - error("waitpid failed (%s)", - strerror_r(errno, sbuf, sizeof(sbuf))); + fprintf(stderr, " Error: waitpid failed (%s)", + strerror_r(errno, sbuf, sizeof(sbuf))); return -ERR_RUN_COMMAND_WAITPID; } if (waiting != pid) diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h index cf7d655ee2a3..4a55393a6547 100644 --- a/tools/perf/util/run-command.h +++ b/tools/perf/util/run-command.h @@ -1,6 +1,8 @@ #ifndef __PERF_RUN_COMMAND_H #define __PERF_RUN_COMMAND_H +#include + enum { ERR_RUN_COMMAND_FORK = 10000, ERR_RUN_COMMAND_EXEC, diff --git a/tools/perf/util/sigchain.c b/tools/perf/util/sigchain.c index ba785e9b1841..3537c348a18e 100644 --- a/tools/perf/util/sigchain.c +++ b/tools/perf/util/sigchain.c @@ -1,5 +1,6 @@ +#include +#include "subcmd-util.h" #include "sigchain.h" -#include "cache.h" #define SIGCHAIN_MAX_SIGNALS 32 diff --git a/tools/perf/util/subcmd-util.h b/tools/perf/util/subcmd-util.h index 98fb9f9270eb..321aeb11a381 100644 --- a/tools/perf/util/subcmd-util.h +++ b/tools/perf/util/subcmd-util.h @@ -1,8 +1,66 @@ #ifndef __PERF_SUBCMD_UTIL_H #define __PERF_SUBCMD_UTIL_H +#include +#include #include +#define NORETURN __attribute__((__noreturn__)) + +static inline void report(const char *prefix, const char *err, va_list params) +{ + char msg[1024]; + vsnprintf(msg, sizeof(msg), err, params); + fprintf(stderr, " %s%s\n", prefix, msg); +} + +static NORETURN inline void die(const char *err, ...) +{ + va_list params; + + va_start(params, err); + report(" Fatal: ", err, params); + exit(128); + va_end(params); +} + +#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) + +#define alloc_nr(x) (((x)+16)*3/2) + +/* + * Realloc the buffer pointed at by variable 'x' so that it can hold + * at least 'nr' entries; the number of entries currently allocated + * is 'alloc', using the standard growing factor alloc_nr() macro. + * + * DO NOT USE any expression with side-effect for 'x' or 'alloc'. + */ +#define ALLOC_GROW(x, nr, alloc) \ + do { \ + if ((nr) > alloc) { \ + if (alloc_nr(alloc) < (nr)) \ + alloc = (nr); \ + else \ + alloc = alloc_nr(alloc); \ + x = xrealloc((x), alloc * sizeof(*(x))); \ + } \ + } while(0) + +static inline void *xrealloc(void *ptr, size_t size) +{ + void *ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) { + ret = realloc(ptr, size); + if (!ret && !size) + ret = realloc(ptr, 1); + if (!ret) + die("Out of memory, realloc failed"); + } + return ret; +} + #define astrcatf(out, fmt, ...) \ ({ \ char *tmp = *(out); \ @@ -21,4 +79,13 @@ static inline void astrcat(char **out, const char *add) free(tmp); } +static inline int prefixcmp(const char *str, const char *prefix) +{ + for (; ; str++, prefix++) + if (!*prefix) + return 0; + else if (*str != *prefix) + return (unsigned char)*prefix - (unsigned char)*str; +} + #endif /* __PERF_SUBCMD_UTIL_H */ diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 150858f3b4f0..4b519c59bdc3 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -151,12 +151,6 @@ extern void set_warning_routine(void (*routine)(const char *err, va_list params) extern int prefixcmp(const char *str, const char *prefix); extern void set_buildid_dir(const char *dir); -static inline const char *skip_prefix(const char *str, const char *prefix) -{ - size_t len = strlen(prefix); - return strncmp(str, prefix, len) ? NULL : str + len; -} - #ifdef __GLIBC_PREREQ #if __GLIBC_PREREQ(2, 1) #define HAVE_STRCHRNUL @@ -187,14 +181,6 @@ static inline void *zalloc(size_t size) #define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) -static inline int has_extension(const char *filename, const char *ext) -{ - size_t len = strlen(filename); - size_t extlen = strlen(ext); - - return len > extlen && !memcmp(filename + len - extlen, ext, extlen); -} - /* Sane ctype - no locale, and works with signed chars */ #undef isascii #undef isspace -- cgit v1.2.2 From 4b6ab94eabe4f55371cff4569750bb3996c55db6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:39 -0600 Subject: perf subcmd: Create subcmd library Move the subcommand-related files from perf to a new library named libsubcmd.a. Since we're moving files anyway, go ahead and rename 'exec_cmd.*' to 'exec-cmd.*' to be consistent with the naming of all the other files. Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/c0a838d4c878ab17fee50998811612b2281355c1.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 7 - tools/perf/util/auxtrace.c | 2 +- tools/perf/util/cache.h | 2 +- tools/perf/util/cgroup.c | 2 +- tools/perf/util/config.c | 2 +- tools/perf/util/evlist.c | 2 +- tools/perf/util/exec_cmd.c | 209 ------- tools/perf/util/exec_cmd.h | 16 - tools/perf/util/help-unknown-cmd.c | 2 +- tools/perf/util/help.c | 268 --------- tools/perf/util/help.h | 34 -- tools/perf/util/pager.c | 100 ---- tools/perf/util/pager.h | 9 - tools/perf/util/parse-branch-options.c | 2 +- tools/perf/util/parse-events.c | 4 +- tools/perf/util/parse-options.c | 983 --------------------------------- tools/perf/util/parse-options.h | 228 -------- tools/perf/util/parse-regs-options.c | 2 +- tools/perf/util/run-command.c | 227 -------- tools/perf/util/run-command.h | 60 -- tools/perf/util/sigchain.c | 53 -- tools/perf/util/sigchain.h | 10 - tools/perf/util/sort.h | 2 +- tools/perf/util/subcmd-config.c | 11 - tools/perf/util/subcmd-config.h | 14 - tools/perf/util/subcmd-util.h | 91 --- 26 files changed, 11 insertions(+), 2331 deletions(-) delete mode 100644 tools/perf/util/exec_cmd.c delete mode 100644 tools/perf/util/exec_cmd.h delete mode 100644 tools/perf/util/help.c delete mode 100644 tools/perf/util/help.h delete mode 100644 tools/perf/util/pager.c delete mode 100644 tools/perf/util/pager.h delete mode 100644 tools/perf/util/parse-options.c delete mode 100644 tools/perf/util/parse-options.h delete mode 100644 tools/perf/util/run-command.c delete mode 100644 tools/perf/util/run-command.h delete mode 100644 tools/perf/util/sigchain.c delete mode 100644 tools/perf/util/sigchain.h delete mode 100644 tools/perf/util/subcmd-config.c delete mode 100644 tools/perf/util/subcmd-config.h delete mode 100644 tools/perf/util/subcmd-util.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 196beefa16a9..94b1099f2c22 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -9,13 +9,10 @@ libperf-y += env.o libperf-y += event.o libperf-y += evlist.o libperf-y += evsel.o -libperf-y += exec_cmd.o libperf-y += find_next_bit.o -libperf-y += help.o libperf-y += kallsyms.o libperf-y += levenshtein.o libperf-y += llvm-utils.o -libperf-y += parse-options.o libperf-y += parse-events.o libperf-y += perf_regs.o libperf-y += path.o @@ -23,7 +20,6 @@ libperf-y += rbtree.o libperf-y += libstring.o libperf-y += bitmap.o libperf-y += hweight.o -libperf-y += run-command.o libperf-y += quote.o libperf-y += strbuf.o libperf-y += string.o @@ -32,11 +28,9 @@ libperf-y += strfilter.o libperf-y += top.o libperf-y += usage.o libperf-y += wrapper.o -libperf-y += sigchain.o libperf-y += dso.o libperf-y += symbol.o libperf-y += color.o -libperf-y += pager.o libperf-y += header.o libperf-y += callchain.o libperf-y += values.o @@ -88,7 +82,6 @@ libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-y += term.o libperf-y += help-unknown-cmd.o -libperf-y += subcmd-config.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 7f10430af39c..360fda01f3b0 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -45,7 +45,7 @@ #include "event.h" #include "session.h" #include "debug.h" -#include "parse-options.h" +#include #include "intel-pt.h" #include "intel-bts.h" diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index fc6a745d2ec6..07b5d63947b1 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -4,7 +4,7 @@ #include #include "util.h" #include "strbuf.h" -#include "pager.h" +#include #include "../perf.h" #include "../ui/ui.h" diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 32e12ecfe9c5..90aa1b46b2e5 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -1,6 +1,6 @@ #include "util.h" #include "../perf.h" -#include "parse-options.h" +#include #include "evsel.h" #include "cgroup.h" #include "evlist.h" diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 2e452ac1353d..d3e12e30e1d5 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c @@ -10,7 +10,7 @@ */ #include "util.h" #include "cache.h" -#include "exec_cmd.h" +#include #include "util/hist.h" /* perf_hist_config */ #include "util/llvm-utils.h" /* perf_llvm_config */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d1b6c206bb93..8c44aadb9810 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -18,7 +18,7 @@ #include #include "parse-events.h" -#include "parse-options.h" +#include #include diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c deleted file mode 100644 index e7f9ed7943e3..000000000000 --- a/tools/perf/util/exec_cmd.c +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "exec_cmd.h" -#include "subcmd-config.h" - -#define MAX_ARGS 32 -#define PATH_MAX 4096 - -static const char *argv_exec_path; -static const char *argv0_path; - -void exec_cmd_init(const char *exec_name, const char *prefix, - const char *exec_path, const char *exec_path_env) -{ - subcmd_config.exec_name = exec_name; - subcmd_config.prefix = prefix; - subcmd_config.exec_path = exec_path; - subcmd_config.exec_path_env = exec_path_env; -} - -#define is_dir_sep(c) ((c) == '/') - -static int is_absolute_path(const char *path) -{ - return path[0] == '/'; -} - -static const char *get_pwd_cwd(void) -{ - static char cwd[PATH_MAX + 1]; - char *pwd; - struct stat cwd_stat, pwd_stat; - if (getcwd(cwd, PATH_MAX) == NULL) - return NULL; - pwd = getenv("PWD"); - if (pwd && strcmp(pwd, cwd)) { - stat(cwd, &cwd_stat); - if (!stat(pwd, &pwd_stat) && - pwd_stat.st_dev == cwd_stat.st_dev && - pwd_stat.st_ino == cwd_stat.st_ino) { - strlcpy(cwd, pwd, PATH_MAX); - } - } - return cwd; -} - -static const char *make_nonrelative_path(const char *path) -{ - static char buf[PATH_MAX + 1]; - - if (is_absolute_path(path)) { - if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) - die("Too long path: %.*s", 60, path); - } else { - const char *cwd = get_pwd_cwd(); - if (!cwd) - die("Cannot determine the current working directory"); - if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) - die("Too long path: %.*s", 60, path); - } - return buf; -} - -char *system_path(const char *path) -{ - char *buf = NULL; - - if (is_absolute_path(path)) - return strdup(path); - - astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); - - return buf; -} - -const char *extract_argv0_path(const char *argv0) -{ - const char *slash; - - if (!argv0 || !*argv0) - return NULL; - slash = argv0 + strlen(argv0); - - while (argv0 <= slash && !is_dir_sep(*slash)) - slash--; - - if (slash >= argv0) { - argv0_path = strndup(argv0, slash - argv0); - return argv0_path ? slash + 1 : NULL; - } - - return argv0; -} - -void set_argv_exec_path(const char *exec_path) -{ - argv_exec_path = exec_path; - /* - * Propagate this setting to external programs. - */ - setenv(subcmd_config.exec_path_env, exec_path, 1); -} - - -/* Returns the highest-priority location to look for subprograms. */ -char *get_argv_exec_path(void) -{ - char *env; - - if (argv_exec_path) - return strdup(argv_exec_path); - - env = getenv(subcmd_config.exec_path_env); - if (env && *env) - return strdup(env); - - return system_path(subcmd_config.exec_path); -} - -static void add_path(char **out, const char *path) -{ - if (path && *path) { - if (is_absolute_path(path)) - astrcat(out, path); - else - astrcat(out, make_nonrelative_path(path)); - - astrcat(out, ":"); - } -} - -void setup_path(void) -{ - const char *old_path = getenv("PATH"); - char *new_path = NULL; - char *tmp = get_argv_exec_path(); - - add_path(&new_path, tmp); - add_path(&new_path, argv0_path); - free(tmp); - - if (old_path) - astrcat(&new_path, old_path); - else - astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); - - setenv("PATH", new_path, 1); - - free(new_path); -} - -static const char **prepare_exec_cmd(const char **argv) -{ - int argc; - const char **nargv; - - for (argc = 0; argv[argc]; argc++) - ; /* just counting */ - nargv = malloc(sizeof(*nargv) * (argc + 2)); - - nargv[0] = subcmd_config.exec_name; - for (argc = 0; argv[argc]; argc++) - nargv[argc + 1] = argv[argc]; - nargv[argc + 1] = NULL; - return nargv; -} - -int execv_cmd(const char **argv) { - const char **nargv = prepare_exec_cmd(argv); - - /* execvp() can only ever return if it fails */ - execvp(subcmd_config.exec_name, (char **)nargv); - - free(nargv); - return -1; -} - - -int execl_cmd(const char *cmd,...) -{ - int argc; - const char *argv[MAX_ARGS + 1]; - const char *arg; - va_list param; - - va_start(param, cmd); - argv[0] = cmd; - argc = 1; - while (argc < MAX_ARGS) { - arg = argv[argc++] = va_arg(param, char *); - if (!arg) - break; - } - va_end(param); - if (MAX_ARGS <= argc) { - fprintf(stderr, " Error: too many args to run %s\n", cmd); - return -1; - } - - argv[argc] = NULL; - return execv_cmd(argv); -} diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h deleted file mode 100644 index f1bd3436ad5f..000000000000 --- a/tools/perf/util/exec_cmd.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __PERF_EXEC_CMD_H -#define __PERF_EXEC_CMD_H - -extern void exec_cmd_init(const char *exec_name, const char *prefix, - const char *exec_path, const char *exec_path_env); - -extern void set_argv_exec_path(const char *exec_path); -extern const char *extract_argv0_path(const char *path); -extern void setup_path(void); -extern int execv_cmd(const char **argv); /* NULL terminated */ -extern int execl_cmd(const char *cmd, ...); -/* get_argv_exec_path and system_path return malloc'd string, caller must free it */ -extern char *get_argv_exec_path(void); -extern char *system_path(const char *path); - -#endif /* __PERF_EXEC_CMD_H */ diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c index a0820f16f511..dc1e41c9b054 100644 --- a/tools/perf/util/help-unknown-cmd.c +++ b/tools/perf/util/help-unknown-cmd.c @@ -1,5 +1,5 @@ #include "cache.h" -#include "help.h" +#include #include "../builtin.h" #include "levenshtein.h" diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c deleted file mode 100644 index 8169480066c6..000000000000 --- a/tools/perf/util/help.c +++ /dev/null @@ -1,268 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "help.h" -#include "exec_cmd.h" - -void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) -{ - struct cmdname *ent = malloc(sizeof(*ent) + len + 1); - - ent->len = len; - memcpy(ent->name, name, len); - ent->name[len] = 0; - - ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); - cmds->names[cmds->cnt++] = ent; -} - -void clean_cmdnames(struct cmdnames *cmds) -{ - unsigned int i; - - for (i = 0; i < cmds->cnt; ++i) - zfree(&cmds->names[i]); - zfree(&cmds->names); - cmds->cnt = 0; - cmds->alloc = 0; -} - -int cmdname_compare(const void *a_, const void *b_) -{ - struct cmdname *a = *(struct cmdname **)a_; - struct cmdname *b = *(struct cmdname **)b_; - return strcmp(a->name, b->name); -} - -void uniq(struct cmdnames *cmds) -{ - unsigned int i, j; - - if (!cmds->cnt) - return; - - for (i = j = 1; i < cmds->cnt; i++) - if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) - cmds->names[j++] = cmds->names[i]; - - cmds->cnt = j; -} - -void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) -{ - size_t ci, cj, ei; - int cmp; - - ci = cj = ei = 0; - while (ci < cmds->cnt && ei < excludes->cnt) { - cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); - if (cmp < 0) - cmds->names[cj++] = cmds->names[ci++]; - else if (cmp == 0) - ci++, ei++; - else if (cmp > 0) - ei++; - } - - while (ci < cmds->cnt) - cmds->names[cj++] = cmds->names[ci++]; - - cmds->cnt = cj; -} - -static void get_term_dimensions(struct winsize *ws) -{ - char *s = getenv("LINES"); - - if (s != NULL) { - ws->ws_row = atoi(s); - s = getenv("COLUMNS"); - if (s != NULL) { - ws->ws_col = atoi(s); - if (ws->ws_row && ws->ws_col) - return; - } - } -#ifdef TIOCGWINSZ - if (ioctl(1, TIOCGWINSZ, ws) == 0 && - ws->ws_row && ws->ws_col) - return; -#endif - ws->ws_row = 25; - ws->ws_col = 80; -} - -static void pretty_print_string_list(struct cmdnames *cmds, int longest) -{ - int cols = 1, rows; - int space = longest + 1; /* min 1 SP between words */ - struct winsize win; - int max_cols; - int i, j; - - get_term_dimensions(&win); - max_cols = win.ws_col - 1; /* don't print *on* the edge */ - - if (space < max_cols) - cols = max_cols / space; - rows = (cmds->cnt + cols - 1) / cols; - - for (i = 0; i < rows; i++) { - printf(" "); - - for (j = 0; j < cols; j++) { - unsigned int n = j * rows + i; - unsigned int size = space; - - if (n >= cmds->cnt) - break; - if (j == cols-1 || n + rows >= cmds->cnt) - size = 1; - printf("%-*s", size, cmds->names[n]->name); - } - putchar('\n'); - } -} - -static int is_executable(const char *name) -{ - struct stat st; - - if (stat(name, &st) || /* stat, not lstat */ - !S_ISREG(st.st_mode)) - return 0; - - return st.st_mode & S_IXUSR; -} - -static int has_extension(const char *filename, const char *ext) -{ - size_t len = strlen(filename); - size_t extlen = strlen(ext); - - return len > extlen && !memcmp(filename + len - extlen, ext, extlen); -} - -static void list_commands_in_dir(struct cmdnames *cmds, - const char *path, - const char *prefix) -{ - int prefix_len; - DIR *dir = opendir(path); - struct dirent *de; - char *buf = NULL; - - if (!dir) - return; - if (!prefix) - prefix = "perf-"; - prefix_len = strlen(prefix); - - astrcatf(&buf, "%s/", path); - - while ((de = readdir(dir)) != NULL) { - int entlen; - - if (prefixcmp(de->d_name, prefix)) - continue; - - astrcat(&buf, de->d_name); - if (!is_executable(buf)) - continue; - - entlen = strlen(de->d_name) - prefix_len; - if (has_extension(de->d_name, ".exe")) - entlen -= 4; - - add_cmdname(cmds, de->d_name + prefix_len, entlen); - } - closedir(dir); - free(buf); -} - -void load_command_list(const char *prefix, - struct cmdnames *main_cmds, - struct cmdnames *other_cmds) -{ - const char *env_path = getenv("PATH"); - char *exec_path = get_argv_exec_path(); - - if (exec_path) { - list_commands_in_dir(main_cmds, exec_path, prefix); - qsort(main_cmds->names, main_cmds->cnt, - sizeof(*main_cmds->names), cmdname_compare); - uniq(main_cmds); - } - - if (env_path) { - char *paths, *path, *colon; - path = paths = strdup(env_path); - while (1) { - if ((colon = strchr(path, ':'))) - *colon = 0; - if (!exec_path || strcmp(path, exec_path)) - list_commands_in_dir(other_cmds, path, prefix); - - if (!colon) - break; - path = colon + 1; - } - free(paths); - - qsort(other_cmds->names, other_cmds->cnt, - sizeof(*other_cmds->names), cmdname_compare); - uniq(other_cmds); - } - free(exec_path); - exclude_cmds(other_cmds, main_cmds); -} - -void list_commands(const char *title, struct cmdnames *main_cmds, - struct cmdnames *other_cmds) -{ - unsigned int i, longest = 0; - - for (i = 0; i < main_cmds->cnt; i++) - if (longest < main_cmds->names[i]->len) - longest = main_cmds->names[i]->len; - for (i = 0; i < other_cmds->cnt; i++) - if (longest < other_cmds->names[i]->len) - longest = other_cmds->names[i]->len; - - if (main_cmds->cnt) { - char *exec_path = get_argv_exec_path(); - printf("available %s in '%s'\n", title, exec_path); - printf("----------------"); - mput_char('-', strlen(title) + strlen(exec_path)); - putchar('\n'); - pretty_print_string_list(main_cmds, longest); - putchar('\n'); - free(exec_path); - } - - if (other_cmds->cnt) { - printf("%s available from elsewhere on your $PATH\n", title); - printf("---------------------------------------"); - mput_char('-', strlen(title)); - putchar('\n'); - pretty_print_string_list(other_cmds, longest); - putchar('\n'); - } -} - -int is_in_cmdlist(struct cmdnames *c, const char *s) -{ - unsigned int i; - - for (i = 0; i < c->cnt; i++) - if (!strcmp(s, c->names[i]->name)) - return 1; - return 0; -} diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h deleted file mode 100644 index 096c8bc45cd7..000000000000 --- a/tools/perf/util/help.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef __PERF_HELP_H -#define __PERF_HELP_H - -#include - -struct cmdnames { - size_t alloc; - size_t cnt; - struct cmdname { - size_t len; /* also used for similarity index in help.c */ - char name[]; - } **names; -}; - -static inline void mput_char(char c, unsigned int num) -{ - while(num--) - putchar(c); -} - -void load_command_list(const char *prefix, - struct cmdnames *main_cmds, - struct cmdnames *other_cmds); -void add_cmdname(struct cmdnames *cmds, const char *name, size_t len); -void clean_cmdnames(struct cmdnames *cmds); -int cmdname_compare(const void *a, const void *b); -void uniq(struct cmdnames *cmds); -/* Here we require that excludes is a sorted list. */ -void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes); -int is_in_cmdlist(struct cmdnames *c, const char *s); -void list_commands(const char *title, struct cmdnames *main_cmds, - struct cmdnames *other_cmds); - -#endif /* __PERF_HELP_H */ diff --git a/tools/perf/util/pager.c b/tools/perf/util/pager.c deleted file mode 100644 index d50f3b58606b..000000000000 --- a/tools/perf/util/pager.c +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include -#include -#include -#include "pager.h" -#include "run-command.h" -#include "sigchain.h" -#include "subcmd-config.h" - -/* - * This is split up from the rest of git so that we can do - * something different on Windows. - */ - -static int spawned_pager; - -void pager_init(const char *pager_env) -{ - subcmd_config.pager_env = pager_env; -} - -static void pager_preexec(void) -{ - /* - * Work around bug in "less" by not starting it until we - * have real input - */ - fd_set in; - - FD_ZERO(&in); - FD_SET(0, &in); - select(1, &in, NULL, &in, NULL); - - setenv("LESS", "FRSX", 0); -} - -static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; -static struct child_process pager_process; - -static void wait_for_pager(void) -{ - fflush(stdout); - fflush(stderr); - /* signal EOF to pager */ - close(1); - close(2); - finish_command(&pager_process); -} - -static void wait_for_pager_signal(int signo) -{ - wait_for_pager(); - sigchain_pop(signo); - raise(signo); -} - -void setup_pager(void) -{ - const char *pager = getenv(subcmd_config.pager_env); - - if (!isatty(1)) - return; - if (!pager) - pager = getenv("PAGER"); - if (!(pager || access("/usr/bin/pager", X_OK))) - pager = "/usr/bin/pager"; - if (!(pager || access("/usr/bin/less", X_OK))) - pager = "/usr/bin/less"; - if (!pager) - pager = "cat"; - if (!*pager || !strcmp(pager, "cat")) - return; - - spawned_pager = 1; /* means we are emitting to terminal */ - - /* spawn the pager */ - pager_argv[2] = pager; - pager_process.argv = pager_argv; - pager_process.in = -1; - pager_process.preexec_cb = pager_preexec; - - if (start_command(&pager_process)) - return; - - /* original process continues, but writes to the pipe */ - dup2(pager_process.in, 1); - if (isatty(2)) - dup2(pager_process.in, 2); - close(pager_process.in); - - /* this makes sure that the parent terminates after the pager */ - sigchain_push_common(wait_for_pager_signal); - atexit(wait_for_pager); -} - -int pager_in_use(void) -{ - return spawned_pager; -} diff --git a/tools/perf/util/pager.h b/tools/perf/util/pager.h deleted file mode 100644 index d6a591a4c017..000000000000 --- a/tools/perf/util/pager.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __PERF_PAGER_H -#define __PERF_PAGER_H - -extern void pager_init(const char *pager_env); - -extern void setup_pager(void); -extern int pager_in_use(void); - -#endif /* __PERF_PAGER_H */ diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index 355eecf6bf59..afc088dd7d20 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c @@ -1,7 +1,7 @@ #include "perf.h" #include "util/util.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/parse-branch-options.h" #define BRANCH_OPT(n, m) \ diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6fc8cd753e1a..4f7b0efdde2f 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -4,9 +4,9 @@ #include "../perf.h" #include "evlist.h" #include "evsel.h" -#include "parse-options.h" +#include #include "parse-events.h" -#include "exec_cmd.h" +#include #include "string.h" #include "symbol.h" #include "cache.h" diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c deleted file mode 100644 index 981bb4481fd5..000000000000 --- a/tools/perf/util/parse-options.c +++ /dev/null @@ -1,983 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "parse-options.h" -#include "subcmd-config.h" -#include "pager.h" - -#define OPT_SHORT 1 -#define OPT_UNSET 2 - -char *error_buf; - -static int opterror(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - fprintf(stderr, " Error: switch `%c' %s", opt->short_name, reason); - else if (flags & OPT_UNSET) - fprintf(stderr, " Error: option `no-%s' %s", opt->long_name, reason); - else - fprintf(stderr, " Error: option `%s' %s", opt->long_name, reason); - - return -1; -} - -static const char *skip_prefix(const char *str, const char *prefix) -{ - size_t len = strlen(prefix); - return strncmp(str, prefix, len) ? NULL : str + len; -} - -static void optwarning(const struct option *opt, const char *reason, int flags) -{ - if (flags & OPT_SHORT) - fprintf(stderr, " Warning: switch `%c' %s", opt->short_name, reason); - else if (flags & OPT_UNSET) - fprintf(stderr, " Warning: option `no-%s' %s", opt->long_name, reason); - else - fprintf(stderr, " Warning: option `%s' %s", opt->long_name, reason); -} - -static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt, - int flags, const char **arg) -{ - const char *res; - - if (p->opt) { - res = p->opt; - p->opt = NULL; - } else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 || - **(p->argv + 1) == '-')) { - res = (const char *)opt->defval; - } else if (p->argc > 1) { - p->argc--; - res = *++p->argv; - } else - return opterror(opt, "requires a value", flags); - if (arg) - *arg = res; - return 0; -} - -static int get_value(struct parse_opt_ctx_t *p, - const struct option *opt, int flags) -{ - const char *s, *arg = NULL; - const int unset = flags & OPT_UNSET; - int err; - - if (unset && p->opt) - return opterror(opt, "takes no value", flags); - if (unset && (opt->flags & PARSE_OPT_NONEG)) - return opterror(opt, "isn't available", flags); - if (opt->flags & PARSE_OPT_DISABLED) - return opterror(opt, "is not usable", flags); - - if (opt->flags & PARSE_OPT_EXCLUSIVE) { - if (p->excl_opt && p->excl_opt != opt) { - char msg[128]; - - if (((flags & OPT_SHORT) && p->excl_opt->short_name) || - p->excl_opt->long_name == NULL) { - snprintf(msg, sizeof(msg), "cannot be used with switch `%c'", - p->excl_opt->short_name); - } else { - snprintf(msg, sizeof(msg), "cannot be used with %s", - p->excl_opt->long_name); - } - opterror(opt, msg, flags); - return -3; - } - p->excl_opt = opt; - } - if (!(flags & OPT_SHORT) && p->opt) { - switch (opt->type) { - case OPTION_CALLBACK: - if (!(opt->flags & PARSE_OPT_NOARG)) - break; - /* FALLTHROUGH */ - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_BIT: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - return opterror(opt, "takes no value", flags); - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - case OPTION_STRING: - case OPTION_INTEGER: - case OPTION_UINTEGER: - case OPTION_LONG: - case OPTION_U64: - default: - break; - } - } - - if (opt->flags & PARSE_OPT_NOBUILD) { - char reason[128]; - bool noarg = false; - - err = snprintf(reason, sizeof(reason), - opt->flags & PARSE_OPT_CANSKIP ? - "is being ignored because %s " : - "is not available because %s", - opt->build_opt); - reason[sizeof(reason) - 1] = '\0'; - - if (err < 0) - strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ? - "is being ignored" : - "is not available", - sizeof(reason)); - - if (!(opt->flags & PARSE_OPT_CANSKIP)) - return opterror(opt, reason, flags); - - err = 0; - if (unset) - noarg = true; - if (opt->flags & PARSE_OPT_NOARG) - noarg = true; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - noarg = true; - - switch (opt->type) { - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_BIT: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - noarg = true; - break; - case OPTION_CALLBACK: - case OPTION_STRING: - case OPTION_INTEGER: - case OPTION_UINTEGER: - case OPTION_LONG: - case OPTION_U64: - default: - break; - } - - if (!noarg) - err = get_arg(p, opt, flags, NULL); - if (err) - return err; - - optwarning(opt, reason, flags); - return 0; - } - - switch (opt->type) { - case OPTION_BIT: - if (unset) - *(int *)opt->value &= ~opt->defval; - else - *(int *)opt->value |= opt->defval; - return 0; - - case OPTION_BOOLEAN: - *(bool *)opt->value = unset ? false : true; - if (opt->set) - *(bool *)opt->set = true; - return 0; - - case OPTION_INCR: - *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; - return 0; - - case OPTION_SET_UINT: - *(unsigned int *)opt->value = unset ? 0 : opt->defval; - return 0; - - case OPTION_SET_PTR: - *(void **)opt->value = unset ? NULL : (void *)opt->defval; - return 0; - - case OPTION_STRING: - err = 0; - if (unset) - *(const char **)opt->value = NULL; - else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - *(const char **)opt->value = (const char *)opt->defval; - else - err = get_arg(p, opt, flags, (const char **)opt->value); - - /* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */ - if (opt->flags & PARSE_OPT_NOEMPTY) { - const char *val = *(const char **)opt->value; - - if (!val) - return err; - - /* Similar to unset if we are given an empty string. */ - if (val[0] == '\0') { - *(const char **)opt->value = NULL; - return 0; - } - } - - return err; - - case OPTION_CALLBACK: - if (unset) - return (*opt->callback)(opt, NULL, 1) ? (-1) : 0; - if (opt->flags & PARSE_OPT_NOARG) - return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - return (*opt->callback)(opt, NULL, 0) ? (-1) : 0; - if (get_arg(p, opt, flags, &arg)) - return -1; - return (*opt->callback)(opt, arg, 0) ? (-1) : 0; - - case OPTION_INTEGER: - if (unset) { - *(int *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(int *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(int *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_UINTEGER: - if (unset) { - *(unsigned int *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(unsigned int *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(unsigned int *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_LONG: - if (unset) { - *(long *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(long *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(long *)opt->value = strtol(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_U64: - if (unset) { - *(u64 *)opt->value = 0; - return 0; - } - if (opt->flags & PARSE_OPT_OPTARG && !p->opt) { - *(u64 *)opt->value = opt->defval; - return 0; - } - if (get_arg(p, opt, flags, &arg)) - return -1; - *(u64 *)opt->value = strtoull(arg, (char **)&s, 10); - if (*s) - return opterror(opt, "expects a numerical value", flags); - return 0; - - case OPTION_END: - case OPTION_ARGUMENT: - case OPTION_GROUP: - default: - die("should not happen, someone must be hit on the forehead"); - } -} - -static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options) -{ - for (; options->type != OPTION_END; options++) { - if (options->short_name == *p->opt) { - p->opt = p->opt[1] ? p->opt + 1 : NULL; - return get_value(p, options, OPT_SHORT); - } - } - return -2; -} - -static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, - const struct option *options) -{ - const char *arg_end = strchr(arg, '='); - const struct option *abbrev_option = NULL, *ambiguous_option = NULL; - int abbrev_flags = 0, ambiguous_flags = 0; - - if (!arg_end) - arg_end = arg + strlen(arg); - - for (; options->type != OPTION_END; options++) { - const char *rest; - int flags = 0; - - if (!options->long_name) - continue; - - rest = skip_prefix(arg, options->long_name); - if (options->type == OPTION_ARGUMENT) { - if (!rest) - continue; - if (*rest == '=') - return opterror(options, "takes no value", flags); - if (*rest) - continue; - p->out[p->cpidx++] = arg - 2; - return 0; - } - if (!rest) { - if (!prefixcmp(options->long_name, "no-")) { - /* - * The long name itself starts with "no-", so - * accept the option without "no-" so that users - * do not have to enter "no-no-" to get the - * negation. - */ - rest = skip_prefix(arg, options->long_name + 3); - if (rest) { - flags |= OPT_UNSET; - goto match; - } - /* Abbreviated case */ - if (!prefixcmp(options->long_name + 3, arg)) { - flags |= OPT_UNSET; - goto is_abbreviated; - } - } - /* abbreviated? */ - if (!strncmp(options->long_name, arg, arg_end - arg)) { -is_abbreviated: - if (abbrev_option) { - /* - * If this is abbreviated, it is - * ambiguous. So when there is no - * exact match later, we need to - * error out. - */ - ambiguous_option = abbrev_option; - ambiguous_flags = abbrev_flags; - } - if (!(flags & OPT_UNSET) && *arg_end) - p->opt = arg_end + 1; - abbrev_option = options; - abbrev_flags = flags; - continue; - } - /* negated and abbreviated very much? */ - if (!prefixcmp("no-", arg)) { - flags |= OPT_UNSET; - goto is_abbreviated; - } - /* negated? */ - if (strncmp(arg, "no-", 3)) - continue; - flags |= OPT_UNSET; - rest = skip_prefix(arg + 3, options->long_name); - /* abbreviated and negated? */ - if (!rest && !prefixcmp(options->long_name, arg + 3)) - goto is_abbreviated; - if (!rest) - continue; - } -match: - if (*rest) { - if (*rest != '=') - continue; - p->opt = rest + 1; - } - return get_value(p, options, flags); - } - - if (ambiguous_option) { - fprintf(stderr, - " Error: Ambiguous option: %s (could be --%s%s or --%s%s)", - arg, - (ambiguous_flags & OPT_UNSET) ? "no-" : "", - ambiguous_option->long_name, - (abbrev_flags & OPT_UNSET) ? "no-" : "", - abbrev_option->long_name); - return -1; - } - if (abbrev_option) - return get_value(p, abbrev_option, abbrev_flags); - return -2; -} - -static void check_typos(const char *arg, const struct option *options) -{ - if (strlen(arg) < 3) - return; - - if (!prefixcmp(arg, "no-")) { - fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); - exit(129); - } - - for (; options->type != OPTION_END; options++) { - if (!options->long_name) - continue; - if (!prefixcmp(options->long_name, arg)) { - fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)", arg); - exit(129); - } - } -} - -static void parse_options_start(struct parse_opt_ctx_t *ctx, - int argc, const char **argv, int flags) -{ - memset(ctx, 0, sizeof(*ctx)); - ctx->argc = argc - 1; - ctx->argv = argv + 1; - ctx->out = argv; - ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); - ctx->flags = flags; - if ((flags & PARSE_OPT_KEEP_UNKNOWN) && - (flags & PARSE_OPT_STOP_AT_NON_OPTION)) - die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together"); -} - -static int usage_with_options_internal(const char * const *, - const struct option *, int, - struct parse_opt_ctx_t *); - -static int parse_options_step(struct parse_opt_ctx_t *ctx, - const struct option *options, - const char * const usagestr[]) -{ - int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP); - int excl_short_opt = 1; - const char *arg; - - /* we must reset ->opt, unknown short option leave it dangling */ - ctx->opt = NULL; - - for (; ctx->argc; ctx->argc--, ctx->argv++) { - arg = ctx->argv[0]; - if (*arg != '-' || !arg[1]) { - if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) - break; - ctx->out[ctx->cpidx++] = ctx->argv[0]; - continue; - } - - if (arg[1] != '-') { - ctx->opt = ++arg; - if (internal_help && *ctx->opt == 'h') { - return usage_with_options_internal(usagestr, options, 0, ctx); - } - switch (parse_short_opt(ctx, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 1); - case -2: - goto unknown; - case -3: - goto exclusive; - default: - break; - } - if (ctx->opt) - check_typos(arg, options); - while (ctx->opt) { - if (internal_help && *ctx->opt == 'h') - return usage_with_options_internal(usagestr, options, 0, ctx); - arg = ctx->opt; - switch (parse_short_opt(ctx, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 1); - case -2: - /* fake a short option thing to hide the fact that we may have - * started to parse aggregated stuff - * - * This is leaky, too bad. - */ - ctx->argv[0] = strdup(ctx->opt - 1); - *(char *)ctx->argv[0] = '-'; - goto unknown; - case -3: - goto exclusive; - default: - break; - } - } - continue; - } - - if (!arg[2]) { /* "--" */ - if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { - ctx->argc--; - ctx->argv++; - } - break; - } - - arg += 2; - if (internal_help && !strcmp(arg, "help-all")) - return usage_with_options_internal(usagestr, options, 1, ctx); - if (internal_help && !strcmp(arg, "help")) - return usage_with_options_internal(usagestr, options, 0, ctx); - if (!strcmp(arg, "list-opts")) - return PARSE_OPT_LIST_OPTS; - if (!strcmp(arg, "list-cmds")) - return PARSE_OPT_LIST_SUBCMDS; - switch (parse_long_opt(ctx, arg, options)) { - case -1: - return parse_options_usage(usagestr, options, arg, 0); - case -2: - goto unknown; - case -3: - excl_short_opt = 0; - goto exclusive; - default: - break; - } - continue; -unknown: - if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN)) - return PARSE_OPT_UNKNOWN; - ctx->out[ctx->cpidx++] = ctx->argv[0]; - ctx->opt = NULL; - } - return PARSE_OPT_DONE; - -exclusive: - parse_options_usage(usagestr, options, arg, excl_short_opt); - if ((excl_short_opt && ctx->excl_opt->short_name) || - ctx->excl_opt->long_name == NULL) { - char opt = ctx->excl_opt->short_name; - parse_options_usage(NULL, options, &opt, 1); - } else { - parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0); - } - return PARSE_OPT_HELP; -} - -static int parse_options_end(struct parse_opt_ctx_t *ctx) -{ - memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out)); - ctx->out[ctx->cpidx + ctx->argc] = NULL; - return ctx->cpidx + ctx->argc; -} - -int parse_options_subcommand(int argc, const char **argv, const struct option *options, - const char *const subcommands[], const char *usagestr[], int flags) -{ - struct parse_opt_ctx_t ctx; - - /* build usage string if it's not provided */ - if (subcommands && !usagestr[0]) { - char *buf = NULL; - - astrcatf(&buf, "%s %s [] {", subcmd_config.exec_name, argv[0]); - - for (int i = 0; subcommands[i]; i++) { - if (i) - astrcat(&buf, "|"); - astrcat(&buf, subcommands[i]); - } - astrcat(&buf, "}"); - - usagestr[0] = buf; - } - - parse_options_start(&ctx, argc, argv, flags); - switch (parse_options_step(&ctx, options, usagestr)) { - case PARSE_OPT_HELP: - exit(129); - case PARSE_OPT_DONE: - break; - case PARSE_OPT_LIST_OPTS: - while (options->type != OPTION_END) { - if (options->long_name) - printf("--%s ", options->long_name); - options++; - } - putchar('\n'); - exit(130); - case PARSE_OPT_LIST_SUBCMDS: - if (subcommands) { - for (int i = 0; subcommands[i]; i++) - printf("%s ", subcommands[i]); - } - putchar('\n'); - exit(130); - default: /* PARSE_OPT_UNKNOWN */ - if (ctx.argv[0][1] == '-') - astrcatf(&error_buf, "unknown option `%s'", - ctx.argv[0] + 2); - else - astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt); - usage_with_options(usagestr, options); - } - - return parse_options_end(&ctx); -} - -int parse_options(int argc, const char **argv, const struct option *options, - const char * const usagestr[], int flags) -{ - return parse_options_subcommand(argc, argv, options, NULL, - (const char **) usagestr, flags); -} - -#define USAGE_OPTS_WIDTH 24 -#define USAGE_GAP 2 - -static void print_option_help(const struct option *opts, int full) -{ - size_t pos; - int pad; - - if (opts->type == OPTION_GROUP) { - fputc('\n', stderr); - if (*opts->help) - fprintf(stderr, "%s\n", opts->help); - return; - } - if (!full && (opts->flags & PARSE_OPT_HIDDEN)) - return; - if (opts->flags & PARSE_OPT_DISABLED) - return; - - pos = fprintf(stderr, " "); - if (opts->short_name) - pos += fprintf(stderr, "-%c", opts->short_name); - else - pos += fprintf(stderr, " "); - - if (opts->long_name && opts->short_name) - pos += fprintf(stderr, ", "); - if (opts->long_name) - pos += fprintf(stderr, "--%s", opts->long_name); - - switch (opts->type) { - case OPTION_ARGUMENT: - break; - case OPTION_LONG: - case OPTION_U64: - case OPTION_INTEGER: - case OPTION_UINTEGER: - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=]"); - else - pos += fprintf(stderr, "[]"); - else - pos += fprintf(stderr, " "); - break; - case OPTION_CALLBACK: - if (opts->flags & PARSE_OPT_NOARG) - break; - /* FALLTHROUGH */ - case OPTION_STRING: - if (opts->argh) { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=<%s>]", opts->argh); - else - pos += fprintf(stderr, "[<%s>]", opts->argh); - else - pos += fprintf(stderr, " <%s>", opts->argh); - } else { - if (opts->flags & PARSE_OPT_OPTARG) - if (opts->long_name) - pos += fprintf(stderr, "[=...]"); - else - pos += fprintf(stderr, "[...]"); - else - pos += fprintf(stderr, " ..."); - } - break; - default: /* OPTION_{BIT,BOOLEAN,SET_UINT,SET_PTR} */ - case OPTION_END: - case OPTION_GROUP: - case OPTION_BIT: - case OPTION_BOOLEAN: - case OPTION_INCR: - case OPTION_SET_UINT: - case OPTION_SET_PTR: - break; - } - - if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', stderr); - pad = USAGE_OPTS_WIDTH; - } - fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); - if (opts->flags & PARSE_OPT_NOBUILD) - fprintf(stderr, "%*s(not built-in because %s)\n", - USAGE_OPTS_WIDTH + USAGE_GAP, "", - opts->build_opt); -} - -static int option__cmp(const void *va, const void *vb) -{ - const struct option *a = va, *b = vb; - int sa = tolower(a->short_name), sb = tolower(b->short_name), ret; - - if (sa == 0) - sa = 'z' + 1; - if (sb == 0) - sb = 'z' + 1; - - ret = sa - sb; - - if (ret == 0) { - const char *la = a->long_name ?: "", - *lb = b->long_name ?: ""; - ret = strcmp(la, lb); - } - - return ret; -} - -static struct option *options__order(const struct option *opts) -{ - int nr_opts = 0, len; - const struct option *o = opts; - struct option *ordered; - - for (o = opts; o->type != OPTION_END; o++) - ++nr_opts; - - len = sizeof(*o) * (nr_opts + 1); - ordered = malloc(len); - if (!ordered) - goto out; - memcpy(ordered, opts, len); - - qsort(ordered, nr_opts, sizeof(*o), option__cmp); -out: - return ordered; -} - -static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx) -{ - int i; - - for (i = 1; i < ctx->argc; ++i) { - const char *arg = ctx->argv[i]; - - if (arg[0] != '-') { - if (arg[1] == '\0') { - if (arg[0] == opt->short_name) - return true; - continue; - } - - if (opt->long_name && strcmp(opt->long_name, arg) == 0) - return true; - - if (opt->help && strcasestr(opt->help, arg) != NULL) - return true; - - continue; - } - - if (arg[1] == opt->short_name || - (arg[1] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2) == 0)) - return true; - } - - return false; -} - -static int usage_with_options_internal(const char * const *usagestr, - const struct option *opts, int full, - struct parse_opt_ctx_t *ctx) -{ - struct option *ordered; - - if (!usagestr) - return PARSE_OPT_HELP; - - setup_pager(); - - if (error_buf) { - fprintf(stderr, " Error: %s\n", error_buf); - zfree(&error_buf); - } - - fprintf(stderr, "\n Usage: %s\n", *usagestr++); - while (*usagestr && **usagestr) - fprintf(stderr, " or: %s\n", *usagestr++); - while (*usagestr) { - fprintf(stderr, "%s%s\n", - **usagestr ? " " : "", - *usagestr); - usagestr++; - } - - if (opts->type != OPTION_GROUP) - fputc('\n', stderr); - - ordered = options__order(opts); - if (ordered) - opts = ordered; - - for ( ; opts->type != OPTION_END; opts++) { - if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx)) - continue; - print_option_help(opts, full); - } - - fputc('\n', stderr); - - free(ordered); - - return PARSE_OPT_HELP; -} - -void usage_with_options(const char * const *usagestr, - const struct option *opts) -{ - usage_with_options_internal(usagestr, opts, 0, NULL); - exit(129); -} - -void usage_with_options_msg(const char * const *usagestr, - const struct option *opts, const char *fmt, ...) -{ - va_list ap; - char *tmp = error_buf; - - va_start(ap, fmt); - if (vasprintf(&error_buf, fmt, ap) == -1) - die("vasprintf failed"); - va_end(ap); - - free(tmp); - - usage_with_options_internal(usagestr, opts, 0, NULL); - exit(129); -} - -int parse_options_usage(const char * const *usagestr, - const struct option *opts, - const char *optstr, bool short_opt) -{ - if (!usagestr) - goto opt; - - fprintf(stderr, "\n Usage: %s\n", *usagestr++); - while (*usagestr && **usagestr) - fprintf(stderr, " or: %s\n", *usagestr++); - while (*usagestr) { - fprintf(stderr, "%s%s\n", - **usagestr ? " " : "", - *usagestr); - usagestr++; - } - fputc('\n', stderr); - -opt: - for ( ; opts->type != OPTION_END; opts++) { - if (short_opt) { - if (opts->short_name == *optstr) { - print_option_help(opts, 0); - break; - } - continue; - } - - if (opts->long_name == NULL) - continue; - - if (!prefixcmp(opts->long_name, optstr)) - print_option_help(opts, 0); - if (!prefixcmp("no-", optstr) && - !prefixcmp(opts->long_name, optstr + 3)) - print_option_help(opts, 0); - } - - return PARSE_OPT_HELP; -} - - -int parse_opt_verbosity_cb(const struct option *opt, - const char *arg __maybe_unused, - int unset) -{ - int *target = opt->value; - - if (unset) - /* --no-quiet, --no-verbose */ - *target = 0; - else if (opt->short_name == 'v') { - if (*target >= 0) - (*target)++; - else - *target = 1; - } else { - if (*target <= 0) - (*target)--; - else - *target = -1; - } - return 0; -} - -static struct option * -find_option(struct option *opts, int shortopt, const char *longopt) -{ - for (; opts->type != OPTION_END; opts++) { - if ((shortopt && opts->short_name == shortopt) || - (opts->long_name && longopt && - !strcmp(opts->long_name, longopt))) - return opts; - } - return NULL; -} - -void set_option_flag(struct option *opts, int shortopt, const char *longopt, - int flag) -{ - struct option *opt = find_option(opts, shortopt, longopt); - - if (opt) - opt->flags |= flag; - return; -} - -void set_option_nobuild(struct option *opts, int shortopt, - const char *longopt, - const char *build_opt, - bool can_skip) -{ - struct option *opt = find_option(opts, shortopt, longopt); - - if (!opt) - return; - - opt->flags |= PARSE_OPT_NOBUILD; - opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0; - opt->build_opt = build_opt; -} diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h deleted file mode 100644 index dec893f10477..000000000000 --- a/tools/perf/util/parse-options.h +++ /dev/null @@ -1,228 +0,0 @@ -#ifndef __PERF_PARSE_OPTIONS_H -#define __PERF_PARSE_OPTIONS_H - -#include -#include - -enum parse_opt_type { - /* special types */ - OPTION_END, - OPTION_ARGUMENT, - OPTION_GROUP, - /* options with no arguments */ - OPTION_BIT, - OPTION_BOOLEAN, - OPTION_INCR, - OPTION_SET_UINT, - OPTION_SET_PTR, - /* options with arguments (usually) */ - OPTION_STRING, - OPTION_INTEGER, - OPTION_LONG, - OPTION_CALLBACK, - OPTION_U64, - OPTION_UINTEGER, -}; - -enum parse_opt_flags { - PARSE_OPT_KEEP_DASHDASH = 1, - PARSE_OPT_STOP_AT_NON_OPTION = 2, - PARSE_OPT_KEEP_ARGV0 = 4, - PARSE_OPT_KEEP_UNKNOWN = 8, - PARSE_OPT_NO_INTERNAL_HELP = 16, -}; - -enum parse_opt_option_flags { - PARSE_OPT_OPTARG = 1, - PARSE_OPT_NOARG = 2, - PARSE_OPT_NONEG = 4, - PARSE_OPT_HIDDEN = 8, - PARSE_OPT_LASTARG_DEFAULT = 16, - PARSE_OPT_DISABLED = 32, - PARSE_OPT_EXCLUSIVE = 64, - PARSE_OPT_NOEMPTY = 128, - PARSE_OPT_NOBUILD = 256, - PARSE_OPT_CANSKIP = 512, -}; - -struct option; -typedef int parse_opt_cb(const struct option *, const char *arg, int unset); - -/* - * `type`:: - * holds the type of the option, you must have an OPTION_END last in your - * array. - * - * `short_name`:: - * the character to use as a short option name, '\0' if none. - * - * `long_name`:: - * the long option name, without the leading dashes, NULL if none. - * - * `value`:: - * stores pointers to the values to be filled. - * - * `argh`:: - * token to explain the kind of argument this option wants. Keep it - * homogenous across the repository. - * - * `help`:: - * the short help associated to what the option does. - * Must never be NULL (except for OPTION_END). - * OPTION_GROUP uses this pointer to store the group header. - * - * `flags`:: - * mask of parse_opt_option_flags. - * PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs) - * PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs - * PARSE_OPT_NONEG: says that this option cannot be negated - * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in - * the long one. - * - * `callback`:: - * pointer to the callback to use for OPTION_CALLBACK. - * - * `defval`:: - * default value to fill (*->value) with for PARSE_OPT_OPTARG. - * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in - * the value when met. - * CALLBACKS can use it like they want. - * - * `set`:: - * whether an option was set by the user - */ -struct option { - enum parse_opt_type type; - int short_name; - const char *long_name; - void *value; - const char *argh; - const char *help; - const char *build_opt; - - int flags; - parse_opt_cb *callback; - intptr_t defval; - bool *set; - void *data; -}; - -#define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) - -#define OPT_END() { .type = OPTION_END } -#define OPT_ARGUMENT(l, h) { .type = OPTION_ARGUMENT, .long_name = (l), .help = (h) } -#define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } -#define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } -#define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } -#define OPT_BOOLEAN_FLAG(s, l, v, h, f) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h), .flags = (f) } -#define OPT_BOOLEAN_SET(s, l, v, os, h) \ - { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, bool *), .help = (h), \ - .set = check_vtype(os, bool *)} -#define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } -#define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } -#define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } -#define OPT_INTEGER(s, l, v, h) { .type = OPTION_INTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } -#define OPT_UINTEGER(s, l, v, h) { .type = OPTION_UINTEGER, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h) } -#define OPT_LONG(s, l, v, h) { .type = OPTION_LONG, .short_name = (s), .long_name = (l), .value = check_vtype(v, long *), .help = (h) } -#define OPT_U64(s, l, v, h) { .type = OPTION_U64, .short_name = (s), .long_name = (l), .value = check_vtype(v, u64 *), .help = (h) } -#define OPT_STRING(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h) } -#define OPT_STRING_OPTARG(s, l, v, a, h, d) \ - { .type = OPTION_STRING, .short_name = (s), .long_name = (l), \ - .value = check_vtype(v, const char **), (a), .help = (h), \ - .flags = PARSE_OPT_OPTARG, .defval = (intptr_t)(d) } -#define OPT_STRING_NOEMPTY(s, l, v, a, h) { .type = OPTION_STRING, .short_name = (s), .long_name = (l), .value = check_vtype(v, const char **), (a), .help = (h), .flags = PARSE_OPT_NOEMPTY} -#define OPT_DATE(s, l, v, h) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } -#define OPT_CALLBACK(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } -#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } -#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } -#define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ - .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ - .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} -#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ - { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ - .value = (v), (a), .help = (h), .callback = (f), \ - .flags = PARSE_OPT_OPTARG, .data = (d) } - -/* parse_options() will filter out the processed options and leave the - * non-option argments in argv[]. - * Returns the number of arguments left in argv[]. - * - * NOTE: parse_options() and parse_options_subcommand() may call exit() in the - * case of an error (or for 'special' options like --list-cmds or --list-opts). - */ -extern int parse_options(int argc, const char **argv, - const struct option *options, - const char * const usagestr[], int flags); - -extern int parse_options_subcommand(int argc, const char **argv, - const struct option *options, - const char *const subcommands[], - const char *usagestr[], int flags); - -extern NORETURN void usage_with_options(const char * const *usagestr, - const struct option *options); -extern NORETURN __attribute__((format(printf,3,4))) -void usage_with_options_msg(const char * const *usagestr, - const struct option *options, - const char *fmt, ...); - -/*----- incremantal advanced APIs -----*/ - -enum { - PARSE_OPT_HELP = -1, - PARSE_OPT_DONE, - PARSE_OPT_LIST_OPTS, - PARSE_OPT_LIST_SUBCMDS, - PARSE_OPT_UNKNOWN, -}; - -/* - * It's okay for the caller to consume argv/argc in the usual way. - * Other fields of that structure are private to parse-options and should not - * be modified in any way. - */ -struct parse_opt_ctx_t { - const char **argv; - const char **out; - int argc, cpidx; - const char *opt; - const struct option *excl_opt; - int flags; -}; - -extern int parse_options_usage(const char * const *usagestr, - const struct option *opts, - const char *optstr, - bool short_opt); - - -/*----- some often used options -----*/ -extern int parse_opt_abbrev_cb(const struct option *, const char *, int); -extern int parse_opt_approxidate_cb(const struct option *, const char *, int); -extern int parse_opt_verbosity_cb(const struct option *, const char *, int); - -#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") -#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") -#define OPT__VERBOSITY(var) \ - { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \ - PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \ - { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \ - PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 } -#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") -#define OPT__ABBREV(var) \ - { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ - "use digits to display SHA-1s", \ - PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } - -extern const char *parse_options_fix_filename(const char *prefix, const char *file); - -void set_option_flag(struct option *opts, int sopt, const char *lopt, int flag); -void set_option_nobuild(struct option *opts, int shortopt, const char *longopt, - const char *build_opt, bool can_skip); -#endif /* __PERF_PARSE_OPTIONS_H */ diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c index 4f2c1c255d81..646ecf736aad 100644 --- a/tools/perf/util/parse-regs-options.c +++ b/tools/perf/util/parse-regs-options.c @@ -1,7 +1,7 @@ #include "perf.h" #include "util/util.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/parse-regs-options.h" int diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c deleted file mode 100644 index fed37d6ae070..000000000000 --- a/tools/perf/util/run-command.c +++ /dev/null @@ -1,227 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "subcmd-util.h" -#include "run-command.h" -#include "exec_cmd.h" - -#define STRERR_BUFSIZE 128 - -static inline void close_pair(int fd[2]) -{ - close(fd[0]); - close(fd[1]); -} - -static inline void dup_devnull(int to) -{ - int fd = open("/dev/null", O_RDWR); - dup2(fd, to); - close(fd); -} - -int start_command(struct child_process *cmd) -{ - int need_in, need_out, need_err; - int fdin[2], fdout[2], fderr[2]; - char sbuf[STRERR_BUFSIZE]; - - /* - * In case of errors we must keep the promise to close FDs - * that have been passed in via ->in and ->out. - */ - - need_in = !cmd->no_stdin && cmd->in < 0; - if (need_in) { - if (pipe(fdin) < 0) { - if (cmd->out > 0) - close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->in = fdin[1]; - } - - need_out = !cmd->no_stdout - && !cmd->stdout_to_stderr - && cmd->out < 0; - if (need_out) { - if (pipe(fdout) < 0) { - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->out = fdout[0]; - } - - need_err = !cmd->no_stderr && cmd->err < 0; - if (need_err) { - if (pipe(fderr) < 0) { - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - if (need_out) - close_pair(fdout); - else if (cmd->out) - close(cmd->out); - return -ERR_RUN_COMMAND_PIPE; - } - cmd->err = fderr[0]; - } - - fflush(NULL); - cmd->pid = fork(); - if (!cmd->pid) { - if (cmd->no_stdin) - dup_devnull(0); - else if (need_in) { - dup2(fdin[0], 0); - close_pair(fdin); - } else if (cmd->in) { - dup2(cmd->in, 0); - close(cmd->in); - } - - if (cmd->no_stderr) - dup_devnull(2); - else if (need_err) { - dup2(fderr[1], 2); - close_pair(fderr); - } - - if (cmd->no_stdout) - dup_devnull(1); - else if (cmd->stdout_to_stderr) - dup2(2, 1); - else if (need_out) { - dup2(fdout[1], 1); - close_pair(fdout); - } else if (cmd->out > 1) { - dup2(cmd->out, 1); - close(cmd->out); - } - - if (cmd->dir && chdir(cmd->dir)) - die("exec %s: cd to %s failed (%s)", cmd->argv[0], - cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); - if (cmd->env) { - for (; *cmd->env; cmd->env++) { - if (strchr(*cmd->env, '=')) - putenv((char*)*cmd->env); - else - unsetenv(*cmd->env); - } - } - if (cmd->preexec_cb) - cmd->preexec_cb(); - if (cmd->exec_cmd) { - execv_cmd(cmd->argv); - } else { - execvp(cmd->argv[0], (char *const*) cmd->argv); - } - exit(127); - } - - if (cmd->pid < 0) { - int err = errno; - if (need_in) - close_pair(fdin); - else if (cmd->in) - close(cmd->in); - if (need_out) - close_pair(fdout); - else if (cmd->out) - close(cmd->out); - if (need_err) - close_pair(fderr); - return err == ENOENT ? - -ERR_RUN_COMMAND_EXEC : - -ERR_RUN_COMMAND_FORK; - } - - if (need_in) - close(fdin[0]); - else if (cmd->in) - close(cmd->in); - - if (need_out) - close(fdout[1]); - else if (cmd->out) - close(cmd->out); - - if (need_err) - close(fderr[1]); - - return 0; -} - -static int wait_or_whine(pid_t pid) -{ - char sbuf[STRERR_BUFSIZE]; - - for (;;) { - int status, code; - pid_t waiting = waitpid(pid, &status, 0); - - if (waiting < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, " Error: waitpid failed (%s)", - strerror_r(errno, sbuf, sizeof(sbuf))); - return -ERR_RUN_COMMAND_WAITPID; - } - if (waiting != pid) - return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; - if (WIFSIGNALED(status)) - return -ERR_RUN_COMMAND_WAITPID_SIGNAL; - - if (!WIFEXITED(status)) - return -ERR_RUN_COMMAND_WAITPID_NOEXIT; - code = WEXITSTATUS(status); - switch (code) { - case 127: - return -ERR_RUN_COMMAND_EXEC; - case 0: - return 0; - default: - return -code; - } - } -} - -int finish_command(struct child_process *cmd) -{ - return wait_or_whine(cmd->pid); -} - -int run_command(struct child_process *cmd) -{ - int code = start_command(cmd); - if (code) - return code; - return finish_command(cmd); -} - -static void prepare_run_command_v_opt(struct child_process *cmd, - const char **argv, - int opt) -{ - memset(cmd, 0, sizeof(*cmd)); - cmd->argv = argv; - cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0; - cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; -} - -int run_command_v_opt(const char **argv, int opt) -{ - struct child_process cmd; - prepare_run_command_v_opt(&cmd, argv, opt); - return run_command(&cmd); -} diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h deleted file mode 100644 index 4a55393a6547..000000000000 --- a/tools/perf/util/run-command.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __PERF_RUN_COMMAND_H -#define __PERF_RUN_COMMAND_H - -#include - -enum { - ERR_RUN_COMMAND_FORK = 10000, - ERR_RUN_COMMAND_EXEC, - ERR_RUN_COMMAND_PIPE, - ERR_RUN_COMMAND_WAITPID, - ERR_RUN_COMMAND_WAITPID_WRONG_PID, - ERR_RUN_COMMAND_WAITPID_SIGNAL, - ERR_RUN_COMMAND_WAITPID_NOEXIT, -}; -#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK) - -struct child_process { - const char **argv; - pid_t pid; - /* - * Using .in, .out, .err: - * - Specify 0 for no redirections (child inherits stdin, stdout, - * stderr from parent). - * - Specify -1 to have a pipe allocated as follows: - * .in: returns the writable pipe end; parent writes to it, - * the readable pipe end becomes child's stdin - * .out, .err: returns the readable pipe end; parent reads from - * it, the writable pipe end becomes child's stdout/stderr - * The caller of start_command() must close the returned FDs - * after it has completed reading from/writing to it! - * - Specify > 0 to set a channel to a particular FD as follows: - * .in: a readable FD, becomes child's stdin - * .out: a writable FD, becomes child's stdout/stderr - * .err > 0 not supported - * The specified FD is closed by start_command(), even in case - * of errors! - */ - int in; - int out; - int err; - const char *dir; - const char *const *env; - unsigned no_stdin:1; - unsigned no_stdout:1; - unsigned no_stderr:1; - unsigned exec_cmd:1; /* if this is to be external sub-command */ - unsigned stdout_to_stderr:1; - void (*preexec_cb)(void); -}; - -int start_command(struct child_process *); -int finish_command(struct child_process *); -int run_command(struct child_process *); - -#define RUN_COMMAND_NO_STDIN 1 -#define RUN_EXEC_CMD 2 /*If this is to be external sub-command */ -#define RUN_COMMAND_STDOUT_TO_STDERR 4 -int run_command_v_opt(const char **argv, int opt); - -#endif /* __PERF_RUN_COMMAND_H */ diff --git a/tools/perf/util/sigchain.c b/tools/perf/util/sigchain.c deleted file mode 100644 index 3537c348a18e..000000000000 --- a/tools/perf/util/sigchain.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "subcmd-util.h" -#include "sigchain.h" - -#define SIGCHAIN_MAX_SIGNALS 32 - -struct sigchain_signal { - sigchain_fun *old; - int n; - int alloc; -}; -static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; - -static void check_signum(int sig) -{ - if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) - die("BUG: signal out of range: %d", sig); -} - -static int sigchain_push(int sig, sigchain_fun f) -{ - struct sigchain_signal *s = signals + sig; - check_signum(sig); - - ALLOC_GROW(s->old, s->n + 1, s->alloc); - s->old[s->n] = signal(sig, f); - if (s->old[s->n] == SIG_ERR) - return -1; - s->n++; - return 0; -} - -int sigchain_pop(int sig) -{ - struct sigchain_signal *s = signals + sig; - check_signum(sig); - if (s->n < 1) - return 0; - - if (signal(sig, s->old[s->n - 1]) == SIG_ERR) - return -1; - s->n--; - return 0; -} - -void sigchain_push_common(sigchain_fun f) -{ - sigchain_push(SIGINT, f); - sigchain_push(SIGHUP, f); - sigchain_push(SIGTERM, f); - sigchain_push(SIGQUIT, f); - sigchain_push(SIGPIPE, f); -} diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h deleted file mode 100644 index 959d64eb5557..000000000000 --- a/tools/perf/util/sigchain.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __PERF_SIGCHAIN_H -#define __PERF_SIGCHAIN_H - -typedef void (*sigchain_fun)(int); - -int sigchain_pop(int sig); - -void sigchain_push_common(sigchain_fun f); - -#endif /* __PERF_SIGCHAIN_H */ diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 31228851e397..86f05e7a5566 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -18,7 +18,7 @@ #include "debug.h" #include "header.h" -#include "parse-options.h" +#include #include "parse-events.h" #include "hist.h" #include "thread.h" diff --git a/tools/perf/util/subcmd-config.c b/tools/perf/util/subcmd-config.c deleted file mode 100644 index d017c728bd1b..000000000000 --- a/tools/perf/util/subcmd-config.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "subcmd-config.h" - -#define UNDEFINED "SUBCMD_HAS_NOT_BEEN_INITIALIZED" - -struct subcmd_config subcmd_config = { - .exec_name = UNDEFINED, - .prefix = UNDEFINED, - .exec_path = UNDEFINED, - .exec_path_env = UNDEFINED, - .pager_env = UNDEFINED, -}; diff --git a/tools/perf/util/subcmd-config.h b/tools/perf/util/subcmd-config.h deleted file mode 100644 index cc8514030b5c..000000000000 --- a/tools/perf/util/subcmd-config.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __PERF_SUBCMD_CONFIG_H -#define __PERF_SUBCMD_CONFIG_H - -struct subcmd_config { - const char *exec_name; - const char *prefix; - const char *exec_path; - const char *exec_path_env; - const char *pager_env; -}; - -extern struct subcmd_config subcmd_config; - -#endif /* __PERF_SUBCMD_CONFIG_H */ diff --git a/tools/perf/util/subcmd-util.h b/tools/perf/util/subcmd-util.h deleted file mode 100644 index 321aeb11a381..000000000000 --- a/tools/perf/util/subcmd-util.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef __PERF_SUBCMD_UTIL_H -#define __PERF_SUBCMD_UTIL_H - -#include -#include -#include - -#define NORETURN __attribute__((__noreturn__)) - -static inline void report(const char *prefix, const char *err, va_list params) -{ - char msg[1024]; - vsnprintf(msg, sizeof(msg), err, params); - fprintf(stderr, " %s%s\n", prefix, msg); -} - -static NORETURN inline void die(const char *err, ...) -{ - va_list params; - - va_start(params, err); - report(" Fatal: ", err, params); - exit(128); - va_end(params); -} - -#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) - -#define alloc_nr(x) (((x)+16)*3/2) - -/* - * Realloc the buffer pointed at by variable 'x' so that it can hold - * at least 'nr' entries; the number of entries currently allocated - * is 'alloc', using the standard growing factor alloc_nr() macro. - * - * DO NOT USE any expression with side-effect for 'x' or 'alloc'. - */ -#define ALLOC_GROW(x, nr, alloc) \ - do { \ - if ((nr) > alloc) { \ - if (alloc_nr(alloc) < (nr)) \ - alloc = (nr); \ - else \ - alloc = alloc_nr(alloc); \ - x = xrealloc((x), alloc * sizeof(*(x))); \ - } \ - } while(0) - -static inline void *xrealloc(void *ptr, size_t size) -{ - void *ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) { - ret = realloc(ptr, size); - if (!ret && !size) - ret = realloc(ptr, 1); - if (!ret) - die("Out of memory, realloc failed"); - } - return ret; -} - -#define astrcatf(out, fmt, ...) \ -({ \ - char *tmp = *(out); \ - if (asprintf((out), "%s" fmt, tmp ?: "", ## __VA_ARGS__) == -1) \ - die("asprintf failed"); \ - free(tmp); \ -}) - -static inline void astrcat(char **out, const char *add) -{ - char *tmp = *out; - - if (asprintf(out, "%s%s", tmp ?: "", add) == -1) - die("asprintf failed"); - - free(tmp); -} - -static inline int prefixcmp(const char *str, const char *prefix) -{ - for (; ; str++, prefix++) - if (!*prefix) - return 0; - else if (*str != *prefix) - return (unsigned char)*prefix - (unsigned char)*str; -} - -#endif /* __PERF_SUBCMD_UTIL_H */ -- cgit v1.2.2 From 5f3339d2e83ca587c2e13c3e37e1b5fb7c68ebe5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:19 +0100 Subject: perf thread_map: Add thread_map user level event Adding the thread_map event to pass/store thread maps as data in the pipe/perf.data. Storing the thread ID along with the standard comm[16] thread name string. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-4-git-send-email-jolsa@kernel.org [ Renamed thread_map_data_event to thread_map_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 13 +++++++++++++ tools/perf/util/session.c | 26 ++++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 42 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 8b10621b415c..771545a27b9b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -37,6 +37,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO", [PERF_RECORD_AUXTRACE] = "AUXTRACE", [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", + [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index a0dbcbd4f6d8..66f303e69c4d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -226,6 +226,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE_INFO = 70, PERF_RECORD_AUXTRACE = 71, PERF_RECORD_AUXTRACE_ERROR = 72, + PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_HEADER_MAX }; @@ -356,6 +357,17 @@ struct context_switch_event { u32 next_prev_tid; }; +struct thread_map_event_entry { + u64 pid; + char comm[16]; +}; + +struct thread_map_event { + struct perf_event_header header; + u64 nr; + struct thread_map_event_entry entries[]; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -378,6 +390,7 @@ union perf_event { struct aux_event aux; struct itrace_start_event itrace_start; struct context_switch_event context_switch; + struct thread_map_event thread_map; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 9774686525b4..36b07b22392d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -296,6 +296,16 @@ int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, return 0; } + +static +int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -346,6 +356,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->auxtrace = process_event_auxtrace_stub; if (tool->auxtrace_error == NULL) tool->auxtrace_error = process_event_auxtrace_error_stub; + if (tool->thread_map == NULL) + tool->thread_map = process_event_thread_map_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -616,6 +628,17 @@ static void perf_event__auxtrace_error_swap(union perf_event *event, event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip); } +static void perf_event__thread_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + unsigned i; + + event->thread_map.nr = bswap_64(event->thread_map.nr); + + for (i = 0; i < event->thread_map.nr; i++) + event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -643,6 +666,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, + [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1179,6 +1203,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, case PERF_RECORD_AUXTRACE_ERROR: perf_session__auxtrace_error_inc(session, event); return tool->auxtrace_error(tool, event, session); + case PERF_RECORD_THREAD_MAP: + return tool->thread_map(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index cab8cc24831b..1af4774960c3 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -55,7 +55,8 @@ struct perf_tool { event_op2 build_id, id_index, auxtrace_info, - auxtrace_error; + auxtrace_error, + thread_map; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- cgit v1.2.2 From 99471c967a00c875bb5d61f377d4267904545499 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:20 +0100 Subject: perf thread_map: Add thread_map event sythesize function Introduce the perf_event__synthesize_thread_map2 function to synthesize struct thread_map. The perf_event__synthesize_thread_map name is already taken for synthesizing the complete threads data (comm/mmap/fork). Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-5-git-send-email-jolsa@kernel.org [ Rename thread_map_data_event to thread_map_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 36 ++++++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 4 ++++ 2 files changed, 40 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 771545a27b9b..b13373a60337 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -700,6 +700,42 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, return err; } +int perf_event__synthesize_thread_map2(struct perf_tool *tool, + struct thread_map *threads, + perf_event__handler_t process, + struct machine *machine) +{ + union perf_event *event; + int i, err, size; + + size = sizeof(event->thread_map); + size += threads->nr * sizeof(event->thread_map.entries[0]); + + event = zalloc(size); + if (!event) + return -ENOMEM; + + event->header.type = PERF_RECORD_THREAD_MAP; + event->header.size = size; + event->thread_map.nr = threads->nr; + + for (i = 0; i < threads->nr; i++) { + struct thread_map_event_entry *entry = &event->thread_map.entries[i]; + char *comm = thread_map__comm(threads, i); + + if (!comm) + comm = (char *) ""; + + entry->pid = thread_map__pid(threads, i); + strncpy((char *) &entry->comm, comm, sizeof(entry->comm)); + } + + err = process(tool, event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 66f303e69c4d..952dd4d83f81 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -408,6 +408,10 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, unsigned int proc_map_timeout); +int perf_event__synthesize_thread_map2(struct perf_tool *tool, + struct thread_map *threads, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, -- cgit v1.2.2 From 59660942397b57b37eccba014544623cf4beb12b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:21 +0100 Subject: perf thread_map: Add thread_map__new_event function Introducing the thread_map__new_event function to create a struct thread_map object from a thread_map event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/thread_map.c | 27 +++++++++++++++++++++++++++ tools/perf/util/thread_map.h | 3 +++ 2 files changed, 30 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 371fb28fe5b1..08afc6909953 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -13,6 +13,7 @@ #include "thread_map.h" #include "util.h" #include "debug.h" +#include "event.h" /* Skip "." and ".." directories */ static int filter(const struct dirent *dir) @@ -409,3 +410,29 @@ void thread_map__read_comms(struct thread_map *threads) for (i = 0; i < threads->nr; ++i) comm_init(threads, i); } + +static void thread_map__copy_event(struct thread_map *threads, + struct thread_map_event *event) +{ + unsigned i; + + threads->nr = (int) event->nr; + + for (i = 0; i < event->nr; i++) { + thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid); + threads->map[i].comm = strndup(event->entries[i].comm, 16); + } + + atomic_set(&threads->refcnt, 1); +} + +struct thread_map *thread_map__new_event(struct thread_map_event *event) +{ + struct thread_map *threads; + + threads = thread_map__alloc(event->nr); + if (threads) + thread_map__copy_event(threads, event); + + return threads; +} diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index af679d8a50f8..85e4c7c4fbde 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -16,11 +16,14 @@ struct thread_map { struct thread_map_data map[]; }; +struct thread_map_event; + struct thread_map *thread_map__new_dummy(void); struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_tid(pid_t tid); struct thread_map *thread_map__new_by_uid(uid_t uid); struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); +struct thread_map *thread_map__new_event(struct thread_map_event *event); struct thread_map *thread_map__get(struct thread_map *map); void thread_map__put(struct thread_map *map); -- cgit v1.2.2 From ec7fa596f514b76a5f1003ffe9e6dfb50cb9e811 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:22 +0100 Subject: perf thread_map: Add perf_event__fprintf_thread_map function To display a thread_map event for a raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 16 ++++++++++++++++ tools/perf/util/event.h | 1 + 2 files changed, 17 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index b13373a60337..938f006c758e 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -820,6 +820,22 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) event->mmap2.filename); } +size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) +{ + struct thread_map *threads = thread_map__new_event(&event->thread_map); + size_t ret; + + ret = fprintf(fp, " nr: "); + + if (threads) + ret += thread_map__fprintf(threads, fp); + else + ret += fprintf(fp, "failed to get threads from event\n"); + + thread_map__put(threads); + return ret; +} + int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 952dd4d83f81..b7ad896d1317 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -516,6 +516,7 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, -- cgit v1.2.2 From 6640b6c227fc85fd8bdcc4a31239a04450487f6a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:23 +0100 Subject: perf cpu_map: Add cpu_map user level event Adding the cpu_map event to pass/store cpu maps as data in a pipe/perf.data. We store maps in 2 formats: - list of cpus - mask of cpus The format that takes less space is selected transparently in the following patch. The interface is made generic, so we could add the cpumap event data into another event in the following patches. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-8-git-send-email-jolsa@kernel.org [ cpu_map_data_cpus -> cpu_map_entries, cpu_map_data_mask -> cpu_map_mask ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 28 ++++++++++++++++++++++++++ tools/perf/util/session.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 81 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 938f006c758e..719c0781a82a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -38,6 +38,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE] = "AUXTRACE", [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", + [PERF_RECORD_CPU_MAP] = "CPU_MAP", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index b7ad896d1317..1c82a0ebda73 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -227,6 +227,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE = 71, PERF_RECORD_AUXTRACE_ERROR = 72, PERF_RECORD_THREAD_MAP = 73, + PERF_RECORD_CPU_MAP = 74, PERF_RECORD_HEADER_MAX }; @@ -271,6 +272,32 @@ struct events_stats { u32 nr_proc_map_timeout; }; +enum { + PERF_CPU_MAP__CPUS = 0, + PERF_CPU_MAP__MASK = 1, +}; + +struct cpu_map_entries { + u16 nr; + u16 cpu[]; +}; + +struct cpu_map_mask { + u16 nr; + u16 long_size; + unsigned long mask[]; +}; + +struct cpu_map_data { + u16 type; + char data[]; +}; + +struct cpu_map_event { + struct perf_event_header header; + struct cpu_map_data data; +}; + struct attr_event { struct perf_event_header header; struct perf_event_attr attr; @@ -391,6 +418,7 @@ union perf_event { struct itrace_start_event itrace_start; struct context_switch_event context_switch; struct thread_map_event thread_map; + struct cpu_map_event cpu_map; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 36b07b22392d..4350f5e85bf5 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -306,6 +306,15 @@ int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, return 0; } +static +int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -358,6 +367,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->auxtrace_error = process_event_auxtrace_error_stub; if (tool->thread_map == NULL) tool->thread_map = process_event_thread_map_stub; + if (tool->cpu_map == NULL) + tool->cpu_map = process_event_cpu_map_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -639,6 +650,42 @@ static void perf_event__thread_map_swap(union perf_event *event, event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid); } +static void perf_event__cpu_map_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + struct cpu_map_data *data = &event->cpu_map.data; + struct cpu_map_entries *cpus; + struct cpu_map_mask *mask; + unsigned i; + + data->type = bswap_64(data->type); + + switch (data->type) { + case PERF_CPU_MAP__CPUS: + cpus = (struct cpu_map_entries *)data->data; + + cpus->nr = bswap_16(cpus->nr); + + for (i = 0; i < cpus->nr; i++) + cpus->cpu[i] = bswap_16(cpus->cpu[i]); + break; + case PERF_CPU_MAP__MASK: + mask = (struct cpu_map_mask *) data->data; + + mask->nr = bswap_16(mask->nr); + mask->long_size = bswap_16(mask->long_size); + + switch (mask->long_size) { + case 4: mem_bswap_32(&mask->mask, mask->nr); break; + case 8: mem_bswap_64(&mask->mask, mask->nr); break; + default: + pr_err("cpu_map swap: unsupported long size\n"); + } + default: + break; + } +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -667,6 +714,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, + [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1205,6 +1253,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->auxtrace_error(tool, event, session); case PERF_RECORD_THREAD_MAP: return tool->thread_map(tool, event, session); + case PERF_RECORD_CPU_MAP: + return tool->cpu_map(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 1af4774960c3..9e5925c78519 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -56,7 +56,8 @@ struct perf_tool { id_index, auxtrace_info, auxtrace_error, - thread_map; + thread_map, + cpu_map; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- cgit v1.2.2 From 6c872901af07c41745f1abf5ceac9b3b4d9cdbb6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:24 +0100 Subject: perf cpu_map: Add cpu_map event synthesize function Introduce the perf_event__synthesize_cpu_map function to synthesize a struct cpu_map. Added generic interface: cpu_map_data__alloc cpu_map_data__synthesize to make the cpu_map synthesizing usable for other events. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 8 +++ 2 files changed, 139 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 719c0781a82a..15d6466a4b8f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -737,6 +737,137 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool, return err; } +static void synthesize_cpus(struct cpu_map_entries *cpus, + struct cpu_map *map) +{ + int i; + + cpus->nr = map->nr; + + for (i = 0; i < map->nr; i++) + cpus->cpu[i] = map->map[i]; +} + +static void synthesize_mask(struct cpu_map_mask *mask, + struct cpu_map *map, int max) +{ + int i; + + mask->nr = BITS_TO_LONGS(max); + mask->long_size = sizeof(long); + + for (i = 0; i < map->nr; i++) + set_bit(map->map[i], mask->mask); +} + +static size_t cpus_size(struct cpu_map *map) +{ + return sizeof(struct cpu_map_entries) + map->nr * sizeof(u16); +} + +static size_t mask_size(struct cpu_map *map, int *max) +{ + int i; + + *max = 0; + + for (i = 0; i < map->nr; i++) { + /* bit possition of the cpu is + 1 */ + int bit = map->map[i] + 1; + + if (bit > *max) + *max = bit; + } + + return sizeof(struct cpu_map_mask) + BITS_TO_LONGS(*max) * sizeof(long); +} + +void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max) +{ + size_t size_cpus, size_mask; + bool is_dummy = cpu_map__empty(map); + + /* + * Both array and mask data have variable size based + * on the number of cpus and their actual values. + * The size of the 'struct cpu_map_data' is: + * + * array = size of 'struct cpu_map_entries' + + * number of cpus * sizeof(u64) + * + * mask = size of 'struct cpu_map_mask' + + * maximum cpu bit converted to size of longs + * + * and finaly + the size of 'struct cpu_map_data'. + */ + size_cpus = cpus_size(map); + size_mask = mask_size(map, max); + + if (is_dummy || (size_cpus < size_mask)) { + *size += size_cpus; + *type = PERF_CPU_MAP__CPUS; + } else { + *size += size_mask; + *type = PERF_CPU_MAP__MASK; + } + + *size += sizeof(struct cpu_map_data); + return zalloc(*size); +} + +void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map, + u16 type, int max) +{ + data->type = type; + + switch (type) { + case PERF_CPU_MAP__CPUS: + synthesize_cpus((struct cpu_map_entries *) data->data, map); + break; + case PERF_CPU_MAP__MASK: + synthesize_mask((struct cpu_map_mask *) data->data, map, max); + default: + break; + }; +} + +static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map) +{ + size_t size = sizeof(struct cpu_map_event); + struct cpu_map_event *event; + int max; + u16 type; + + event = cpu_map_data__alloc(map, &size, &type, &max); + if (!event) + return NULL; + + event->header.type = PERF_RECORD_CPU_MAP; + event->header.size = size; + event->data.type = type; + + cpu_map_data__synthesize(&event->data, map, type, max); + return event; +} + +int perf_event__synthesize_cpu_map(struct perf_tool *tool, + struct cpu_map *map, + perf_event__handler_t process, + struct machine *machine) +{ + struct cpu_map_event *event; + int err; + + event = cpu_map_event__new(map); + if (!event) + return -ENOMEM; + + err = process(tool, (union perf_event *) event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1c82a0ebda73..de18ee0e9c96 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -425,6 +425,7 @@ void perf_event__print_totals(void); struct perf_tool; struct thread_map; +struct cpu_map; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -440,6 +441,10 @@ int perf_event__synthesize_thread_map2(struct perf_tool *tool, struct thread_map *threads, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_cpu_map(struct perf_tool *tool, + struct cpu_map *cpus, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool mmap_data, @@ -550,4 +555,7 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, const char *symbol_name); +void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max); +void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map, + u16 type, int max); #endif /* __PERF_RECORD_H */ -- cgit v1.2.2 From f77b57ad4fc42a074eae564bbb6660f0a3ff5503 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:25 +0100 Subject: perf cpu_map: Add cpu_map__new_event function Introducing the cpu_map__new_event function to create a struct cpu_map object from a cpu_map event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 42 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 1 + 2 files changed, 43 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 10af1e7524fb..a0717b93d8f5 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "asm/bug.h" static struct cpu_map *cpu_map__default_new(void) @@ -179,6 +180,47 @@ out: return cpus; } +static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) +{ + struct cpu_map *map; + + map = cpu_map__empty_new(cpus->nr); + if (map) { + unsigned i; + + for (i = 0; i < cpus->nr; i++) + map->map[i] = (int)cpus->cpu[i]; + } + + return map; +} + +static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask) +{ + struct cpu_map *map; + int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE; + + nr = bitmap_weight(mask->mask, nbits); + + map = cpu_map__empty_new(nr); + if (map) { + int cpu, i = 0; + + for_each_set_bit(cpu, mask->mask, nbits) + map->map[i++] = cpu; + } + return map; + +} + +struct cpu_map *cpu_map__new_data(struct cpu_map_data *data) +{ + if (data->type == PERF_CPU_MAP__CPUS) + return cpu_map__from_entries((struct cpu_map_entries *)data->data); + else + return cpu_map__from_mask((struct cpu_map_mask *)data->data); +} + size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) { int i; diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 85f7772457fa..71c41b9efabb 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -17,6 +17,7 @@ struct cpu_map { struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__empty_new(int nr); struct cpu_map *cpu_map__dummy_new(void); +struct cpu_map *cpu_map__new_data(struct cpu_map_data *data); struct cpu_map *cpu_map__read(FILE *file); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); int cpu_map__get_socket_id(int cpu); -- cgit v1.2.2 From eb12a1afdc02e59fc09934743490549c77327b1a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:26 +0100 Subject: perf cpu_map: Add perf_event__fprintf_cpu_map function To display a cpu_map event for raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 16 ++++++++++++++++ tools/perf/util/event.h | 1 + 2 files changed, 17 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 15d6466a4b8f..f31ab3b8f918 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -968,6 +968,22 @@ size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp) return ret; } +size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp) +{ + struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data); + size_t ret; + + ret = fprintf(fp, " nr: "); + + if (cpus) + ret += cpu_map__fprintf(cpus, fp); + else + ret += fprintf(fp, "failed to get cpumap from event\n"); + + cpu_map__put(cpus); + return ret; +} + int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index de18ee0e9c96..74a434116b2c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -550,6 +550,7 @@ size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp); u64 kallsyms__get_function_start(const char *kallsyms_filename, -- cgit v1.2.2 From 374fb9e362f64e730388abc1de9bb93829670a54 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:27 +0100 Subject: perf tools: Add stat config user level event Adding the stat config event to pass/store stat config data, so report tools (report/script) know how to interpret stat data. The config data is stored in a 'tag|value' way to allow for easy extension and backwards compatibility. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-12-git-send-email-jolsa@kernel.org [ stat_config_term_event -> stat_config_event_entry ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 20 ++++++++++++++++++++ tools/perf/util/session.c | 24 ++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 47 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index f31ab3b8f918..43e2dfc2c73b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -39,6 +39,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", [PERF_RECORD_CPU_MAP] = "CPU_MAP", + [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 74a434116b2c..16cee44de56b 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -228,6 +228,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_AUXTRACE_ERROR = 72, PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_CPU_MAP = 74, + PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_HEADER_MAX }; @@ -395,6 +396,24 @@ struct thread_map_event { struct thread_map_event_entry entries[]; }; +enum { + PERF_STAT_CONFIG_TERM__AGGR_MODE = 0, + PERF_STAT_CONFIG_TERM__INTERVAL = 1, + PERF_STAT_CONFIG_TERM__SCALE = 2, + PERF_STAT_CONFIG_TERM__MAX = 3, +}; + +struct stat_config_event_entry { + u64 tag; + u64 val; +}; + +struct stat_config_event { + struct perf_event_header header; + u64 nr; + struct stat_config_event_entry data[]; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -419,6 +438,7 @@ union perf_event { struct context_switch_event context_switch; struct thread_map_event thread_map; struct cpu_map_event cpu_map; + struct stat_config_event stat_config; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4350f5e85bf5..fbc52ab3eb75 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -315,6 +315,15 @@ int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, return 0; } +static +int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -369,6 +378,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->thread_map = process_event_thread_map_stub; if (tool->cpu_map == NULL) tool->cpu_map = process_event_cpu_map_stub; + if (tool->stat_config == NULL) + tool->stat_config = process_event_stat_config_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -686,6 +697,16 @@ static void perf_event__cpu_map_swap(union perf_event *event, } } +static void perf_event__stat_config_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + u64 size; + + size = event->stat_config.nr * sizeof(event->stat_config.data[0]); + size += 1; /* nr item itself */ + mem_bswap_64(&event->stat_config.nr, size); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -715,6 +736,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, + [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1255,6 +1277,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->thread_map(tool, event, session); case PERF_RECORD_CPU_MAP: return tool->cpu_map(tool, event, session); + case PERF_RECORD_STAT_CONFIG: + return tool->stat_config(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 9e5925c78519..aa7ae73d76b4 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -57,7 +57,8 @@ struct perf_tool { auxtrace_info, auxtrace_error, thread_map, - cpu_map; + cpu_map, + stat_config; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- cgit v1.2.2 From 6742434261158ad9678bf15b165304e0200cc324 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:28 +0100 Subject: perf tools: Add stat config event synthesize function Introduce the perf_event__synthesize_stat_config to synthesize a 'struct perf_stat_config'. Storing the stat config in the form of tag-value pairs will, I believe, sort out future version extensibility issues. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-13-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 40 ++++++++++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 5 +++++ 2 files changed, 45 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 43e2dfc2c73b..1ea693c2a14b 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -10,6 +10,8 @@ #include "thread.h" #include "thread_map.h" #include "symbol/kallsyms.h" +#include "asm/bug.h" +#include "stat.h" static const char *perf_event__names[] = { [0] = "TOTAL", @@ -869,6 +871,44 @@ int perf_event__synthesize_cpu_map(struct perf_tool *tool, return err; } +int perf_event__synthesize_stat_config(struct perf_tool *tool, + struct perf_stat_config *config, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_config_event *event; + int size, i = 0, err; + + size = sizeof(*event); + size += (PERF_STAT_CONFIG_TERM__MAX * sizeof(event->data[0])); + + event = zalloc(size); + if (!event) + return -ENOMEM; + + event->header.type = PERF_RECORD_STAT_CONFIG; + event->header.size = size; + event->nr = PERF_STAT_CONFIG_TERM__MAX; + +#define ADD(__term, __val) \ + event->data[i].tag = PERF_STAT_CONFIG_TERM__##__term; \ + event->data[i].val = __val; \ + i++; + + ADD(AGGR_MODE, config->aggr_mode) + ADD(INTERVAL, config->interval) + ADD(SCALE, config->scale) + + WARN_ONCE(i != PERF_STAT_CONFIG_TERM__MAX, + "stat config terms unbalanced\n"); +#undef ADD + + err = process(tool, (union perf_event *) event, NULL, machine); + + free(event); + return err; +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 16cee44de56b..39014c7c5a5b 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -446,6 +446,7 @@ void perf_event__print_totals(void); struct perf_tool; struct thread_map; struct cpu_map; +struct perf_stat_config; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -472,6 +473,10 @@ int perf_event__synthesize_threads(struct perf_tool *tool, int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_config(struct perf_tool *tool, + struct perf_stat_config *config, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, -- cgit v1.2.2 From 8e381596b67af53564a69f16440d3e5d5a73d034 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:29 +0100 Subject: perf tools: Add stat config event read function Introducing the perf_event__read_stat_config function to read a struct perf_stat_config object data from a stat config event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-14-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 24 ++++++++++++++++++++++++ tools/perf/util/event.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1ea693c2a14b..223deaf2fba7 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -909,6 +909,30 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, return err; } +void perf_event__read_stat_config(struct perf_stat_config *config, + struct stat_config_event *event) +{ + unsigned i; + + for (i = 0; i < event->nr; i++) { + + switch (event->data[i].tag) { +#define CASE(__term, __val) \ + case PERF_STAT_CONFIG_TERM__##__term: \ + config->__val = event->data[i].val; \ + break; + + CASE(AGGR_MODE, aggr_mode) + CASE(SCALE, scale) + CASE(INTERVAL, interval) +#undef CASE + default: + pr_warning("unknown stat config term %" PRIu64 "\n", + event->data[i].tag); + } + } +} + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { const char *s; diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 39014c7c5a5b..4e87be2e1afa 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -477,6 +477,8 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); +void perf_event__read_stat_config(struct perf_stat_config *config, + struct stat_config_event *event); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, -- cgit v1.2.2 From d80518c90bb2b4af9755d79af5dfe9d44e04cdb9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:30 +0100 Subject: perf tools: Add stat user level event Adding a stat event to store a 'struct perf_counter_values' for a given event/cpu/thread. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-15-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 19 +++++++++++++++++++ tools/perf/util/session.c | 25 +++++++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 47 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 223deaf2fba7..670123fee60a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -42,6 +42,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_THREAD_MAP] = "THREAD_MAP", [PERF_RECORD_CPU_MAP] = "CPU_MAP", [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", + [PERF_RECORD_STAT] = "STAT", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4e87be2e1afa..f23f464c680a 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -229,6 +229,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_THREAD_MAP = 73, PERF_RECORD_CPU_MAP = 74, PERF_RECORD_STAT_CONFIG = 75, + PERF_RECORD_STAT = 76, PERF_RECORD_HEADER_MAX }; @@ -414,6 +415,23 @@ struct stat_config_event { struct stat_config_event_entry data[]; }; +struct stat_event { + struct perf_event_header header; + + u64 id; + u32 cpu; + u32 thread; + + union { + struct { + u64 val; + u64 ena; + u64 run; + }; + u64 values[3]; + }; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -439,6 +457,7 @@ union perf_event { struct thread_map_event thread_map; struct cpu_map_event cpu_map; struct stat_config_event stat_config; + struct stat_event stat; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fbc52ab3eb75..663a2fdab42c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -324,6 +324,15 @@ int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_stat_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *perf_session + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -380,6 +389,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->cpu_map = process_event_cpu_map_stub; if (tool->stat_config == NULL) tool->stat_config = process_event_stat_config_stub; + if (tool->stat == NULL) + tool->stat = process_stat_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -707,6 +718,17 @@ static void perf_event__stat_config_swap(union perf_event *event, mem_bswap_64(&event->stat_config.nr, size); } +static void perf_event__stat_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->stat.id = bswap_64(event->stat.id); + event->stat.thread = bswap_32(event->stat.thread); + event->stat.cpu = bswap_32(event->stat.cpu); + event->stat.val = bswap_64(event->stat.val); + event->stat.ena = bswap_64(event->stat.ena); + event->stat.run = bswap_64(event->stat.run); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -737,6 +759,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap, [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, + [PERF_RECORD_STAT] = perf_event__stat_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1279,6 +1302,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->cpu_map(tool, event, session); case PERF_RECORD_STAT_CONFIG: return tool->stat_config(tool, event, session); + case PERF_RECORD_STAT: + return tool->stat(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index aa7ae73d76b4..f0b9da0c166a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -58,7 +58,8 @@ struct perf_tool { auxtrace_error, thread_map, cpu_map, - stat_config; + stat_config, + stat; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- cgit v1.2.2 From 5796f8f073fe50171376f058376dde93ec5f3785 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:31 +0100 Subject: perf tools: Add stat event synthesize function Introduce the perf_event__synthesize_stat function to synthesize a 'struct stat_event'. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-16-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 22 ++++++++++++++++++++++ tools/perf/util/event.h | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 670123fee60a..eb8243ab6ab1 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -910,6 +910,28 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, return err; } +int perf_event__synthesize_stat(struct perf_tool *tool, + u32 cpu, u32 thread, u64 id, + struct perf_counts_values *count, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_event event; + + event.header.type = PERF_RECORD_STAT; + event.header.size = sizeof(event); + event.header.misc = 0; + + event.id = id; + event.cpu = cpu; + event.thread = thread; + event.val = count->val; + event.ena = count->ena; + event.run = count->run; + + return process(tool, (union perf_event *) &event, NULL, machine); +} + void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event) { diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index f23f464c680a..336eb44babf8 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -466,6 +466,7 @@ struct perf_tool; struct thread_map; struct cpu_map; struct perf_stat_config; +struct perf_counts_values; typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, @@ -498,7 +499,11 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, struct machine *machine); void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event); - +int perf_event__synthesize_stat(struct perf_tool *tool, + u32 cpu, u32 thread, u64 id, + struct perf_counts_values *count, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -- cgit v1.2.2 From 0ea0e3558607626196eb09ace796aac585e61f5c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:32 +0100 Subject: perf tools: Add stat event read function Introducing the perf_event__process_stat_event function to process a 'struct perf_stat' data from a stat event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-17-git-send-email-jolsa@kernel.org [ Renamed 'stat' parameter to 'st' to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat.c | 23 +++++++++++++++++++++++ tools/perf/util/stat.h | 6 ++++++ 2 files changed, 29 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 2d9d8306dbd3..0ad59cea318c 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -341,3 +341,26 @@ int perf_stat_process_counter(struct perf_stat_config *config, return 0; } + +int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct perf_counts_values count; + struct stat_event *st = &event->stat; + struct perf_evsel *counter; + + count.val = st->val; + count.ena = st->ena; + count.run = st->run; + + counter = perf_evlist__id2evsel(session->evlist, st->id); + if (!counter) { + pr_err("Failed to resolve counter for stat event.\n"); + return -EINVAL; + } + + *perf_counts(counter->counts, st->cpu, st->thread) = count; + counter->supported = true; + return 0; +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index da1d11c4f8c1..afe6844e5219 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -90,4 +90,10 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist); int perf_stat_process_counter(struct perf_stat_config *config, struct perf_evsel *counter); +struct perf_tool; +union perf_event; +struct perf_session; +int perf_event__process_stat_event(struct perf_tool *tool, + union perf_event *event, + struct perf_session *session); #endif -- cgit v1.2.2 From 2d8f0f18a5c37cf0322cb385b99adb1167b7cf78 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:33 +0100 Subject: perf tools: Add stat round user level event Adding the stat round event to be stored after each stat interval round, so that report tools (report/script) gets notified and process interval data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-18-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 13 +++++++++++++ tools/perf/util/session.c | 21 +++++++++++++++++++++ tools/perf/util/tool.h | 3 ++- 4 files changed, 37 insertions(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index eb8243ab6ab1..725db548c7d5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -43,6 +43,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_CPU_MAP] = "CPU_MAP", [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", [PERF_RECORD_STAT] = "STAT", + [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 336eb44babf8..5eb4f55a141d 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -230,6 +230,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_CPU_MAP = 74, PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_STAT = 76, + PERF_RECORD_STAT_ROUND = 77, PERF_RECORD_HEADER_MAX }; @@ -432,6 +433,17 @@ struct stat_event { }; }; +enum { + PERF_STAT_ROUND_TYPE__INTERVAL = 0, + PERF_STAT_ROUND_TYPE__FINAL = 1, +}; + +struct stat_round_event { + struct perf_event_header header; + u64 type; + u64 time; +}; + union perf_event { struct perf_event_header header; struct mmap_event mmap; @@ -458,6 +470,7 @@ union perf_event { struct cpu_map_event cpu_map; struct stat_config_event stat_config; struct stat_event stat; + struct stat_round_event stat_round; }; void perf_event__print_totals(void); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 663a2fdab42c..5b3a81a6b795 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -333,6 +333,15 @@ static int process_stat_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_stat_round_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *perf_session + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) @@ -391,6 +400,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->stat_config = process_event_stat_config_stub; if (tool->stat == NULL) tool->stat = process_stat_stub; + if (tool->stat_round == NULL) + tool->stat_round = process_stat_round_stub; } static void swap_sample_id_all(union perf_event *event, void *data) @@ -729,6 +740,13 @@ static void perf_event__stat_swap(union perf_event *event, event->stat.run = bswap_64(event->stat.run); } +static void perf_event__stat_round_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->stat_round.type = bswap_64(event->stat_round.type); + event->stat_round.time = bswap_64(event->stat_round.time); +} + typedef void (*perf_event__swap_op)(union perf_event *event, bool sample_id_all); @@ -760,6 +778,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap, [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_STAT] = perf_event__stat_swap, + [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1304,6 +1323,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, return tool->stat_config(tool, event, session); case PERF_RECORD_STAT: return tool->stat(tool, event, session); + case PERF_RECORD_STAT_ROUND: + return tool->stat_round(tool, event, session); default: return -EINVAL; } diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index f0b9da0c166a..d04d9e5f444a 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -59,7 +59,8 @@ struct perf_tool { thread_map, cpu_map, stat_config, - stat; + stat, + stat_round; event_op3 auxtrace; bool ordered_events; bool ordering_requires_timestamps; -- cgit v1.2.2 From d4c2259195f538505d2570e78555532372fb4ad2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:34 +0100 Subject: perf tools: Add stat round event synthesize function Introduce the perf_event__synthesize_stat_round function to synthesize a 'struct stat_round_event'. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-19-git-send-email-jolsa@kernel.org [ Renamed 'time' parameter to 'evtime' to fix build on older systems ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 17 +++++++++++++++++ tools/perf/util/event.h | 4 ++++ 2 files changed, 21 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 725db548c7d5..e4c68ba79974 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -933,6 +933,23 @@ int perf_event__synthesize_stat(struct perf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } +int perf_event__synthesize_stat_round(struct perf_tool *tool, + u64 evtime, u64 type, + perf_event__handler_t process, + struct machine *machine) +{ + struct stat_round_event event; + + event.header.type = PERF_RECORD_STAT_ROUND; + event.header.size = sizeof(event); + event.header.misc = 0; + + event.time = evtime; + event.type = type; + + return process(tool, (union perf_event *) &event, NULL, machine); +} + void perf_event__read_stat_config(struct perf_stat_config *config, struct stat_config_event *event) { diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 5eb4f55a141d..1afaa21eeebe 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -517,6 +517,10 @@ int perf_event__synthesize_stat(struct perf_tool *tool, struct perf_counts_values *count, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_round(struct perf_tool *tool, + u64 time, u64 type, + perf_event__handler_t process, + struct machine *machine); int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -- cgit v1.2.2 From e08a4564e23f8f89a055d717887674f54a9da515 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:35 +0100 Subject: perf tools: Add stat events fprintf functions Introducing the following functions to display the stat events for raw dump. perf_event__fprintf_stat perf_event__fprintf_stat_round perf_event__fprintf_stat_config Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-20-git-send-email-jolsa@kernel.org [ s/stat/st/g and s/round/rd/g parameters to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat.c | 39 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/stat.h | 4 ++++ 2 files changed, 43 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 0ad59cea318c..2f901d15e063 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c @@ -364,3 +364,42 @@ int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused, counter->supported = true; return 0; } + +size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp) +{ + struct stat_event *st = (struct stat_event *) event; + size_t ret; + + ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n", + st->id, st->cpu, st->thread); + ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n", + st->val, st->ena, st->run); + + return ret; +} + +size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp) +{ + struct stat_round_event *rd = (struct stat_round_event *)event; + size_t ret; + + ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time, + rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL"); + + return ret; +} + +size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp) +{ + struct perf_stat_config sc; + size_t ret; + + perf_event__read_stat_config(&sc, &event->stat_config); + + ret = fprintf(fp, "\n"); + ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode); + ret += fprintf(fp, "... scale %d\n", sc.scale); + ret += fprintf(fp, "... interval %u\n", sc.interval); + + return ret; +} diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index afe6844e5219..086f4e128d63 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h @@ -96,4 +96,8 @@ struct perf_session; int perf_event__process_stat_event(struct perf_tool *tool, union perf_event *event, struct perf_session *session); + +size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp); +size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp); #endif -- cgit v1.2.2 From ffe777254cce24fb5fde3f0aa91fc755cfb1b812 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:36 +0100 Subject: perf tools: Add event_update user level event It'll serve as a base event for additional event attributes details, that are not part of the attr event. At the moment this event is just a dummy one without any specific functionality. The type value will distinguish the update event details. It'll come in the following patches. The idea for this event is to be extensible for any update that the event might need in the future. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-21-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + tools/perf/util/event.h | 10 ++++++++++ tools/perf/util/header.c | 20 ++++++++++++++++++++ tools/perf/util/header.h | 3 +++ tools/perf/util/session.c | 21 +++++++++++++++++++++ tools/perf/util/tool.h | 1 + 6 files changed, 56 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index e4c68ba79974..cd61bb1f3917 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -44,6 +44,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG", [PERF_RECORD_STAT] = "STAT", [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", + [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", }; const char *perf_event__name(unsigned int id) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1afaa21eeebe..6966a4b7c0f0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -231,6 +231,7 @@ enum perf_user_event_type { /* above any possible kernel type */ PERF_RECORD_STAT_CONFIG = 75, PERF_RECORD_STAT = 76, PERF_RECORD_STAT_ROUND = 77, + PERF_RECORD_EVENT_UPDATE = 78, PERF_RECORD_HEADER_MAX }; @@ -307,6 +308,14 @@ struct attr_event { u64 id[]; }; +struct event_update_event { + struct perf_event_header header; + u64 type; + u64 id; + + char data[]; +}; + #define MAX_EVENT_NAME 64 struct perf_trace_event_type { @@ -456,6 +465,7 @@ union perf_event { struct throttle_event throttle; struct sample_event sample; struct attr_event attr; + struct event_update_event event_update; struct event_type_event event_type; struct tracing_data_event tracing_data; struct build_id_event build_id; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5ac7bdb0dff7..6b4e00220a5a 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2745,6 +2745,26 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, return 0; } +int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_evlist **pevlist) +{ + struct event_update_event *ev = &event->event_update; + struct perf_evlist *evlist; + struct perf_evsel *evsel; + + if (!pevlist || *pevlist == NULL) + return -EINVAL; + + evlist = *pevlist; + + evsel = perf_evlist__id2evsel(evlist, ev->id); + if (evsel == NULL) + return -EINVAL; + + return 0; +} + int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, perf_event__handler_t process) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 05f27cb6b7e3..1e843c67a4ff 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -107,6 +107,9 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); +int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_evlist **pevlist); int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5b3a81a6b795..49e5cdc4cc5a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -205,6 +205,15 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, return 0; } +static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_evlist **pevlist + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, @@ -374,6 +383,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->unthrottle = process_event_stub; if (tool->attr == NULL) tool->attr = process_event_synth_attr_stub; + if (tool->event_update == NULL) + tool->event_update = process_event_synth_event_update_stub; if (tool->tracing_data == NULL) tool->tracing_data = process_event_synth_tracing_data_stub; if (tool->build_id == NULL) @@ -625,6 +636,13 @@ static void perf_event__hdr_attr_swap(union perf_event *event, mem_bswap_64(event->attr.id, size); } +static void perf_event__event_update_swap(union perf_event *event, + bool sample_id_all __maybe_unused) +{ + event->event_update.type = bswap_64(event->event_update.type); + event->event_update.id = bswap_64(event->event_update.id); +} + static void perf_event__event_type_swap(union perf_event *event, bool sample_id_all __maybe_unused) { @@ -779,6 +797,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap, [PERF_RECORD_STAT] = perf_event__stat_swap, [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, + [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, [PERF_RECORD_HEADER_MAX] = NULL, }; @@ -1290,6 +1309,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, perf_session__set_comm_exec(session); } return err; + case PERF_RECORD_EVENT_UPDATE: + return tool->event_update(tool, event, &session->evlist); case PERF_RECORD_HEADER_EVENT_TYPE: /* * Depreceated, but we need to handle it for sake diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index d04d9e5f444a..55de4cffcd4e 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -50,6 +50,7 @@ struct perf_tool { throttle, unthrottle; event_attr_op attr; + event_attr_op event_update; event_op2 tracing_data; event_oe finished_round; event_op2 build_id, -- cgit v1.2.2 From a6e5281780d1da65c15ce529707f43eb4a6df856 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:37 +0100 Subject: perf tools: Add event_update event unit type Adding unit type 'event update' event, that stores/transfer events unit name. The unit name is part of the perf stat output data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-22-git-send-email-jolsa@kernel.org [ Rename __alloc() to __new() for consistency ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 4 ++++ tools/perf/util/header.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 3 files changed, 51 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6966a4b7c0f0..64c4cdf5aada 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -308,6 +308,10 @@ struct attr_event { u64 id[]; }; +enum { + PERF_EVENT_UPDATE__UNIT = 0, +}; + struct event_update_event { struct perf_event_header header; u64 type; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 6b4e00220a5a..5759ebfde666 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2686,6 +2686,43 @@ int perf_event__synthesize_attr(struct perf_tool *tool, return err; } +static struct event_update_event * +event_update_event__new(size_t size, u64 type, u64 id) +{ + struct event_update_event *ev; + + size += sizeof(*ev); + size = PERF_ALIGN(size, sizeof(u64)); + + ev = zalloc(size); + if (ev) { + ev->header.type = PERF_RECORD_EVENT_UPDATE; + ev->header.size = (u16)size; + ev->type = type; + ev->id = id; + } + return ev; +} + +int +perf_event__synthesize_event_update_unit(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + size_t size = strlen(evsel->unit); + int err; + + ev = event_update_event__new(size + 1, PERF_EVENT_UPDATE__UNIT, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + strncpy(ev->data, evsel->unit, size); + err = process(tool, (union perf_event *)ev, NULL, NULL); + free(ev); + return err; +} + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2762,6 +2799,13 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, if (evsel == NULL) return -EINVAL; + switch (ev->type) { + case PERF_EVENT_UPDATE__UNIT: + evsel->unit = strdup(ev->data); + default: + break; + } + return 0; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1e843c67a4ff..6aa2b9242fc1 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -105,6 +105,9 @@ int perf_event__synthesize_attr(struct perf_tool *tool, int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process); +int perf_event__synthesize_event_update_unit(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, -- cgit v1.2.2 From daeecbc0c431f15f492fb8d704080a02de6e2918 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:38 +0100 Subject: perf tools: Add event_update event scale type A__allocdding scale type 'event update' event, that stores/transfer events scale value. The PMU events can define the scale value which is used to multiply events data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-23-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 5 +++++ tools/perf/util/header.c | 26 ++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 3 files changed, 34 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 64c4cdf5aada..44198e8550e4 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -310,6 +310,11 @@ struct attr_event { enum { PERF_EVENT_UPDATE__UNIT = 0, + PERF_EVENT_UPDATE__SCALE = 1, +}; + +struct event_update_event_scale { + double scale; }; struct event_update_event { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 5759ebfde666..30edb4ba258e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2723,6 +2723,27 @@ perf_event__synthesize_event_update_unit(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_scale(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + struct event_update_event_scale *ev_data; + int err; + + ev = event_update_event__new(sizeof(*ev_data), PERF_EVENT_UPDATE__SCALE, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + ev_data = (struct event_update_event_scale *) ev->data; + ev_data->scale = evsel->scale; + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} + + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2787,6 +2808,7 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, struct perf_evlist **pevlist) { struct event_update_event *ev = &event->event_update; + struct event_update_event_scale *ev_scale; struct perf_evlist *evlist; struct perf_evsel *evsel; @@ -2802,6 +2824,10 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, switch (ev->type) { case PERF_EVENT_UPDATE__UNIT: evsel->unit = strdup(ev->data); + break; + case PERF_EVENT_UPDATE__SCALE: + ev_scale = (struct event_update_event_scale *) ev->data; + evsel->scale = ev_scale->scale; default: break; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 6aa2b9242fc1..fad04cbab666 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -108,6 +108,9 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, int perf_event__synthesize_event_update_unit(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_scale(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, -- cgit v1.2.2 From 802c9048b824eaa3c75d875e2d107460ad586439 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:39 +0100 Subject: perf tools: Add event_update event name type Adding name type 'event update' event, that stores/transfer events name. Event's name is stored within perf.data's EVENT_DESC feature, but we don't have it if we get the report data from pipe. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-24-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 1 + tools/perf/util/header.c | 21 +++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 3 files changed, 25 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 44198e8550e4..235196b575d6 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -311,6 +311,7 @@ struct attr_event { enum { PERF_EVENT_UPDATE__UNIT = 0, PERF_EVENT_UPDATE__SCALE = 1, + PERF_EVENT_UPDATE__NAME = 2, }; struct event_update_event_scale { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 30edb4ba258e..cd3d005a34af 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2743,6 +2743,24 @@ perf_event__synthesize_event_update_scale(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_name(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + struct event_update_event *ev; + size_t len = strlen(evsel->name); + int err; + + ev = event_update_event__new(len + 1, PERF_EVENT_UPDATE__NAME, evsel->id[0]); + if (ev == NULL) + return -ENOMEM; + + strncpy(ev->data, evsel->name, len); + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, @@ -2825,6 +2843,9 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, case PERF_EVENT_UPDATE__UNIT: evsel->unit = strdup(ev->data); break; + case PERF_EVENT_UPDATE__NAME: + evsel->name = strdup(ev->data); + break; case PERF_EVENT_UPDATE__SCALE: ev_scale = (struct event_update_event_scale *) ev->data; evsel->scale = ev_scale->scale; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index fad04cbab666..51cf566a0835 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -111,6 +111,9 @@ int perf_event__synthesize_event_update_unit(struct perf_tool *tool, int perf_event__synthesize_event_update_scale(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_name(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, -- cgit v1.2.2 From 86ebb09f96fe6886e1e5d53b648df5537ba859ca Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:40 +0100 Subject: perf tools: Add event_update event cpus type Adding the cpumask 'event update' event, that stores/transfer the cpumask for a event. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-25-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.h | 5 +++++ tools/perf/util/header.c | 42 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 3 files changed, 50 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 235196b575d6..b7ffb7ee9971 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -312,6 +312,11 @@ enum { PERF_EVENT_UPDATE__UNIT = 0, PERF_EVENT_UPDATE__SCALE = 1, PERF_EVENT_UPDATE__NAME = 2, + PERF_EVENT_UPDATE__CPUS = 3, +}; + +struct event_update_event_cpus { + struct cpu_map_data cpus; }; struct event_update_event_scale { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index cd3d005a34af..79d3eb984db7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2762,6 +2762,38 @@ perf_event__synthesize_event_update_name(struct perf_tool *tool, return err; } +int +perf_event__synthesize_event_update_cpus(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process) +{ + size_t size = sizeof(struct event_update_event); + struct event_update_event *ev; + int max, err; + u16 type; + + if (!evsel->own_cpus) + return 0; + + ev = cpu_map_data__alloc(evsel->own_cpus, &size, &type, &max); + if (!ev) + return -ENOMEM; + + ev->header.type = PERF_RECORD_EVENT_UPDATE; + ev->header.size = (u16)size; + ev->type = PERF_EVENT_UPDATE__CPUS; + ev->id = evsel->id[0]; + + cpu_map_data__synthesize((struct cpu_map_data *) ev->data, + evsel->own_cpus, + type, max); + + err = process(tool, (union perf_event*) ev, NULL, NULL); + free(ev); + return err; +} + + int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) @@ -2827,8 +2859,10 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, { struct event_update_event *ev = &event->event_update; struct event_update_event_scale *ev_scale; + struct event_update_event_cpus *ev_cpus; struct perf_evlist *evlist; struct perf_evsel *evsel; + struct cpu_map *map; if (!pevlist || *pevlist == NULL) return -EINVAL; @@ -2849,6 +2883,14 @@ int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, case PERF_EVENT_UPDATE__SCALE: ev_scale = (struct event_update_event_scale *) ev->data; evsel->scale = ev_scale->scale; + case PERF_EVENT_UPDATE__CPUS: + ev_cpus = (struct event_update_event_cpus *) ev->data; + + map = cpu_map__new_data(&ev_cpus->cpus); + if (map) + evsel->own_cpus = map; + else + pr_err("failed to get event_update cpus\n"); default: break; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 51cf566a0835..a1bc0c5706a3 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -114,6 +114,9 @@ int perf_event__synthesize_event_update_scale(struct perf_tool *tool, int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct perf_evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, + struct perf_evsel *evsel, + perf_event__handler_t process); int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist); int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, -- cgit v1.2.2 From c853f9394b7bc189632673cac802bdbf6537463b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:41 +0100 Subject: perf tools: Add perf_event__fprintf_event_update function To display a 'event update' event for raw dump. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-26-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 38 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 1 + 2 files changed, 39 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 79d3eb984db7..49676c14c8ae 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2793,6 +2793,44 @@ perf_event__synthesize_event_update_cpus(struct perf_tool *tool, return err; } +size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) +{ + struct event_update_event *ev = &event->event_update; + struct event_update_event_scale *ev_scale; + struct event_update_event_cpus *ev_cpus; + struct cpu_map *map; + size_t ret; + + ret = fprintf(fp, "\n... id: %" PRIu64 "\n", ev->id); + + switch (ev->type) { + case PERF_EVENT_UPDATE__SCALE: + ev_scale = (struct event_update_event_scale *) ev->data; + ret += fprintf(fp, "... scale: %f\n", ev_scale->scale); + break; + case PERF_EVENT_UPDATE__UNIT: + ret += fprintf(fp, "... unit: %s\n", ev->data); + break; + case PERF_EVENT_UPDATE__NAME: + ret += fprintf(fp, "... name: %s\n", ev->data); + break; + case PERF_EVENT_UPDATE__CPUS: + ev_cpus = (struct event_update_event_cpus *) ev->data; + ret += fprintf(fp, "... "); + + map = cpu_map__new_data(&ev_cpus->cpus); + if (map) + ret += cpu_map__fprintf(map, fp); + else + ret += fprintf(fp, "failed to get cpus\n"); + break; + default: + ret += fprintf(fp, "... unknown type\n"); + break; + } + + return ret; +} int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_session *session, diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a1bc0c5706a3..710deecf8f80 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -122,6 +122,7 @@ int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_evlist **pevlist); +size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct perf_evlist *evlist, -- cgit v1.2.2 From 2d2aea6ae736503d3896c4997b494760ed8febc1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:42 +0100 Subject: perf report: Display newly added events in raw dump The 'perf report -D' command will now display detailed output for these newly added events: event_update thread_map cpu_map stat stat_config stat_round Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-27-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 49e5cdc4cc5a..a90c74b67e43 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -17,6 +17,7 @@ #include "asm/bug.h" #include "auxtrace.h" #include "thread-stack.h" +#include "stat.h" static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, @@ -210,6 +211,9 @@ static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_ struct perf_evlist **pevlist __maybe_unused) { + if (dump_trace) + perf_event__fprintf_event_update(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -311,6 +315,9 @@ int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_thread_map(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -320,6 +327,9 @@ int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_cpu_map(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -329,6 +339,9 @@ int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_session *session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat_config(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -338,6 +351,9 @@ static int process_stat_stub(struct perf_tool *tool __maybe_unused, struct perf_session *perf_session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat(event, stdout); + dump_printf(": unhandled!\n"); return 0; } @@ -347,6 +363,9 @@ static int process_stat_round_stub(struct perf_tool *tool __maybe_unused, struct perf_session *perf_session __maybe_unused) { + if (dump_trace) + perf_event__fprintf_stat_round(event, stdout); + dump_printf(": unhandled!\n"); return 0; } -- cgit v1.2.2 From ffa517adf625fa6a6c168285534e1ff7344fa2f1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:43 +0100 Subject: perf tools: Introduce stat perf.data header feature Introducing the 'stat' feature to mark a perf.data as created by the 'perf stat record' command. It contains no data. It's needed so that the report tools (report/script) can differentiate sampling data from counting data, because they need to be treated in a different way. In the future it might be used to store the version of the stat storage system used. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1445784728-21732-28-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 14 ++++++++++++++ tools/perf/util/header.h | 1 + 2 files changed, 15 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 49676c14c8ae..f50b7235ecb6 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -868,6 +868,13 @@ static int write_auxtrace(int fd, struct perf_header *h, return err; } +static int write_stat(int fd __maybe_unused, + struct perf_header *h __maybe_unused, + struct perf_evlist *evlist __maybe_unused) +{ + return 0; +} + static void print_hostname(struct perf_header *ph, int fd __maybe_unused, FILE *fp) { @@ -1159,6 +1166,12 @@ static void print_auxtrace(struct perf_header *ph __maybe_unused, fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n"); } +static void print_stat(struct perf_header *ph __maybe_unused, + int fd __maybe_unused, FILE *fp) +{ + fprintf(fp, "# contains stat data\n"); +} + static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, FILE *fp) { @@ -1948,6 +1961,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), FEAT_OPP(HEADER_GROUP_DESC, group_desc), FEAT_OPP(HEADER_AUXTRACE, auxtrace), + FEAT_OPA(HEADER_STAT, stat), }; struct header_print_data { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 710deecf8f80..cff9892452ee 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -31,6 +31,7 @@ enum { HEADER_PMU_MAPPINGS, HEADER_GROUP_DESC, HEADER_AUXTRACE, + HEADER_STAT, HEADER_LAST_FEATURE, HEADER_FEAT_BITS = 256, }; -- cgit v1.2.2 From 3ba78bd00e508bf46a6aa2b8e296dc8287ea4c29 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:47 +0100 Subject: perf stat record: Initialize record features Disabling all non stat related features. Also as we now enable STAT feature in the data file, adding code to instruct session open to skip sample type checking for stat data files. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a90c74b67e43..d5636ba94b20 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -37,6 +37,9 @@ static int perf_session__open(struct perf_session *session) if (perf_data_file__is_pipe(file)) return 0; + if (perf_header__has_feat(&session->header, HEADER_STAT)) + return 0; + if (!perf_evlist__valid_sample_type(session->evlist)) { pr_err("non matching sample_type\n"); return -1; -- cgit v1.2.2 From 1c59612de0264790698e32eb0368daf3fcba4c65 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: perf evlist: Export id_add_fd() Will be used to storing the event IDs in evlist object so it get stored into perf.data file. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-6-git-send-email-jolsa@kernel.org [ Split from the patch storing the ids in the perf.data file ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 6 +++--- tools/perf/util/evlist.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 8c44aadb9810..b9eac0daa0b9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -534,9 +534,9 @@ void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, evsel->id[evsel->ids++] = id; } -static int perf_evlist__id_add_fd(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, int fd) +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd) { u64 read_data[4] = { 0, }; int id_idx = 1; /* The first entry is the counter value */ diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index a459fe71b452..139a50038097 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -97,6 +97,9 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, int cpu, int thread, u64 id); +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd); int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); -- cgit v1.2.2 From fd36f3dd79331b9610664b867ff205465bf9ce68 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:06:58 +0900 Subject: perf hist: Pass struct sample to __hists__add_entry() This is a preparation to add more info into the hist_entry. Also it already passes too many argument, so passing sample directly will reduce the overhead of the function call. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 31 +++++++++++++++++-------------- tools/perf/util/hist.h | 4 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 56e97f5af598..039bb91d0a92 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -461,7 +461,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct symbol *sym_parent, struct branch_info *bi, struct mem_info *mi, - u64 period, u64 weight, u64 transaction, + struct perf_sample *sample, bool sample_self) { struct hist_entry entry = { @@ -478,15 +478,15 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .level = al->level, .stat = { .nr_events = 1, - .period = period, - .weight = weight, + .period = sample->period, + .weight = sample->weight, }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent) | al->filtered, .hists = hists, .branch_info = bi, .mem_info = mi, - .transaction = transaction, + .transaction = sample->transaction, }; return hists__findnew_entry(hists, &entry, al, sample_self); @@ -526,12 +526,13 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al u64 cost; struct mem_info *mi = iter->priv; struct hists *hists = evsel__hists(iter->evsel); + struct perf_sample *sample = iter->sample; struct hist_entry *he; if (mi == NULL) return -EINVAL; - cost = iter->sample->weight; + cost = sample->weight; if (!cost) cost = 1; @@ -542,8 +543,10 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al * and this is indirectly achieved by passing period=weight here * and the he_stat__add_period() function. */ + sample->period = cost; + he = __hists__add_entry(hists, al, iter->parent, NULL, mi, - cost, cost, 0, true); + sample, true); if (!he) return -ENOMEM; @@ -630,6 +633,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a struct branch_info *bi; struct perf_evsel *evsel = iter->evsel; struct hists *hists = evsel__hists(evsel); + struct perf_sample *sample = iter->sample; struct hist_entry *he = NULL; int i = iter->curr; int err = 0; @@ -643,9 +647,11 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a * The report shows the percentage of total branches captured * and not events sampled. Thus we use a pseudo period of 1. */ + sample->period = 1; + sample->weight = bi->flags.cycles ? bi->flags.cycles : 1; + he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, - 1, bi->flags.cycles ? bi->flags.cycles : 1, - 0, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -682,8 +688,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location struct hist_entry *he; he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -744,8 +749,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, int err = 0; he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, true); + sample, true); if (he == NULL) return -ENOMEM; @@ -818,8 +822,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, } he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, - sample->period, sample->weight, - sample->transaction, false); + sample, false); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index a48a2078d288..36439bfad059 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -114,8 +114,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *parent, struct branch_info *bi, - struct mem_info *mi, u64 period, - u64 weight, u64 transaction, + struct mem_info *mi, + struct perf_sample *sample, bool sample_self); int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, int max_stack_depth, void *arg); -- cgit v1.2.2 From 723928340c9d28d92dcaff8b8fbc9100a1cf9429 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Dec 2015 11:16:17 +0900 Subject: perf hist: Save raw_data/size for tracepoint events The raw_data and raw_size fields are to provide tracepoint specific information. They will be used by dynamic sort keys later. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450923377-18641-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 24 ++++++++++++++++++++++++ tools/perf/util/sort.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 039bb91d0a92..2dcf38a78f08 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -369,6 +369,25 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, if (symbol_conf.use_callchain) callchain_init(he->callchain); + if (he->raw_data) { + he->raw_data = memdup(he->raw_data, he->raw_size); + + if (he->raw_data == NULL) { + map__put(he->ms.map); + if (he->branch_info) { + map__put(he->branch_info->from.map); + map__put(he->branch_info->to.map); + free(he->branch_info); + } + if (he->mem_info) { + map__put(he->mem_info->iaddr.map); + map__put(he->mem_info->daddr.map); + } + free(he->stat_acc); + free(he); + return NULL; + } + } INIT_LIST_HEAD(&he->pairs.node); thread__get(he->thread); } @@ -487,6 +506,8 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .branch_info = bi, .mem_info = mi, .transaction = sample->transaction, + .raw_data = sample->raw_data, + .raw_size = sample->raw_size, }; return hists__findnew_entry(hists, &entry, al, sample_self); @@ -801,6 +822,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, .sym = al->sym, }, .parent = iter->parent, + .raw_data = sample->raw_data, + .raw_size = sample->raw_size, }; int i; struct callchain_cursor cursor; @@ -974,6 +997,7 @@ void hist_entry__delete(struct hist_entry *he) if (he->srcfile && he->srcfile[0]) free(he->srcfile); free_callchain(he->callchain); + free(he->raw_data); free(he); } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 86f05e7a5566..d29898708dbd 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -122,6 +122,8 @@ struct hist_entry { struct branch_info *branch_info; struct hists *hists; struct mem_info *mem_info; + void *raw_data; + u32 raw_size; struct callchain_root callchain[0]; /* must be last member */ }; -- cgit v1.2.2 From 40184c46a3055a97e2efa69da6f17c05bff4b776 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:01 +0900 Subject: perf tools: Pass evlist to setup_sorting() This is a preparation to support dynamic sort keys for tracepoint events. Dynamic sort keys can be created for specific fields in trace events so it needs the event information. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-5-git-send-email-namhyung@kernel.org [ Moving the evlist creation earlier in top was split to a previous patch ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 15 +++++++++------ tools/perf/util/sort.h | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2d8ccd4d9e1b..0c038a27fe5c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -4,6 +4,8 @@ #include "comm.h" #include "symbol.h" #include "evsel.h" +#include "evlist.h" +#include regex_t parent_regex; const char default_parent_pattern[] = "^sys_|^do_page_fault"; @@ -1583,7 +1585,8 @@ int hpp_dimension__add_output(unsigned col) return __hpp_dimension__add_output(&hpp_sort_dimensions[col]); } -int sort_dimension__add(const char *tok) +static int sort_dimension__add(const char *tok, + struct perf_evlist *evlist __maybe_unused) { unsigned int i; @@ -1712,7 +1715,7 @@ static int setup_sort_order(void) return 0; } -static int __setup_sorting(void) +static int __setup_sorting(struct perf_evlist *evlist) { char *tmp, *tok, *str; const char *sort_keys; @@ -1743,7 +1746,7 @@ static int __setup_sorting(void) for (tok = strtok_r(str, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { - ret = sort_dimension__add(tok); + ret = sort_dimension__add(tok, evlist); if (ret == -EINVAL) { error("Invalid --sort key: `%s'", tok); break; @@ -1954,16 +1957,16 @@ out: return ret; } -int setup_sorting(void) +int setup_sorting(struct perf_evlist *evlist) { int err; - err = __setup_sorting(); + err = __setup_sorting(evlist); if (err < 0) return err; if (parent_pattern != default_parent_pattern) { - err = sort_dimension__add("parent"); + err = sort_dimension__add("parent", evlist); if (err < 0) return err; } diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index d29898708dbd..1a00f1eb9d21 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -226,10 +226,11 @@ struct sort_entry { extern struct sort_entry sort_thread; extern struct list_head hist_entry__sort_list; -int setup_sorting(void); +struct perf_evlist; +struct pevent; +int setup_sorting(struct perf_evlist *evlist); int setup_output_field(void); void reset_output_field(void); -extern int sort_dimension__add(const char *); void sort__setup_elide(FILE *fp); void perf_hpp__set_elide(int idx, bool elide); -- cgit v1.2.2 From c7c2a5e40f17ab3b14716d4f08d03792a9b683e7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:02 +0900 Subject: perf tools: Add dynamic sort key for tracepoint events The existing sort keys are less useful for tracepoint events in that they are always sampled at the same place, the function where the tracepoint is located. For example, a 'perf report' on sched:sched_switch event looks like the following: # Overhead Command Shared Object Symbol # ........ ............... ................ .............. # 47.22% swapper [kernel.vmlinux] [k] __schedule 21.67% transmission-gt [kernel.vmlinux] [k] __schedule 8.23% netctl-auto [kernel.vmlinux] [k] __schedule 5.53% kworker/0:1H [kernel.vmlinux] [k] __schedule 1.98% Xephyr [kernel.vmlinux] [k] __schedule 1.33% irq/33-iwlwifi [kernel.vmlinux] [k] __schedule 1.17% wpa_cli [kernel.vmlinux] [k] __schedule 1.13% rcu_preempt [kernel.vmlinux] [k] __schedule 0.85% ksoftirqd/0 [kernel.vmlinux] [k] __schedule 0.77% Timer [kernel.vmlinux] [k] __schedule In fact, tracepoints have meaningful information in their fields but there's no way to use in 'perf report' currently. The dynamic sort keys are introduced in this patc to overcome this limitation. The sched:sched_switch events have following fields: # sudo cat /sys/kernel/debug/tracing/events/sched/sched_switch/format name: sched_switch ID: 268 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:char prev_comm[16]; offset:8; size:16; signed:1; field:pid_t prev_pid; offset:24; size:4; signed:1; field:int prev_prio; offset:28; size:4; signed:1; field:long prev_state; offset:32; size:8; signed:1; field:char next_comm[16]; offset:40; size:16; signed:1; field:pid_t next_pid; offset:56; size:4; signed:1; field:int next_prio; offset:60; size:4; signed:1; print fmt: "prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d", REC->prev_comm, REC->prev_pid, REC->prev_prio, REC->prev_state & (2048-1) ? __print_flags(REC->prev_state & (2048-1), "|", { 1, "S"} , { 2, "D" }, { 4, "T" }, { 8, "t" }, { 16, "Z" }, { 32, "X" }, { 64, "x" }, { 128, "K"}, { 256, "W" }, { 512, "P" }, { 1024, "N" }) : "R", REC->prev_state & 2048 ? "+" : "", REC->next_comm, REC->next_pid, REC->next_prio With dynamic sort keys, you can use as a sort key. Those dynamic keys are checked and created on demand. For instance, below is to sort by next_pid field output on the same data file: $ perf report -s comm,sched:sched_switch.next_pid --stdio ... # Overhead Command next_pid # ........ ............... .......... # 21.23% transmission-gt 0 20.86% swapper 17773 6.62% netctl-auto 0 5.25% swapper 109 5.21% kworker/0:1H 0 1.98% Xephyr 0 1.98% swapper 6524 1.98% swapper 27478 1.37% swapper 27476 1.17% swapper 233 Multiple dynamic sort keys are also supported: $ perf report -s comm,sched:sched_switch.next_pid,sched:sched_switch.next_comm --stdio ... # Overhead Command next_pid next_comm # ........ ............... .......... ................ # 20.86% swapper 17773 transmission-gt 9.64% transmission-gt 0 swapper/0 9.16% transmission-gt 0 swapper/2 5.25% swapper 109 kworker/0:1H 5.21% kworker/0:1H 0 swapper/0 2.14% netctl-auto 0 swapper/2 1.98% netctl-auto 0 swapper/0 1.98% swapper 6524 Xephyr 1.98% swapper 27478 netctl-auto 1.78% transmission-gt 0 swapper/3 1.53% Xephyr 0 swapper/0 1.29% netctl-auto 0 swapper/1 1.29% swapper 27476 netctl-auto 1.21% netctl-auto 0 swapper/3 1.17% swapper 233 irq/33-iwlwifi Note that pid 0 exists for each cpu so have comm of 'swapper/N'. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 0c038a27fe5c..cc659ba0e232 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1531,6 +1531,216 @@ static int __sort_dimension__add_hpp_output(struct sort_dimension *sd) return 0; } +struct hpp_dynamic_entry { + struct perf_hpp_fmt hpp; + struct perf_evsel *evsel; + struct format_field *field; + unsigned dynamic_len; +}; + +static int hde_width(struct hpp_dynamic_entry *hde) +{ + if (!hde->hpp.len) { + int len = hde->dynamic_len; + int namelen = strlen(hde->field->name); + int fieldlen = hde->field->size; + + if (namelen > len) + len = namelen; + + if (!(hde->field->flags & FIELD_IS_STRING)) { + /* length for print hex numbers */ + fieldlen = hde->field->size * 2 + 2; + } + if (fieldlen > len) + len = fieldlen; + + hde->hpp.len = len; + } + return hde->hpp.len; +} + +static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct perf_evsel *evsel __maybe_unused) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, hde->field->name); +} + +static int __sort__hde_width(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp __maybe_unused, + struct perf_evsel *evsel __maybe_unused) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + return len; +} + +static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he) +{ + struct hpp_dynamic_entry *hde; + size_t len = fmt->user_len; + struct trace_seq seq; + int ret; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (!len) + len = hde_width(hde); + + if (hists_to_evsel(he->hists) != hde->evsel) + return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, "N/A"); + + trace_seq_init(&seq); + pevent_print_field(&seq, he->raw_data, hde->field); + ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, seq.buffer); + trace_seq_destroy(&seq); + return ret; +} + +static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, + struct hist_entry *a, struct hist_entry *b) +{ + struct hpp_dynamic_entry *hde; + struct format_field *field; + unsigned offset, size; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + if (hists_to_evsel(a->hists) != hde->evsel) + return 0; + + field = hde->field; + if (field->flags & FIELD_IS_DYNAMIC) { + unsigned long long dyn; + + pevent_read_number_field(field, a->raw_data, &dyn); + offset = dyn & 0xffff; + size = (dyn >> 16) & 0xffff; + + /* record max width for output */ + if (size > hde->dynamic_len) + hde->dynamic_len = size; + } else { + offset = field->offset; + size = field->size; + } + + return memcmp(a->raw_data + offset, b->raw_data + offset, size); +} + +static struct hpp_dynamic_entry * +__alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) +{ + struct hpp_dynamic_entry *hde; + + hde = malloc(sizeof(*hde)); + if (hde == NULL) { + pr_debug("Memory allocation failed\n"); + return NULL; + } + + hde->evsel = evsel; + hde->field = field; + hde->dynamic_len = 0; + + hde->hpp.name = field->name; + hde->hpp.header = __sort__hde_header; + hde->hpp.width = __sort__hde_width; + hde->hpp.entry = __sort__hde_entry; + hde->hpp.color = NULL; + + hde->hpp.cmp = __sort__hde_cmp; + hde->hpp.collapse = __sort__hde_cmp; + hde->hpp.sort = __sort__hde_cmp; + + INIT_LIST_HEAD(&hde->hpp.list); + INIT_LIST_HEAD(&hde->hpp.sort_list); + hde->hpp.elide = false; + hde->hpp.len = 0; + hde->hpp.user_len = 0; + + return hde; +} + +static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) +{ + char *str, *event_name, *field_name; + struct perf_evsel *evsel, *pos; + struct format_field *field; + struct hpp_dynamic_entry *hde; + int ret = 0; + + if (evlist == NULL) + return -ENOENT; + + str = strdup(tok); + if (str == NULL) + return -ENOMEM; + + event_name = str; + field_name = strchr(str, '.'); + if (field_name == NULL) { + ret = -EINVAL; + goto out; + } + *field_name++ = '\0'; + + evsel = NULL; + evlist__for_each(evlist, pos) { + if (!strcmp(pos->name, event_name)) { + evsel = pos; + break; + } + } + + if (evsel == NULL) { + pr_debug("Cannot find event: %s\n", event_name); + ret = -ENOENT; + goto out; + } + + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + pr_debug("%s is not a tracepoint event\n", event_name); + ret = -EINVAL; + goto out; + } + + field = pevent_find_any_field(evsel->tp_format, field_name); + if (field == NULL) { + pr_debug("Cannot find event field for %s.%s\n", + event_name, field_name); + ret = -ENOENT; + goto out; + } + + hde = __alloc_dynamic_entry(evsel, field); + if (hde == NULL) { + ret = -ENOMEM; + goto out; + } + + perf_hpp__register_sort_field(&hde->hpp); + +out: + free(str); + return ret; +} + static int __sort_dimension__add(struct sort_dimension *sd) { if (sd->taken) @@ -1667,6 +1877,9 @@ static int sort_dimension__add(const char *tok, return 0; } + if (!add_dynamic_entry(evlist, tok)) + return 0; + return -ESRCH; } -- cgit v1.2.2 From 60517d28fbd91629686dcf9a39aef4e068a3d5f6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:03 +0900 Subject: perf tools: Try to show pretty printed output for dynamic sort keys Each tracepoint event has format string for print to improve readability. Try to parse the output and match the field name. If it finds one, use that for the result. If not, fallbacks to the original output. For example, sort on kmem:kmalloc.gfp_flags looks like below: (Note: libtraceevent plugins are not installed on my system. They might affect the output below) Before: # Overhead Command gfp_flags # ........ ....... .......... # 99.89% perf 32848 0.06% sleep 208 0.03% perf 32976 0.01% perf 208 After: # Overhead Command gfp_flags # ........ ....... ................... # 99.89% perf GFP_NOFS|GFP_ZERO 0.06% sleep GFP_KERNEL 0.03% perf GFP_KERNEL|GFP_ZERO 0.01% perf GFP_KERNEL Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-7-git-send-email-namhyung@kernel.org [ Fixed clash with earlier, updated patch in this patchkit ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 1 + tools/perf/util/sort.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++--- tools/perf/util/sort.h | 1 + 3 files changed, 102 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2dcf38a78f08..fdb97e16a8c3 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -997,6 +997,7 @@ void hist_entry__delete(struct hist_entry *he) if (he->srcfile && he->srcfile[0]) free(he->srcfile); free_callchain(he->callchain); + free(he->trace_output); free(he->raw_data); free(he); } diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cc659ba0e232..22d28c7e0b01 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1560,6 +1560,62 @@ static int hde_width(struct hpp_dynamic_entry *hde) return hde->hpp.len; } +static char *get_trace_output(struct hist_entry *he) +{ + struct trace_seq seq; + struct perf_evsel *evsel; + struct pevent_record rec = { + .data = he->raw_data, + .size = he->raw_size, + }; + + evsel = hists_to_evsel(he->hists); + + trace_seq_init(&seq); + pevent_event_info(&seq, evsel->tp_format, &rec); + return seq.buffer; +} + +static void update_dynamic_len(struct hpp_dynamic_entry *hde, + struct hist_entry *he) +{ + char *str, *pos; + struct format_field *field = hde->field; + size_t namelen; + bool last = false; + + /* parse pretty print result and update max length */ + if (!he->trace_output) + he->trace_output = get_trace_output(he); + + namelen = strlen(field->name); + str = he->trace_output; + + while (str) { + pos = strchr(str, ' '); + if (pos == NULL) { + last = true; + pos = str + strlen(str); + } + + if (!strncmp(str, field->name, namelen)) { + size_t len; + + str += namelen + 1; + len = pos - str; + + if (len > hde->dynamic_len) + hde->dynamic_len = len; + break; + } + + if (last) + str = NULL; + else + str = pos + 1; + } +} + static int __sort__hde_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct perf_evsel *evsel __maybe_unused) { @@ -1594,7 +1650,10 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, { struct hpp_dynamic_entry *hde; size_t len = fmt->user_len; - struct trace_seq seq; + char *str, *pos; + struct format_field *field; + size_t namelen; + bool last = false; int ret; hde = container_of(fmt, struct hpp_dynamic_entry, hpp); @@ -1605,10 +1664,43 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, if (hists_to_evsel(he->hists) != hde->evsel) return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, "N/A"); - trace_seq_init(&seq); - pevent_print_field(&seq, he->raw_data, hde->field); - ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, seq.buffer); - trace_seq_destroy(&seq); + field = hde->field; + + namelen = strlen(field->name); + str = he->trace_output; + + while (str) { + pos = strchr(str, ' '); + if (pos == NULL) { + last = true; + pos = str + strlen(str); + } + + if (!strncmp(str, field->name, namelen)) { + str += namelen + 1; + str = strndup(str, pos - str); + + if (str == NULL) + return scnprintf(hpp->buf, hpp->size, + "%*.*s", len, len, "ERROR"); + break; + } + + if (last) + str = NULL; + else + str = pos + 1; + } + + if (str == NULL) { + struct trace_seq seq; + trace_seq_init(&seq); + pevent_print_field(&seq, he->raw_data, hde->field); + str = seq.buffer; + } + + ret = scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, str); + free(str); return ret; } @@ -1638,6 +1730,9 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, } else { offset = field->offset; size = field->size; + + update_dynamic_len(hde, a); + update_dynamic_len(hde, b); } return memcmp(a->raw_data + offset, b->raw_data + offset, size); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 1a00f1eb9d21..f6d2a7e3e7f2 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -124,6 +124,7 @@ struct hist_entry { struct mem_info *mem_info; void *raw_data; u32 raw_size; + void *trace_output; struct callchain_root callchain[0]; /* must be last member */ }; -- cgit v1.2.2 From a34bb6a08d6020bde0475bc901793771858a1112 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:04 +0900 Subject: perf tools: Add 'trace' sort key The 'trace' sort key is to show tracepoint event output using either print fmt or plugin. For example sched_switch event (using plugin) will show output like below: # perf record -e sched:sched_switch -a usleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.197 MB perf.data (69 samples) ] # $ perf report -s trace --stdio ... # Overhead Trace output # ........ ................................................... # 9.48% swapper/0:0 [120] R ==> transmission-gt:17773 [120] 9.48% transmission-gt:17773 [120] S ==> swapper/0:0 [120] 9.04% swapper/2:0 [120] R ==> transmission-gt:17773 [120] 8.92% transmission-gt:17773 [120] S ==> swapper/2:0 [120] 5.25% swapper/0:0 [120] R ==> kworker/0:1H:109 [100] 5.21% kworker/0:1H:109 [100] S ==> swapper/0:0 [120] 1.78% swapper/3:0 [120] R ==> transmission-gt:17773 [120] 1.78% transmission-gt:17773 [120] S ==> swapper/3:0 [120] 1.53% Xephyr:6524 [120] S ==> swapper/0:0 [120] 1.53% swapper/0:0 [120] R ==> Xephyr:6524 [120] 1.17% swapper/2:0 [120] R ==> irq/33-iwlwifi:233 [49] 1.13% irq/33-iwlwifi:233 [49] S ==> swapper/2:0 [120] Note that the 'trace' sort key works only for tracepoint events. If it's used to other type of events, just "N/A" will be printed. Suggested-and-acked-by: Jiri Olsa Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 76 +++++++++++++++++++++++++++++++++++++++----------- tools/perf/util/sort.h | 1 + 3 files changed, 62 insertions(+), 16 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 36439bfad059..15b22c563d30 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -52,6 +52,7 @@ enum hist_column { HISTC_MEM_IADDR_SYMBOL, HISTC_TRANSACTION, HISTC_CYCLES, + HISTC_TRACE, HISTC_NR_COLS, /* Last entry */ }; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 22d28c7e0b01..db8476a9b103 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -445,6 +445,65 @@ struct sort_entry sort_socket = { .se_width_idx = HISTC_SOCKET, }; +/* --sort trace */ + +static char *get_trace_output(struct hist_entry *he) +{ + struct trace_seq seq; + struct perf_evsel *evsel; + struct pevent_record rec = { + .data = he->raw_data, + .size = he->raw_size, + }; + + evsel = hists_to_evsel(he->hists); + + trace_seq_init(&seq); + pevent_event_info(&seq, evsel->tp_format, &rec); + return seq.buffer; +} + +static int64_t +sort__trace_cmp(struct hist_entry *left, struct hist_entry *right) +{ + struct perf_evsel *evsel; + + evsel = hists_to_evsel(left->hists); + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + return 0; + + if (left->trace_output == NULL) + left->trace_output = get_trace_output(left); + if (right->trace_output == NULL) + right->trace_output = get_trace_output(right); + + hists__new_col_len(left->hists, HISTC_TRACE, strlen(left->trace_output)); + hists__new_col_len(right->hists, HISTC_TRACE, strlen(right->trace_output)); + + return strcmp(right->trace_output, left->trace_output); +} + +static int hist_entry__trace_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width) +{ + struct perf_evsel *evsel; + + evsel = hists_to_evsel(he->hists); + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + return scnprintf(bf, size, "%-*.*s", width, width, "N/A"); + + if (he->trace_output == NULL) + he->trace_output = get_trace_output(he); + return repsep_snprintf(bf, size, "%-*.*s", width, width, he->trace_output); +} + +struct sort_entry sort_trace = { + .se_header = "Trace output", + .se_cmp = sort__trace_cmp, + .se_snprintf = hist_entry__trace_snprintf, + .se_width_idx = HISTC_TRACE, +}; + /* sort keys for branch stacks */ static int64_t @@ -1314,6 +1373,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), DIM(SORT_TRANSACTION, "transaction", sort_transaction), + DIM(SORT_TRACE, "trace", sort_trace), }; #undef DIM @@ -1560,22 +1620,6 @@ static int hde_width(struct hpp_dynamic_entry *hde) return hde->hpp.len; } -static char *get_trace_output(struct hist_entry *he) -{ - struct trace_seq seq; - struct perf_evsel *evsel; - struct pevent_record rec = { - .data = he->raw_data, - .size = he->raw_size, - }; - - evsel = hists_to_evsel(he->hists); - - trace_seq_init(&seq); - pevent_event_info(&seq, evsel->tp_format, &rec); - return seq.buffer; -} - static void update_dynamic_len(struct hpp_dynamic_entry *hde, struct hist_entry *he) { diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index f6d2a7e3e7f2..6b7590ade229 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -183,6 +183,7 @@ enum sort_type { SORT_LOCAL_WEIGHT, SORT_GLOBAL_WEIGHT, SORT_TRANSACTION, + SORT_TRACE, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, -- cgit v1.2.2 From 053a3989e12fdf3be45c00ec1cb0ce09fba0ee4a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:05 +0900 Subject: perf report/top: Add --raw-trace option The --raw-trace option allows disabling pretty printing by the event's print_fmt or plugin. Besides that, each dynamic sort key now can receive a 'raw' suffix separated by '/' to ask for the raw trace of a specific field. $ perf report -s comm,kmem:kmalloc.gfp_flags ... # Overhead Command gfp_flags # ........ ....... ................... # 99.89% perf GFP_NOFS|GFP_ZERO 0.06% sleep GFP_KERNEL 0.03% perf GFP_KERNEL|GFP_ZERO 0.01% perf GFP_KERNEL Now $ perf report -s comm,kmem:kmalloc.gfp_flags --raw-trace or $ perf report -s comm,kmem:kmalloc.gfp_flags/raw ... # Overhead Command gfp_flags # ........ ....... .......... # 99.89% perf 32848 0.06% sleep 208 0.03% perf 32976 0.01% perf 208 Suggested-and-Acked-by: Jiri Olsa Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 32 +++++++++++++++++++++++++++++--- tools/perf/util/symbol.h | 3 ++- 2 files changed, 31 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index db8476a9b103..34776854626e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -459,7 +459,12 @@ static char *get_trace_output(struct hist_entry *he) evsel = hists_to_evsel(he->hists); trace_seq_init(&seq); - pevent_event_info(&seq, evsel->tp_format, &rec); + if (symbol_conf.raw_trace) { + pevent_print_fields(&seq, he->raw_data, he->raw_size, + evsel->tp_format); + } else { + pevent_event_info(&seq, evsel->tp_format, &rec); + } return seq.buffer; } @@ -1596,6 +1601,7 @@ struct hpp_dynamic_entry { struct perf_evsel *evsel; struct format_field *field; unsigned dynamic_len; + bool raw_trace; }; static int hde_width(struct hpp_dynamic_entry *hde) @@ -1628,6 +1634,9 @@ static void update_dynamic_len(struct hpp_dynamic_entry *hde, size_t namelen; bool last = false; + if (hde->raw_trace) + return; + /* parse pretty print result and update max length */ if (!he->trace_output) he->trace_output = get_trace_output(he); @@ -1708,8 +1717,10 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, if (hists_to_evsel(he->hists) != hde->evsel) return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, "N/A"); - field = hde->field; + if (hde->raw_trace) + goto raw_field; + field = hde->field; namelen = strlen(field->name); str = he->trace_output; @@ -1738,6 +1749,7 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, if (str == NULL) { struct trace_seq seq; +raw_field: trace_seq_init(&seq); pevent_print_field(&seq, he->raw_data, hde->field); str = seq.buffer; @@ -1818,10 +1830,11 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) { - char *str, *event_name, *field_name; + char *str, *event_name, *field_name, *raw_opt; struct perf_evsel *evsel, *pos; struct format_field *field; struct hpp_dynamic_entry *hde; + bool raw_trace = symbol_conf.raw_trace; int ret = 0; if (evlist == NULL) @@ -1839,6 +1852,18 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) } *field_name++ = '\0'; + raw_opt = strchr(field_name, '/'); + if (raw_opt) { + *raw_opt++ = '\0'; + + if (strcmp(raw_opt, "raw")) { + pr_err("Unsupported field option %s\n", raw_opt); + ret = -EINVAL; + goto out; + } + raw_trace = true; + } + evsel = NULL; evlist__for_each(evlist, pos) { if (!strcmp(pos->name, event_name)) { @@ -1872,6 +1897,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) ret = -ENOMEM; goto out; } + hde->raw_trace = raw_trace; perf_hpp__register_sort_field(&hde->hpp); diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 857f707ac12b..ccd1caa40e11 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -109,7 +109,8 @@ struct symbol_conf { branch_callstack, has_filter, show_ref_callgraph, - hide_unresolved; + hide_unresolved, + raw_trace; const char *vmlinux_name, *kallsyms_name, *source_prefix, -- cgit v1.2.2 From 5d0cff93bb7aa85349230d4e29902b2648640c53 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:06 +0900 Subject: perf tools: Support shortcuts for events in dynamic sort keys The dynamic sort key requires event name but specifying full event name is rather inconvenient. This patch adds more ways to identify the event in a more compact way. 1. If session has just one event, event name can be omitted. 2. Events can be accessed by index preceded by a percent sign. 3. A part of the name can be used, if it's not ambiguous. The partial name should not contain ':' in it. 4. Full system + event name is still used, it should contain ':'. So in the below example all does same thing: $ perf record -e sched:sched_switch -a sleep 1 $ perf report -s next_pid,next_comm $ perf report -s %1.next_pid,%1.next_comm $ perf report -s switch.next_pid,switch.next_comm $ perf report -s sched:sched_switch.next_pid,sched:sched_switch.next_comm Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 107 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 20 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 34776854626e..f3a98c25fb8d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1828,10 +1828,90 @@ __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) return hde; } +static int parse_field_name(char *str, char **event, char **field, char **opt) +{ + char *event_name, *field_name, *opt_name; + + event_name = str; + field_name = strchr(str, '.'); + + if (field_name) { + *field_name++ = '\0'; + } else { + event_name = NULL; + field_name = str; + } + + opt_name = strchr(field_name, '/'); + if (opt_name) + *opt_name++ = '\0'; + + *event = event_name; + *field = field_name; + *opt = opt_name; + + return 0; +} + +/* find match evsel using a given event name. The event name can be: + * 1. NULL - only valid for single event session + * 2. '%' + event index (e.g. '%1' for first event) + * 3. full event name (e.g. sched:sched_switch) + * 4. partial event name (should not contain ':') + */ +static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name) +{ + struct perf_evsel *evsel = NULL; + struct perf_evsel *pos; + bool full_name; + + /* case 1 */ + if (event_name == NULL) { + if (evlist->nr_entries != 1) { + pr_debug("event name should be given\n"); + return NULL; + } + + return perf_evlist__first(evlist); + } + + /* case 2 */ + if (event_name[0] == '%') { + int nr = strtol(event_name+1, NULL, 0); + + if (nr > evlist->nr_entries) + return NULL; + + evsel = perf_evlist__first(evlist); + while (--nr > 0) + evsel = perf_evsel__next(evsel); + + return evsel; + } + + full_name = !!strchr(event_name, ':'); + evlist__for_each(evlist, pos) { + /* case 3 */ + if (full_name && !strcmp(pos->name, event_name)) + return pos; + /* case 4 */ + if (!full_name && strstr(pos->name, event_name)) { + if (evsel) { + pr_debug("'%s' event is ambiguous: it can be %s or %s\n", + event_name, evsel->name, pos->name); + return NULL; + } + evsel = pos; + } + } + + return evsel; +} + static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) { - char *str, *event_name, *field_name, *raw_opt; - struct perf_evsel *evsel, *pos; + char *str, *event_name, *field_name, *opt_name; + struct perf_evsel *evsel; struct format_field *field; struct hpp_dynamic_entry *hde; bool raw_trace = symbol_conf.raw_trace; @@ -1844,34 +1924,21 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) if (str == NULL) return -ENOMEM; - event_name = str; - field_name = strchr(str, '.'); - if (field_name == NULL) { + if (parse_field_name(str, &event_name, &field_name, &opt_name) < 0) { ret = -EINVAL; goto out; } - *field_name++ = '\0'; - raw_opt = strchr(field_name, '/'); - if (raw_opt) { - *raw_opt++ = '\0'; - - if (strcmp(raw_opt, "raw")) { - pr_err("Unsupported field option %s\n", raw_opt); + if (opt_name) { + if (strcmp(opt_name, "raw")) { + pr_debug("unsupported field option %s\n", opt_name); ret = -EINVAL; goto out; } raw_trace = true; } - evsel = NULL; - evlist__for_each(evlist, pos) { - if (!strcmp(pos->name, event_name)) { - evsel = pos; - break; - } - } - + evsel = find_evsel(evlist, event_name); if (evsel == NULL) { pr_debug("Cannot find event: %s\n", event_name); ret = -ENOENT; -- cgit v1.2.2 From 3b099bf5898ac1bf44d822f0bc15a7517e6fa117 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:07 +0900 Subject: perf tools: Support '.*' dynamic sort key Support '*' character for field name to add all (non-common) fields as sort keys easily. $ perf report -s 'switch.*' --stdio ... # Overhead prev_comm prev_pid prev_prio prev_state next_comm next_pid next_prio # ........ ........... ......... ......... .......... ............ ........ ......... # 3.82% swapper/0 0 120 0 netctl-auto 18711 120 3.75% netctl-auto 18711 120 1 swapper/0 0 120 2.24% swapper/1 0 120 0 netctl-auto 18709 120 2.24% netctl-auto 18709 120 1 swapper/1 0 120 1.80% swapper/2 0 120 0 rcu_preempt 7 120 1.80% swapper/2 0 120 0 netctl-auto 18711 120 1.80% rcu_preempt 7 120 1 swapper/2 0 120 1.80% netctl-auto 18711 120 1 swapper/2 0 120 ... Suggested-and-acked-by: Jiri Olsa Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-11-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f3a98c25fb8d..f6aef15a651d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1908,12 +1908,27 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam return evsel; } +static int __dynamic_dimension__add(struct perf_evsel *evsel, + struct format_field *field, + bool raw_trace) +{ + struct hpp_dynamic_entry *hde; + + hde = __alloc_dynamic_entry(evsel, field); + if (hde == NULL) + return -ENOMEM; + + hde->raw_trace = raw_trace; + + perf_hpp__register_sort_field(&hde->hpp); + return 0; +} + static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) { char *str, *event_name, *field_name, *opt_name; struct perf_evsel *evsel; struct format_field *field; - struct hpp_dynamic_entry *hde; bool raw_trace = symbol_conf.raw_trace; int ret = 0; @@ -1951,22 +1966,26 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) goto out; } - field = pevent_find_any_field(evsel->tp_format, field_name); - if (field == NULL) { - pr_debug("Cannot find event field for %s.%s\n", - event_name, field_name); - ret = -ENOENT; - goto out; - } + if (!strcmp(field_name, "*")) { + field = evsel->tp_format->format.fields; - hde = __alloc_dynamic_entry(evsel, field); - if (hde == NULL) { - ret = -ENOMEM; - goto out; - } - hde->raw_trace = raw_trace; + while (field) { + ret = __dynamic_dimension__add(evsel, field, raw_trace); + if (ret < 0) + goto out; - perf_hpp__register_sort_field(&hde->hpp); + field = field->next; + } + } else { + field = pevent_find_any_field(evsel->tp_format, field_name); + if (field == NULL) { + pr_debug("Cannot find event field for %s.%s\n", + event_name, field_name); + return -ENOENT; + } + + ret = __dynamic_dimension__add(evsel, field, raw_trace); + } out: free(str); -- cgit v1.2.2 From 361459f163fa1ec7ff4700ec876c3b7ff5f36cc6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:08 +0900 Subject: perf tools: Skip dynamic fields not defined for current event When there are multiple events, each dynamic sort key is defined just for one event. In this case other events will always show "N/A" for those fields. But they are meaningless and consume precious screen width. Let's skip those undefined dynamic fields. $ perf record -e kmem:kmalloc,kmem:kfree -a sleep 1 $ perf report -s 'comm,kmalloc.*' --stdio # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 20K of event 'kmem:kmalloc' # Event count (approx.): 20533 # # Overhead Command call_site ptr bytes_req bytes_alloc gfp_flags # ........ ....... .................. .................. ......... ........... ................... # 99.89% perf ffffffffa01d4396 0xffff8803ffb79720 96 96 GFP_NOFS|GFP_ZERO 0.06% sleep ffffffff8114e1cd 0xffff8803d228a000 4096 4096 GFP_KERNEL 0.03% perf ffffffff811d6ae6 0xffff8803f7678f00 240 256 GFP_KERNEL|GFP_ZERO 0.00% perf ffffffff812263c1 0xffff880406172380 128 128 GFP_KERNEL 0.00% perf ffffffff812264b9 0xffff8803ffac1600 504 512 GFP_KERNEL 0.00% perf ffffffff81226634 0xffff880401dc5280 28 32 GFP_KERNEL 0.00% sleep ffffffff81226da9 0xffff8803ffac3a00 392 512 GFP_KERNEL # Samples: 20K of event 'kmem:kfree' # Event count (approx.): 20597 # # Overhead Command # ........ .............. # 99.63% perf 0.14% sleep 0.11% irq/36-iwlwifi 0.11% kworker/u16:0 0.01% Xorg 0.00% firefox Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-12-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 2 +- tools/perf/util/hist.h | 14 ++++++++++++-- tools/perf/util/sort.c | 20 ++++++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index fdb97e16a8c3..afc9b8f1b36c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1112,7 +1112,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) int64_t cmp = 0; perf_hpp__for_each_sort_list(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, a->hists)) continue; cmp = fmt->sort(fmt, a, b); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 15b22c563d30..cb8f37349972 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -262,10 +262,20 @@ void perf_hpp__append_sort_keys(void); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b); +bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *format); +bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists); -static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) +static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format, + struct hists *hists) { - return format->elide; + if (format->elide) + return true; + + if (perf_hpp__is_dynamic_entry(format) && + !perf_hpp__defined_dynamic_entry(format, hists)) + return true; + + return false; } void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f6aef15a651d..fd56223793a8 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1698,6 +1698,15 @@ static int __sort__hde_width(struct perf_hpp_fmt *fmt, return len; } +bool perf_hpp__defined_dynamic_entry(struct perf_hpp_fmt *fmt, struct hists *hists) +{ + struct hpp_dynamic_entry *hde; + + hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + + return hists_to_evsel(hists) == hde->evsel; +} + static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) { @@ -1714,9 +1723,6 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, if (!len) len = hde_width(hde); - if (hists_to_evsel(he->hists) != hde->evsel) - return scnprintf(hpp->buf, hpp->size, "%*.*s", len, len, "N/A"); - if (hde->raw_trace) goto raw_field; @@ -1769,9 +1775,6 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, hde = container_of(fmt, struct hpp_dynamic_entry, hpp); - if (hists_to_evsel(a->hists) != hde->evsel) - return 0; - field = hde->field; if (field->flags & FIELD_IS_DYNAMIC) { unsigned long long dyn; @@ -1794,6 +1797,11 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, return memcmp(a->raw_data + offset, b->raw_data + offset, size); } +bool perf_hpp__is_dynamic_entry(struct perf_hpp_fmt *fmt) +{ + return fmt->cmp == __sort__hde_cmp; +} + static struct hpp_dynamic_entry * __alloc_dynamic_entry(struct perf_evsel *evsel, struct format_field *field) { -- cgit v1.2.2 From 2e422fd1e4b0a1c0ca11d360be2147c87911dd1a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:09 +0900 Subject: perf tools: Add 'trace_fields' dynamic sort key The 'trace_fields' sort key is similar as 'trace' sort key, but it shows each fields separately. Each event will get different columns as their fields. $ perf report -s trace_fields --stdio # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 20K of event 'kmem:kmalloc' # Event count (approx.): 20533 # # Overhead Command call_site ptr bytes_req bytes_alloc gfp_flags # ........ ....... .................. .................. ......... ........... ................... # 99.89% perf ffffffffa01d4396 0xffff8803ffb79720 96 96 GFP_NOFS|GFP_ZERO 0.06% sleep ffffffff8114e1cd 0xffff8803d228a000 4096 4096 GFP_KERNEL 0.03% perf ffffffff811d6ae6 0xffff8803f7678f00 240 256 GFP_KERNEL|GFP_ZERO 0.00% perf ffffffff812263c1 0xffff880406172380 128 128 GFP_KERNEL 0.00% perf ffffffff812264b9 0xffff8803ffac1600 504 512 GFP_KERNEL 0.00% perf ffffffff81226634 0xffff880401dc5280 28 32 GFP_KERNEL 0.00% sleep ffffffff81226da9 0xffff8803ffac3a00 392 512 GFP_KERNEL # Samples: 20K of event 'kmem:kfree' # Event count (approx.): 20597 # # Overhead call_site ptr # ........ .................. .................. # 99.58% ffffffffa01d85ad 0xffff8803ffb79720 0.07% ffffffff81443f5c 0xffff8803f7669400 0.02% ffffffff811d5753 0xffff8803f7678f00 0.01% ffffffff81443f5c 0xffff8803f766be00 0.01% ffffffff8114e359 0xffff8803d228a000 0.01% ffffffff81443f5c 0xffff8800d156dc00 0.01% ffffffff81443f5c 0xffff8803f7669400 0.01% ffffffff8114e359 0xffff8803d228a000 0.01% ffffffff8114e359 0xffff8803d228a000 0.01% ffffffff8114e359 0xffff8803d228a000 Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-13-git-send-email-namhyung@kernel.org [ Combined with "perf tools: Fix segfault when using -s trace_fields" ] Link: http://lkml.kernel.org/r/1451991518-25673-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index fd56223793a8..79aa71d26d9f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1932,6 +1932,38 @@ static int __dynamic_dimension__add(struct perf_evsel *evsel, return 0; } +static int add_evsel_fields(struct perf_evsel *evsel, bool raw_trace) +{ + int ret; + struct format_field *field; + + field = evsel->tp_format->format.fields; + while (field) { + ret = __dynamic_dimension__add(evsel, field, raw_trace); + if (ret < 0) + return ret; + + field = field->next; + } + return 0; +} + +static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) +{ + int ret; + struct perf_evsel *evsel; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + continue; + + ret = add_evsel_fields(evsel, raw_trace); + if (ret < 0) + return ret; + } + return 0; +} + static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) { char *str, *event_name, *field_name, *opt_name; @@ -1961,6 +1993,11 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) raw_trace = true; } + if (!strcmp(field_name, "trace_fields")) { + ret = add_all_dynamic_fields(evlist, raw_trace); + goto out; + } + evsel = find_evsel(evlist, event_name); if (evsel == NULL) { pr_debug("Cannot find event: %s\n", event_name); @@ -1975,15 +2012,7 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) } if (!strcmp(field_name, "*")) { - field = evsel->tp_format->format.fields; - - while (field) { - ret = __dynamic_dimension__add(evsel, field, raw_trace); - if (ret < 0) - goto out; - - field = field->next; - } + ret = add_evsel_fields(evsel, raw_trace); } else { field = pevent_find_any_field(evsel->tp_format, field_name); if (field == NULL) { -- cgit v1.2.2 From d49dadea78624353d1df660efb49f187bd5c5971 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:10 +0900 Subject: perf tools: Make 'trace' or 'trace_fields' sort key default for tracepoint events When an evlist contains tracepoint events only, use 'trace' sort key as default. If --raw-trace option was given, use 'trace_fields' instead. This will make users more convenient to see trace result. Suggested-and-Acked-by: Jiri Olsa Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1450804030-29193-14-git-send-email-namhyung@kernel.org [ Check evlist in get_default_sort_order() fixing a segfault in 'perf test hists' reported by Jiri Olsa ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 30 +++++++++++++++++++++++++----- tools/perf/util/sort.h | 1 + 2 files changed, 26 insertions(+), 5 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 79aa71d26d9f..4b4b1c5cccef 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -15,6 +15,7 @@ const char default_branch_sort_order[] = "comm,dso_from,symbol_from,symbol_to,cy const char default_mem_sort_order[] = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; const char default_top_sort_order[] = "dso,symbol"; const char default_diff_sort_order[] = "dso,symbol"; +const char default_tracepoint_sort_order[] = "trace"; const char *sort_order; const char *field_order; regex_t ignore_callees_regex; @@ -2171,7 +2172,7 @@ static int sort_dimension__add(const char *tok, return -ESRCH; } -static const char *get_default_sort_order(void) +static const char *get_default_sort_order(struct perf_evlist *evlist) { const char *default_sort_orders[] = { default_sort_order, @@ -2179,14 +2180,33 @@ static const char *get_default_sort_order(void) default_mem_sort_order, default_top_sort_order, default_diff_sort_order, + default_tracepoint_sort_order, }; + bool use_trace = true; + struct perf_evsel *evsel; BUG_ON(sort__mode >= ARRAY_SIZE(default_sort_orders)); + if (evlist == NULL) + goto out_no_evlist; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + use_trace = false; + break; + } + } + + if (use_trace) { + sort__mode = SORT_MODE__TRACEPOINT; + if (symbol_conf.raw_trace) + return "trace_fields"; + } +out_no_evlist: return default_sort_orders[sort__mode]; } -static int setup_sort_order(void) +static int setup_sort_order(struct perf_evlist *evlist) { char *new_sort_order; @@ -2207,7 +2227,7 @@ static int setup_sort_order(void) * because it's checked over the rest of the code. */ if (asprintf(&new_sort_order, "%s,%s", - get_default_sort_order(), sort_order + 1) < 0) { + get_default_sort_order(evlist), sort_order + 1) < 0) { error("Not enough memory to set up --sort"); return -ENOMEM; } @@ -2222,7 +2242,7 @@ static int __setup_sorting(struct perf_evlist *evlist) const char *sort_keys; int ret = 0; - ret = setup_sort_order(); + ret = setup_sort_order(evlist); if (ret) return ret; @@ -2236,7 +2256,7 @@ static int __setup_sorting(struct perf_evlist *evlist) return 0; } - sort_keys = get_default_sort_order(); + sort_keys = get_default_sort_order(evlist); } str = strdup(sort_keys); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6b7590ade229..dec536b6ab3d 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -167,6 +167,7 @@ enum sort_mode { SORT_MODE__MEMORY, SORT_MODE__TOP, SORT_MODE__DIFF, + SORT_MODE__TRACEPOINT, }; enum sort_type { -- cgit v1.2.2 From 9735be24ec086fbccee321471cc21dedefa956a6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Jan 2016 19:58:35 +0900 Subject: perf tools: Add all matching dynamic sort keys for field name When a perf.data file has multiple events, it's likely to be similar (tracepoint) events. In that case, they might have same field name so add all of them to sort keys instead of bailing out. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1451991518-25673-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 4b4b1c5cccef..04e2a5cb19e3 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1863,10 +1863,9 @@ static int parse_field_name(char *str, char **event, char **field, char **opt) } /* find match evsel using a given event name. The event name can be: - * 1. NULL - only valid for single event session - * 2. '%' + event index (e.g. '%1' for first event) - * 3. full event name (e.g. sched:sched_switch) - * 4. partial event name (should not contain ':') + * 1. '%' + event index (e.g. '%1' for first event) + * 2. full event name (e.g. sched:sched_switch) + * 3. partial event name (should not contain ':') */ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_name) { @@ -1875,16 +1874,6 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam bool full_name; /* case 1 */ - if (event_name == NULL) { - if (evlist->nr_entries != 1) { - pr_debug("event name should be given\n"); - return NULL; - } - - return perf_evlist__first(evlist); - } - - /* case 2 */ if (event_name[0] == '%') { int nr = strtol(event_name+1, NULL, 0); @@ -1900,10 +1889,10 @@ static struct perf_evsel *find_evsel(struct perf_evlist *evlist, char *event_nam full_name = !!strchr(event_name, ':'); evlist__for_each(evlist, pos) { - /* case 3 */ + /* case 2 */ if (full_name && !strcmp(pos->name, event_name)) return pos; - /* case 4 */ + /* case 3 */ if (!full_name && strstr(pos->name, event_name)) { if (evsel) { pr_debug("'%s' event is ambiguous: it can be %s or %s\n", @@ -1965,6 +1954,28 @@ static int add_all_dynamic_fields(struct perf_evlist *evlist, bool raw_trace) return 0; } +static int add_all_matching_fields(struct perf_evlist *evlist, + char *field_name, bool raw_trace) +{ + int ret = -ESRCH; + struct perf_evsel *evsel; + struct format_field *field; + + evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + continue; + + field = pevent_find_any_field(evsel->tp_format, field_name); + if (field == NULL) + continue; + + ret = __dynamic_dimension__add(evsel, field, raw_trace); + if (ret < 0) + break; + } + return ret; +} + static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) { char *str, *event_name, *field_name, *opt_name; @@ -1999,6 +2010,11 @@ static int add_dynamic_entry(struct perf_evlist *evlist, const char *tok) goto out; } + if (event_name == NULL) { + ret = add_all_matching_fields(evlist, field_name, raw_trace); + goto out; + } + evsel = find_evsel(evlist, event_name); if (evsel == NULL) { pr_debug("Cannot find event: %s\n", event_name); -- cgit v1.2.2 From 8058a30ce174060a3c8156dc87b4d4ae39e8281b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:08 +0100 Subject: perf script: Add process_stat/process_stat_interval scripting interface Python and perl scripting code will define those callbacks and get stat data. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452028152-26762-5-git-send-email-jolsa@kernel.org [ Rename 'time' parameters to 'tstamp', to fix the build in older distros ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/trace-event.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b85ee55cca0c..bce5b1dac268 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -65,6 +65,7 @@ int tracing_data_put(struct tracing_data *tdata); struct addr_location; struct perf_session; +struct perf_stat_config; struct scripting_ops { const char *name; @@ -75,6 +76,9 @@ struct scripting_ops { struct perf_sample *sample, struct perf_evsel *evsel, struct addr_location *al); + void (*process_stat)(struct perf_stat_config *config, + struct perf_evsel *evsel, u64 tstamp); + void (*process_stat_interval)(u64 tstamp); int (*generate_script) (struct pevent *pevent, const char *outfile); }; -- cgit v1.2.2 From aef90263561a87ae6d9c6a0f4071d825ce636eef Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:11 +0100 Subject: perf script: Add python support for stat events Add support to get stat events data in perf python scripts. The python script shall implement the following new interface to process stat data: def stat___[](cpu, thread, time, val, ena, run): - is called for every stat event for given counter, if user monitors 'cycles,instructions:u" following callbacks should be defined: def stat__cycles(cpu, thread, time, val, ena, run): def stat__instructions_u(cpu, thread, time, val, ena, run): def stat__interval(time): - is called for every interval with its time, in non interval mode it's called after last stat event with total measured time in ns The rest of the current interface stays untouched.. Please check example CPI metrics script in following patch with command line examples in changelogs. Signed-off-by: Jiri Olsa Tested-by: Kan Liang Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452028152-26762-8-git-send-email-jolsa@kernel.org [ Rename 'time' parameters to 'tstamp', to fix the build in older distros ] Signed-off-by: Arnaldo Carvalho de Melo --- .../util/scripting-engines/trace-event-python.c | 115 +++++++++++++++++++-- 1 file changed, 109 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index a8e825fca42a..d72fafc1c800 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -41,6 +41,9 @@ #include "../thread-stack.h" #include "../trace-event.h" #include "../machine.h" +#include "thread_map.h" +#include "cpumap.h" +#include "stat.h" PyMODINIT_FUNC initperf_trace_context(void); @@ -859,6 +862,104 @@ static void python_process_event(union perf_event *event, } } +static void get_handler_name(char *str, size_t size, + struct perf_evsel *evsel) +{ + char *p = str; + + scnprintf(str, size, "stat__%s", perf_evsel__name(evsel)); + + while ((p = strchr(p, ':'))) { + *p = '_'; + p++; + } +} + +static void +process_stat(struct perf_evsel *counter, int cpu, int thread, u64 tstamp, + struct perf_counts_values *count) +{ + PyObject *handler, *t; + static char handler_name[256]; + int n = 0; + + t = PyTuple_New(MAX_FIELDS); + if (!t) + Py_FatalError("couldn't create Python tuple"); + + get_handler_name(handler_name, sizeof(handler_name), + counter); + + handler = get_handler(handler_name); + if (!handler) { + pr_debug("can't find python handler %s\n", handler_name); + return; + } + + PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); + PyTuple_SetItem(t, n++, PyInt_FromLong(thread)); + + tuple_set_u64(t, n++, tstamp); + tuple_set_u64(t, n++, count->val); + tuple_set_u64(t, n++, count->ena); + tuple_set_u64(t, n++, count->run); + + if (_PyTuple_Resize(&t, n) == -1) + Py_FatalError("error resizing Python tuple"); + + call_object(handler, t, handler_name); + + Py_DECREF(t); +} + +static void python_process_stat(struct perf_stat_config *config, + struct perf_evsel *counter, u64 tstamp) +{ + struct thread_map *threads = counter->threads; + struct cpu_map *cpus = counter->cpus; + int cpu, thread; + + if (config->aggr_mode == AGGR_GLOBAL) { + process_stat(counter, -1, -1, tstamp, + &counter->counts->aggr); + return; + } + + for (thread = 0; thread < threads->nr; thread++) { + for (cpu = 0; cpu < cpus->nr; cpu++) { + process_stat(counter, cpus->map[cpu], + thread_map__pid(threads, thread), tstamp, + perf_counts(counter->counts, cpu, thread)); + } + } +} + +static void python_process_stat_interval(u64 tstamp) +{ + PyObject *handler, *t; + static const char handler_name[] = "stat__interval"; + int n = 0; + + t = PyTuple_New(MAX_FIELDS); + if (!t) + Py_FatalError("couldn't create Python tuple"); + + handler = get_handler(handler_name); + if (!handler) { + pr_debug("can't find python handler %s\n", handler_name); + return; + } + + tuple_set_u64(t, n++, tstamp); + + if (_PyTuple_Resize(&t, n) == -1) + Py_FatalError("error resizing Python tuple"); + + call_object(handler, t, handler_name); + + Py_DECREF(t); +} + static int run_start_sub(void) { main_module = PyImport_AddModule("__main__"); @@ -1201,10 +1302,12 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) } struct scripting_ops python_scripting_ops = { - .name = "Python", - .start_script = python_start_script, - .flush_script = python_flush_script, - .stop_script = python_stop_script, - .process_event = python_process_event, - .generate_script = python_generate_script, + .name = "Python", + .start_script = python_start_script, + .flush_script = python_flush_script, + .stop_script = python_stop_script, + .process_event = python_process_event, + .process_stat = python_process_stat, + .process_stat_interval = python_process_stat_interval, + .generate_script = python_generate_script, }; -- cgit v1.2.2 From 15d2b9956b41ffb5961b897bf61cdc09f722dfbf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 6 Jan 2016 11:49:55 +0100 Subject: perf cpumap: Fix cpu conversion in cpu_map__from_entries We can't convert u16 cpu_map_entries::cpu[x] value directly to int, because it could hold -1, which would be converted as 65535. Adding special treatment for -1, which is not real cpu number, to be converted to (int -1). Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452077397-31958-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cpumap.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index a0717b93d8f5..fa935093a599 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -188,8 +188,17 @@ static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus) if (map) { unsigned i; - for (i = 0; i < cpus->nr; i++) - map->map[i] = (int)cpus->cpu[i]; + for (i = 0; i < cpus->nr; i++) { + /* + * Special treatment for -1, which is not real cpu number, + * and we need to use (int) -1 to initialize map[i], + * otherwise it would become 65535. + */ + if (cpus->cpu[i] == (u16) -1) + map->map[i] = -1; + else + map->map[i] = (int) cpus->cpu[i]; + } } return map; -- cgit v1.2.2 From 84530920de3c6ccb92c6661da784f6cdb66d3304 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 6 Jan 2016 19:50:01 +0100 Subject: perf pmu: fix alias->snapshot missing initialization bug This patch fixes a bug in __perf_pmu__new_alias() whereby the alias->snapshot field was not initialized to false. This led to random alias->snapshot value for an alias and was breaking some measurements such as: $ perf stat -a -e uncore_imc/data_reads/ -I 1000 sleep 100 Because the event ended up being treated as snapshot mode, when it is not. Signed-off-by: Stephane Eranian Cc: Andi Kleen Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452106201-13073-1-git-send-email-eranian@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index e4b173dec4b9..b597bcc8fc78 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -220,6 +220,7 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, alias->scale = 1.0; alias->unit[0] = '\0'; alias->per_pkg = false; + alias->snapshot = false; ret = parse_events_terms(&alias->terms, val); if (ret) { -- cgit v1.2.2 From 5bae0250237f7a5ec4355f9920701de247b8db91 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 13:14:56 -0300 Subject: perf evlist: Introduce perf_evlist__new_dummy constructor For case where all we need is an evlist with just an "dummy" evsel, like in some 'perf test' entries. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-q52le0pblm2k3ncvyilelr9z@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 28 ++++++++++++++++++++++++++++ tools/perf/util/evlist.h | 3 +++ 2 files changed, 31 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b9eac0daa0b9..fa6dbf0ea0d6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -68,6 +68,18 @@ struct perf_evlist *perf_evlist__new_default(void) return evlist; } +struct perf_evlist *perf_evlist__new_dummy(void) +{ + struct perf_evlist *evlist = perf_evlist__new(); + + if (evlist && perf_evlist__add_dummy(evlist)) { + perf_evlist__delete(evlist); + evlist = NULL; + } + + return evlist; +} + /** * perf_evlist__set_id_pos - set the positions of event ids. * @evlist: selected event list @@ -248,6 +260,22 @@ error: return -ENOMEM; } +int perf_evlist__add_dummy(struct perf_evlist *evlist) +{ + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_DUMMY, + .size = sizeof(attr), /* to capture ABI version */ + }; + struct perf_evsel *evsel = perf_evsel__new(&attr); + + if (evsel == NULL) + return -ENOMEM; + + perf_evlist__add(evlist, evsel); + return 0; +} + static int perf_evlist__add_attrs(struct perf_evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs) { diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 139a50038097..3b7e1e2f1a86 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -67,6 +67,7 @@ struct perf_evsel_str_handler { struct perf_evlist *perf_evlist__new(void); struct perf_evlist *perf_evlist__new_default(void); +struct perf_evlist *perf_evlist__new_dummy(void); void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, struct thread_map *threads); void perf_evlist__exit(struct perf_evlist *evlist); @@ -81,6 +82,8 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, #define perf_evlist__add_default_attrs(evlist, array) \ __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) +int perf_evlist__add_dummy(struct perf_evlist *evlist); + int perf_evlist__add_newtp(struct perf_evlist *evlist, const char *sys, const char *name, void *handler); -- cgit v1.2.2 From 372b212263998ae8e3b2f80ce52af3168d749158 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 16:47:11 -0300 Subject: perf python: Add missing files to binding link list Fixing this problem, introduced recently: $ perf test python 16: Try 'import perf' in python, checking link problems : FAILED! In verbose mode we find out what is missing: $ perf test -v python 16: Try 'import perf' in python, checking link problems : --- start --- test child forked, pid 24894 Traceback (most recent call last): File "", line 1, in ImportError: /tmp/build/perf/python/perf.so: undefined symbol: find_next_bit test child finished with -1 ---- end ---- Try 'import perf' in python, checking link problems: FAILED! $ Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Fixes: f77b57ad4fc4 ("perf cpu_map: Add cpu_map__new_event function") Link: http://lkml.kernel.org/n/tip-rajx0zkz6czdrnvvwf0jp76p@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/python-ext-sources | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 51be28b1bca2..38a0b63ebe8b 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -10,6 +10,8 @@ util/ctype.c util/evlist.c util/evsel.c util/cpumap.c +util/bitmap.c +../lib/util/find_next_bit.c ../lib/hweight.c util/thread_map.c util/util.c -- cgit v1.2.2 From 552eb975b83756e3d95aeb5edaeb5c3c032b0021 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 10:46:52 -0300 Subject: tools lib: Move find_next_bit.c to tools/lib/ The commit that introduced it should've moved it to the same place, plus the 'tools/' prefix, but instead moved it to a bogus tools/lib/util/ directory, being the only file there. Move it to tools/lib/find_bit.c, picking the name for the file where these routines live since: 8f6f19dd5143 ("lib: move find_last_bit to lib/find_next_bit.c") Next step is to make tools/lib/find_bit.c to differ from lib/find_bit.c just in removing what is not used by tools/. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: George Spelvin Cc: Namhyung Kim Cc: Rasmus Villemoes Cc: Wang Nan Cc: Yury Norov Link: http://lkml.kernel.org/n/tip-p391cex5mqvahp4pwrton87n@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 6 +++--- tools/perf/util/python-ext-sources | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 94b1099f2c22..e8bc10b41b66 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -9,7 +9,7 @@ libperf-y += env.o libperf-y += event.o libperf-y += evlist.o libperf-y += evsel.o -libperf-y += find_next_bit.o +libperf-y += find_bit.o libperf-y += kallsyms.o libperf-y += levenshtein.o libperf-y += llvm-utils.o @@ -132,7 +132,7 @@ CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c -CFLAGS_find_next_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" @@ -142,7 +142,7 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) -$(OUTPUT)util/find_next_bit.o: ../lib/util/find_next_bit.c FORCE +$(OUTPUT)util/find_bit.o: ../lib/find_bit.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 38a0b63ebe8b..f15d14fb35eb 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -11,7 +11,7 @@ util/evlist.c util/evsel.c util/cpumap.c util/bitmap.c -../lib/util/find_next_bit.c +../lib/find_bit.c ../lib/hweight.c util/thread_map.c util/util.c -- cgit v1.2.2 From 64af4e0da419ef9e9db0d34a3b5836adbf90a5e8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 11:26:43 -0300 Subject: tools lib: Sync tools/lib/find_bit.c with the kernel Need to move the bitmap.[ch] things from tools/perf/ to tools/lib, will be done in the next patches. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: George Spelvin Cc: Namhyung Kim Cc: Rasmus Villemoes Cc: Wang Nan Cc: Yury Norov Link: http://lkml.kernel.org/n/tip-5fys65wkd7gu8j7a7xgukc5t@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/include/linux/bitmap.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 40bd21488032..28f5493da491 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h @@ -11,6 +11,8 @@ int __bitmap_weight(const unsigned long *bitmap, int bits); void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, int bits); +#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) + #define BITMAP_LAST_WORD_MASK(nbits) \ ( \ ((nbits) % BITS_PER_LONG) ? \ -- cgit v1.2.2 From 915b0882c3108a21e9b3b5e176d3151ad522242d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 12:33:37 -0300 Subject: tools lib: Move bitmap.[ch] from tools/perf/ to tools/{lib,include}/ So that lib/find_bit.c doesn't requires anything inside tools/perf/ Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: George Spelvin Cc: Namhyung Kim Cc: Rasmus Villemoes Cc: Wang Nan Cc: Yury Norov Link: http://lkml.kernel.org/n/tip-7lxe7jgohaac5faodndhdmvk@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 5 +++ tools/perf/util/bitmap.c | 31 ---------------- tools/perf/util/include/linux/bitmap.h | 68 ---------------------------------- tools/perf/util/python-ext-sources | 2 +- 4 files changed, 6 insertions(+), 100 deletions(-) delete mode 100644 tools/perf/util/bitmap.c delete mode 100644 tools/perf/util/include/linux/bitmap.h (limited to 'tools/perf/util') diff --git a/tools/perf/util/Build b/tools/perf/util/Build index e8bc10b41b66..5eec53a3f4ac 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -132,6 +132,7 @@ CFLAGS_pmu-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c +CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" @@ -142,6 +143,10 @@ $(OUTPUT)util/kallsyms.o: ../lib/symbol/kallsyms.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) +$(OUTPUT)util/bitmap.o: ../lib/bitmap.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + $(OUTPUT)util/find_bit.o: ../lib/find_bit.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c deleted file mode 100644 index 0a1adc1111fd..000000000000 --- a/tools/perf/util/bitmap.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * From lib/bitmap.c - * Helper functions for bitmap.h. - * - * This source code is licensed under the GNU General Public License, - * Version 2. See the file COPYING for more details. - */ -#include - -int __bitmap_weight(const unsigned long *bitmap, int bits) -{ - int k, w = 0, lim = bits/BITS_PER_LONG; - - for (k = 0; k < lim; k++) - w += hweight_long(bitmap[k]); - - if (bits % BITS_PER_LONG) - w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); - - return w; -} - -void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits) -{ - int k; - int nr = BITS_TO_LONGS(bits); - - for (k = 0; k < nr; k++) - dst[k] = bitmap1[k] | bitmap2[k]; -} diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h deleted file mode 100644 index 28f5493da491..000000000000 --- a/tools/perf/util/include/linux/bitmap.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _PERF_BITOPS_H -#define _PERF_BITOPS_H - -#include -#include - -#define DECLARE_BITMAP(name,bits) \ - unsigned long name[BITS_TO_LONGS(bits)] - -int __bitmap_weight(const unsigned long *bitmap, int bits); -void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, - const unsigned long *bitmap2, int bits); - -#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) - -#define BITMAP_LAST_WORD_MASK(nbits) \ -( \ - ((nbits) % BITS_PER_LONG) ? \ - (1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \ -) - -#define small_const_nbits(nbits) \ - (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) - -static inline void bitmap_zero(unsigned long *dst, int nbits) -{ - if (small_const_nbits(nbits)) - *dst = 0UL; - else { - int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); - memset(dst, 0, len); - } -} - -static inline int bitmap_weight(const unsigned long *src, int nbits) -{ - if (small_const_nbits(nbits)) - return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); - return __bitmap_weight(src, nbits); -} - -static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, - const unsigned long *src2, int nbits) -{ - if (small_const_nbits(nbits)) - *dst = *src1 | *src2; - else - __bitmap_or(dst, src1, src2, nbits); -} - -/** - * test_and_set_bit - Set a bit and return its old value - * @nr: Bit to set - * @addr: Address to count from - */ -static inline int test_and_set_bit(int nr, unsigned long *addr) -{ - unsigned long mask = BIT_MASK(nr); - unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); - unsigned long old; - - old = *p; - *p = old | mask; - - return (old & mask) != 0; -} - -#endif /* _PERF_BITOPS_H */ diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index f15d14fb35eb..8162ba0e2e57 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -10,7 +10,7 @@ util/ctype.c util/evlist.c util/evsel.c util/cpumap.c -util/bitmap.c +../lib/bitmap.c ../lib/find_bit.c ../lib/hweight.c util/thread_map.c -- cgit v1.2.2 From 42b276a2351517409d55b1202a1fa8b05c0cdc99 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Jan 2016 12:06:00 +0900 Subject: perf top: Decay periods in callchains It missed to decay periods in callchains when decaying hist entries. This resulted in more than 100 percent overhead in callchains in the fractal style output. Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1451963160-17196-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 28 ++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 1 + tools/perf/util/hist.c | 1 + 3 files changed, 30 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 564377d2bebf..53c43eb9489e 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -925,6 +925,34 @@ void free_callchain(struct callchain_root *root) free_callchain_node(&root->node); } +static u64 decay_callchain_node(struct callchain_node *node) +{ + struct callchain_node *child; + struct rb_node *n; + u64 child_hits = 0; + + n = rb_first(&node->rb_root_in); + while (n) { + child = container_of(n, struct callchain_node, rb_node_in); + + child_hits += decay_callchain_node(child); + n = rb_next(n); + } + + node->hit = (node->hit * 7) / 8; + node->children_hit = child_hits; + + return node->hit; +} + +void decay_callchain(struct callchain_root *root) +{ + if (!symbol_conf.use_callchain) + return; + + decay_callchain_node(&root->node); +} + int callchain_node__make_parent_list(struct callchain_node *node) { struct callchain_node *parent = node->parent; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 8ac8f043004c..18dd22269764 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -253,6 +253,7 @@ int callchain_node__fprintf_value(struct callchain_node *node, FILE *fp, u64 total); void free_callchain(struct callchain_root *root); +void decay_callchain(struct callchain_root *root); int callchain_node__make_parent_list(struct callchain_node *node); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index afc9b8f1b36c..888776b43cb0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -254,6 +254,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) he_stat__decay(&he->stat); if (symbol_conf.cumulate_callchain) he_stat__decay(he->stat_acc); + decay_callchain(he->callchain); diff = prev_period - he->stat.period; -- cgit v1.2.2 From 1e9abf8b03c8f9352f54171647296c41317679a4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 29 Nov 2015 23:24:17 +0900 Subject: perf report: Change default to use event group view The event group view feature is to see related events together. To use the group view, events should be recorded as a group with a dedicated syntax of surrounding events by braces (-e '{ evt1, evt2, ... }'). Also 'perf report' also requires the --group option to enable it. However it's almost always beneficial to use the group view to see the group events as it's more expressive. And I think it's more natural to see events together if they are recorded as a group. Thus this patch changes the default value to enable it. If users don't want to see like it and keep the original behavior, they can set the report.group config variable to false and/or use --no-group option in the 'perf report' command line. Requested-by: Ingo Molnar Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Taeung Song Link: http://lkml.kernel.org/r/1448807057-3506-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d51abd2e7865..3b2de6eb3376 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -39,6 +39,7 @@ struct symbol_conf symbol_conf = { .cumulate_callchain = true, .show_hist_headers = true, .symfs = "", + .event_group = true, }; static enum dso_binary_type binary_type_symtab[] = { -- cgit v1.2.2 From cbd08b7335c9d559f424dcef7bea333605597490 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:04 +0100 Subject: perf tools: Do not show trace command if it's not compiled in The trace command still appears in help message when you run simple 'perf' command. It's because the generate-cmdlist.sh does not care about the HAVE_LIBAUDIT_SUPPORT dependency of trace command and puts it into generated common_cmds array. Wrapping trace command under HAVE_LIBAUDIT_SUPPORT dependency, which will exclude it from common_cmds array if HAVE_LIBAUDIT_SUPPORT is not set. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-8-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/generate-cmdlist.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 36a885d2cd22..0ac2037c970c 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -36,4 +36,19 @@ do }' "Documentation/perf-$cmd.txt" done echo "#endif /* HAVE_LIBELF_SUPPORT */" + +echo "#ifdef HAVE_LIBAUDIT_SUPPORT" +sed -n -e 's/^perf-\([^ ]*\)[ ].* audit*/\1/p' command-list.txt | +sort | +while read cmd +do + sed -n ' + /^NAME/,/perf-'"$cmd"'/H + ${ + x + s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/ + p + }' "Documentation/perf-$cmd.txt" +done +echo "#endif /* HAVE_LIBELF_SUPPORT */" echo "};" -- cgit v1.2.2 From bb4ced29f5d5ff1d4d51b602dad34a0d15495a67 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:07 +0100 Subject: perf tools: Remove list entry from struct sort_entry It's no longer needed. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index dec536b6ab3d..687bbb124428 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -214,8 +214,6 @@ enum sort_type { */ struct sort_entry { - struct list_head list; - const char *se_header; int64_t (*se_cmp)(struct hist_entry *, struct hist_entry *); -- cgit v1.2.2 From b97511c5bc94ef12613f485ab82f989df04088da Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:08 +0100 Subject: perf tools: Add overhead/overhead_children keys defaults via string We currently set 'overhead' and 'overhead_children' as default sort keys within perf_hpp__init function by directly adding into the sort list. This patch adds 'overhead' and 'overhead_children' in text form into sort_keys and let them be added by standard sort dimension interface. We need to eliminate dirrect sort_list additions to be able to add support for hists specific sort keys. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-12-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/sort.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 04e2a5cb19e3..ec722346e6ff 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2252,6 +2252,34 @@ static int setup_sort_order(struct perf_evlist *evlist) return 0; } +/* + * Adds 'pre,' prefix into 'str' is 'pre' is + * not already part of 'str'. + */ +static char *prefix_if_not_in(const char *pre, char *str) +{ + char *n; + + if (!str || strstr(str, pre)) + return str; + + if (asprintf(&n, "%s,%s", pre, str) < 0) + return NULL; + + free(str); + return n; +} + +static char *setup_overhead(char *keys) +{ + keys = prefix_if_not_in("overhead", keys); + + if (symbol_conf.cumulate_callchain) + keys = prefix_if_not_in("overhead_children", keys); + + return keys; +} + static int __setup_sorting(struct perf_evlist *evlist) { char *tmp, *tok, *str; @@ -2281,6 +2309,17 @@ static int __setup_sorting(struct perf_evlist *evlist) return -ENOMEM; } + /* + * Prepend overhead fields for backward compatibility. + */ + if (!is_strict_order(field_order)) { + str = setup_overhead(str); + if (str == NULL) { + error("Not enough memory to setup overhead keys"); + return -ENOMEM; + } + } + for (tok = strtok_r(str, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { ret = sort_dimension__add(tok, evlist); -- cgit v1.2.2 From fc284be9d88528dd2a28d5471e40a6acde6c3036 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 10:14:10 +0100 Subject: perf hists: Export a couple of hist functions These are necessary for multi threaded sample processing: - hists__get__get_rotate_entries_in() - hists__collapse_insert_entry() - __hists__init() Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-14-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 19 ++++++++++++------- tools/perf/util/hist.h | 5 +++++ 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 888776b43cb0..c226303e3da0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1007,9 +1007,8 @@ void hist_entry__delete(struct hist_entry *he) * collapse the histogram */ -static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, - struct rb_root *root, - struct hist_entry *he) +bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, + struct rb_root *root, struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -1049,7 +1048,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, return true; } -static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) +struct rb_root *hists__get_rotate_entries_in(struct hists *hists) { struct rb_root *root; @@ -1584,10 +1583,8 @@ int perf_hist_config(const char *var, const char *value) return 0; } -static int hists_evsel__init(struct perf_evsel *evsel) +int __hists__init(struct hists *hists) { - struct hists *hists = evsel__hists(evsel); - memset(hists, 0, sizeof(*hists)); hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; hists->entries_in = &hists->entries_in_array[0]; @@ -1627,6 +1624,14 @@ static void hists_evsel__exit(struct perf_evsel *evsel) hists__delete_all_entries(hists); } +static int hists_evsel__init(struct perf_evsel *evsel) +{ + struct hists *hists = evsel__hists(evsel); + + __hists__init(hists); + return 0; +} + /* * XXX We probably need a hists_evsel__exit() to free the hist_entries * stored in the rbtree... diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index cb8f37349972..d4ec4822a103 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -185,6 +185,11 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) } int hists__init(void); +int __hists__init(struct hists *hists); + +struct rb_root *hists__get_rotate_entries_in(struct hists *hists); +bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, + struct rb_root *root, struct hist_entry *he); struct perf_hpp { char *buf; -- cgit v1.2.2 From 14cbfbeb76540cc0c53fbb0ba34b3a4900ebe40f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 20:41:53 +0900 Subject: perf report: Show random usage tip on the help line Currently perf report only shows a help message "For a higher level overview, try: perf report --sort comm,dso" unconditionally (even if the sort keys were used). Add more help tips and show randomly. Load tips from ${prefix}/share/doc/perf-tip/tips.txt file. $ perf report | tail 0.10% swapper [kernel.vmlinux] [k] irq_exit 0.09% swapper [kernel.vmlinux] [k] flush_smp_call_function_queue 0.08% swapper [kernel.vmlinux] [k] native_write_msr_safe 0.03% swapper [kernel.vmlinux] [k] group_sched_in 0.01% perf [kernel.vmlinux] [k] native_write_msr_safe # # (Tip: Search options using a keyword: perf report -h ) # Signed-off-by: Namhyung Kim Acked-by: Ingo Molnar Cc: Andi Kleen Cc: David Ahern Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Wang Nan Link: http://lkml.kernel.org/r/1452166913-27046-1-git-send-email-namhyung@kernel.org [ Renamed it to perf_tip() and the parameter dirname to dirpath to fix the build on older distros ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/util.c | 27 +++++++++++++++++++++++++++ tools/perf/util/util.h | 2 ++ 2 files changed, 29 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index aff0cfd83662..88b8f8d21f58 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -16,6 +16,8 @@ #include #include #include "callchain.h" +#include "strlist.h" +#include struct callchain_param callchain_param = { .mode = CHAIN_GRAPH_ABS, @@ -663,3 +665,28 @@ fetch_kernel_version(unsigned int *puint, char *str, *puint = (version << 16) + (patchlevel << 8) + sublevel; return 0; } + +const char *perf_tip(const char *dirpath) +{ + struct strlist *tips; + struct str_node *node; + char *tip = NULL; + struct strlist_config conf = { + .dirname = system_path(dirpath) , + }; + + tips = strlist__new("tips.txt", &conf); + if (tips == NULL || strlist__nr_entries(tips) == 1) { + tip = (char *)"Cannot find tips.txt file"; + goto out; + } + + node = strlist__entry(tips, random() % strlist__nr_entries(tips)); + if (asprintf(&tip, "Tip: %s", node->s) < 0) + tip = (char *)"Tip: get more memory! ;-)"; + +out: + strlist__delete(tips); + + return tip; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4b519c59bdc3..fe915e616f9b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -342,4 +342,6 @@ int fetch_kernel_version(unsigned int *puint, #define KVER_FMT "%d.%d.%d" #define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) +const char *perf_tip(const char *dirpath); + #endif /* GIT_COMPAT_UTIL_H */ -- cgit v1.2.2 From 23df7f798435796aff07d641456326b81cb34a77 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 Jan 2016 10:13:59 +0100 Subject: perf evlist: Make perf_evlist__open() open evsels with their cpus and threads (like perf record does) 'perf record' uses perf_evsel__open() to open events and passes the evsel->cpus and evsel->threads. Many tests and some tools instead use perf_evlist__open() which passes instead evlist->cpus and evlist->threads. Make perf_evlist__open() follow the 'perf record' behaviour so that a consistent approach is taken. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index fa6dbf0ea0d6..29e085b2e168 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1498,7 +1498,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); evlist__for_each(evlist, evsel) { - err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); + err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); if (err < 0) goto out_err; } -- cgit v1.2.2 From d2190a8091124f832c8862ace3a3d7d70a2506a5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:13:58 +0100 Subject: perf evlist: Remove perf_evlist__(enable|disable)_event functions Replacing them with perf_evsel__(enable|disable). Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 42 ------------------------------------------ tools/perf/util/evlist.h | 4 ---- 2 files changed, 46 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 29e085b2e168..d81f13de2476 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -393,48 +393,6 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist) (evlist->enabled ? perf_evlist__disable : perf_evlist__enable)(evlist); } -int perf_evlist__disable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - int cpu, thread, err; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads = perf_evlist__nr_threads(evlist, evsel); - - if (!evsel->fd) - return 0; - - for (cpu = 0; cpu < nr_cpus; cpu++) { - for (thread = 0; thread < nr_threads; thread++) { - err = ioctl(FD(evsel, cpu, thread), - PERF_EVENT_IOC_DISABLE, 0); - if (err) - return err; - } - } - return 0; -} - -int perf_evlist__enable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - int cpu, thread, err; - int nr_cpus = cpu_map__nr(evlist->cpus); - int nr_threads = perf_evlist__nr_threads(evlist, evsel); - - if (!evsel->fd) - return -EINVAL; - - for (cpu = 0; cpu < nr_cpus; cpu++) { - for (thread = 0; thread < nr_threads; thread++) { - err = ioctl(FD(evsel, cpu, thread), - PERF_EVENT_IOC_ENABLE, 0); - if (err) - return err; - } - } - return 0; -} - static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, struct perf_evsel *evsel, int cpu) { diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 3b7e1e2f1a86..7c4d9a206776 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -155,10 +155,6 @@ void perf_evlist__disable(struct perf_evlist *evlist); void perf_evlist__enable(struct perf_evlist *evlist); void perf_evlist__toggle_enable(struct perf_evlist *evlist); -int perf_evlist__disable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel); -int perf_evlist__enable_event(struct perf_evlist *evlist, - struct perf_evsel *evsel); int perf_evlist__enable_event_idx(struct perf_evlist *evlist, struct perf_evsel *evsel, int idx); -- cgit v1.2.2 From f22ed827a8d5ff5a85e7c8e865baaaaf71a8d0cc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:00 +0100 Subject: perf unwind: Use find_map function in access_dso_mem The find_map helper is already there, so let's use it. Also we're going to introduce wider search in following patch, so it'll be easier to make this change on single place. Signed-off-by: Jiri Olsa Tested-by: Noel Grandin Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libunwind.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 3c258a0e4092..f37859c04317 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -416,20 +416,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as, static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, unw_word_t *data) { - struct addr_location al; + struct map *map; ssize_t size; - thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, - MAP__FUNCTION, addr, &al); - if (!al.map) { + map = find_map(addr, ui); + if (!map) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; } - if (!al.map->dso) + if (!map->dso) return -1; - size = dso__data_read_addr(al.map->dso, al.map, ui->machine, + size = dso__data_read_addr(map->dso, map, ui->machine, addr, (u8 *) data, sizeof(*data)); return !(size == sizeof(*data)); -- cgit v1.2.2 From 0ddf5246f70ecc04e1bb4c4dc2be65977d1c03a7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:01 +0100 Subject: perf unwind: Check for mmaps also in MAP__VARIABLE tree We've seen cases (softice) where DWARF unwinder went through non executable mmaps, which we need to lookup in MAP__VARIABLE tree. Reported-and-Tested-by: Noel Grandin Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-5-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libunwind.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index f37859c04317..ee7e372297e5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -319,6 +319,15 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, MAP__FUNCTION, ip, &al); + if (!al.map) { + /* + * We've seen cases (softice) where DWARF unwinder went + * through non executable mmaps, which we need to lookup + * in MAP__VARIABLE tree. + */ + thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, + MAP__VARIABLE, ip, &al); + } return al.map; } -- cgit v1.2.2 From 0ba98149f8c8b6b2ba36be9938afb731fa719004 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:02 +0100 Subject: perf libdw: Check for mmaps also in MAP__VARIABLE tree We've seen cases (softice) where DWARF unwinder went through non executable mmaps, which we need to lookup in MAP__VARIABLE tree. Reported-and-Tested-by: Noel Grandin Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/unwind-libdw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index db8142ba7cb9..cf5e250bc78e 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -95,6 +95,16 @@ static int access_dso_mem(struct unwind_info *ui, Dwarf_Addr addr, thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, MAP__FUNCTION, addr, &al); + if (!al.map) { + /* + * We've seen cases (softice) where DWARF unwinder went + * through non executable mmaps, which we need to lookup + * in MAP__VARIABLE tree. + */ + thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, + MAP__VARIABLE, addr, &al); + } + if (!al.map) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; -- cgit v1.2.2 From 775d8a1b0d75211cc6123915c6b5b688f2002478 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 09:12:29 +0900 Subject: perf evlist: Add --trace-fields option to show trace fields To use dynamic sort keys, it might be good to add an option to see the list of field names. $ perf evlist -i perf.data.sched sched:sched_switch sched:sched_stat_wait sched:sched_stat_sleep sched:sched_stat_iowait sched:sched_stat_runtime sched:sched_process_fork sched:sched_wakeup sched:sched_wakeup_new sched:sched_migrate_task # Tip: use 'perf evlist --trace-fields' to show fields for tracepoint events $ perf evlist -i perf.data.sched --trace-fields sched:sched_switch: trace_fields: prev_comm,prev_pid,prev_prio,prev_state,next_comm,next_pid,next_prio sched:sched_stat_wait: trace_fields: comm,pid,delay sched:sched_stat_sleep: trace_fields: comm,pid,delay sched:sched_stat_iowait: trace_fields: comm,pid,delay sched:sched_stat_runtime: trace_fields: comm,pid,runtime,vruntime sched:sched_process_fork: trace_fields: parent_comm,parent_pid,child_comm,child_pid sched:sched_wakeup: trace_fields: comm,pid,prio,success,target_cpu sched:sched_wakeup_new: trace_fields: comm,pid,prio,success,target_cpu sched:sched_migrate_task: trace_fields: comm,pid,prio,orig_cpu,dest_cpu Committer notes: For another file, in verbose mode: # perf evlist -v --trace-fields sched:sched_switch: type: 2, size: 112, config: 0x10b, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|PERIOD|RAW, disabled: 1, inherit: 1, mmap: 1, comm: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, trace_fields: prev_comm,prev_pid,prev_prio,prev_state,next_comm,next_pid,next_prio # Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Cc: Wang Nan Link: http://lkml.kernel.org/r/1452125549-1511-5-git-send-email-namhyung@kernel.org [ Replaced 'trace_fields=' with 'trace_fields: ' to make the output consistent in -v mode ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 23 +++++++++++++++++++++++ tools/perf/util/evsel.h | 1 + 2 files changed, 24 insertions(+) (limited to 'tools/perf/util') diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 544e4400de13..cdbaf9b51e42 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2298,6 +2298,29 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, printed += comma_fprintf(fp, &first, " %s=%" PRIu64, term, (u64)evsel->attr.sample_freq); } + + if (details->trace_fields) { + struct format_field *field; + + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) { + printed += comma_fprintf(fp, &first, " (not a tracepoint)"); + goto out; + } + + field = evsel->tp_format->format.fields; + if (field == NULL) { + printed += comma_fprintf(fp, &first, " (no trace field)"); + goto out; + } + + printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); + + field = field->next; + while (field) { + printed += comma_fprintf(fp, &first, "%s", field->name); + field = field->next; + } + } out: fputc('\n', fp); return ++printed; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 5ded1fc0341e..8e75434bd01c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -369,6 +369,7 @@ struct perf_attr_details { bool verbose; bool event_group; bool force; + bool trace_fields; }; int perf_evsel__fprintf(struct perf_evsel *evsel, -- cgit v1.2.2