From 916d4092a1d2d7bb50630497be71ee4c4c2807fa Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 18 Nov 2015 17:38:49 -0300 Subject: [PATCH 001/271] perf test: Fix build of BPF and LLVM on older glibc libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $ rpm -q glibc glibc-2.12-1.166.el6_7.1.x86_64 CC /tmp/build/perf/tests/llvm.o cc1: warnings being treated as errors tests/llvm.c: In function ‘test_llvm__fetch_bpf_obj’: tests/llvm.c:53: error: declaration of ‘index’ shadows a global declaration /usr/include/string.h:489: error: shadowed declaration is here CC /tmp/build/perf/tests/bpf.o cc1: warnings being treated as errors tests/bpf.c: In function ‘__test__bpf’: tests/bpf.c:149: error: declaration of ‘index’ shadows a global declaration /usr/include/string.h:489: error: shadowed declaration is here Cc: He Kuang Cc: Jiri Olsa Cc: Namhyung Kim Cc: pi3orama@163.com Cc: Wang Nan Cc: Zefan Li Fixes: b31de018a628 ("perf test: Enhance the LLVM test: update basic BPF test program") Fixes: ba1fae431e74 ("perf test: Add 'perf test BPF'") Link: http://lkml.kernel.org/n/tip-akpo4r750oya2phxoh9e3447@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bpf.c | 14 +++++++------- tools/perf/tests/llvm.c | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index ec16f7812c8b..6ebfdee3e2c6 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -146,7 +146,7 @@ prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) return obj; } -static int __test__bpf(int index) +static int __test__bpf(int idx) { int ret; void *obj_buf; @@ -154,27 +154,27 @@ static int __test__bpf(int index) struct bpf_object *obj; ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, - bpf_testcase_table[index].prog_id, + bpf_testcase_table[idx].prog_id, true); if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { pr_debug("Unable to get BPF object, %s\n", - bpf_testcase_table[index].msg_compile_fail); - if (index == 0) + bpf_testcase_table[idx].msg_compile_fail); + if (idx == 0) return TEST_SKIP; else return TEST_FAIL; } obj = prepare_bpf(obj_buf, obj_buf_sz, - bpf_testcase_table[index].name); + bpf_testcase_table[idx].name); if (!obj) { ret = TEST_FAIL; goto out; } ret = do_test(obj, - bpf_testcase_table[index].target_func, - bpf_testcase_table[index].expect_result); + bpf_testcase_table[idx].target_func, + bpf_testcase_table[idx].expect_result); out: bpf__clear(); return ret; diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index bc4cf507cde5..366e38ba8b49 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -50,7 +50,7 @@ static struct { int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, - enum test_llvm__testcase index, + enum test_llvm__testcase idx, bool force) { const char *source; @@ -59,11 +59,11 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf, char *tmpl_new = NULL, *clang_opt_new = NULL; int err, old_verbose, ret = TEST_FAIL; - if (index >= __LLVM_TESTCASE_MAX) + if (idx >= __LLVM_TESTCASE_MAX) return TEST_FAIL; - source = bpf_source_table[index].source; - desc = bpf_source_table[index].desc; + source = bpf_source_table[idx].source; + desc = bpf_source_table[idx].desc; perf_config(perf_config_cb, NULL); From 9a13c6587e2f0d5e80ce02f5f9ef62788b48d163 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Tue, 17 Nov 2015 13:54:19 -0800 Subject: [PATCH 002/271] tools: Fix selftests_install Makefile rule Fix copy/paste error in selftests_install rule which was copy-pasted from the clean rule but not properly changed. Signed-off-by: Kevin Hilman Cc: Bamvor Jian Zhang Cc: Jiri Olsa Cc: Jonathan Cameron Cc: Michael Ellerman Cc: Pali Rohar Cc: Pavel Machek Cc: Roberta Dobrescu Cc: Shuah Khan Cc: linaro-kernel@lists.linaro.org Link: http://lkml.kernel.org/r/1447797261-1775-1-git-send-email-khilman@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/Makefile b/tools/Makefile index 7dc820a8c1f1..0ba0df3b516f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -96,7 +96,7 @@ cgroup_install firewire_install hv_install lguest_install perf_install usb_insta $(call descend,$(@:_install=),install) selftests_install: - $(call descend,testing/$(@:_clean=),install) + $(call descend,testing/$(@:_install=),install) turbostat_install x86_energy_perf_policy_install: $(call descend,power/x86/$(@:_install=),install) From 4ddd32741da87657113d964588ce13ee64b34820 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Nov 2015 11:36:29 -0300 Subject: [PATCH 003/271] 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/include/linux/string.h | 9 +++++++++ tools/lib/string.c | 19 +++++++++++++++++++ tools/perf/MANIFEST | 2 ++ tools/perf/util/Build | 6 ++++++ tools/perf/util/include/linux/string.h | 3 --- tools/perf/util/string.c | 16 ---------------- 6 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 tools/include/linux/string.h create mode 100644 tools/lib/string.c delete mode 100644 tools/perf/util/include/linux/string.h diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h new file mode 100644 index 000000000000..f3a6db6ad732 --- /dev/null +++ b/tools/include/linux/string.h @@ -0,0 +1,9 @@ +#ifndef _TOOLS_LINUX_STRING_H_ +#define _TOOLS_LINUX_STRING_H_ + + +#include /* for size_t */ + +void *memdup(const void *src, size_t len); + +#endif /* _LINUX_STRING_H_ */ diff --git a/tools/lib/string.c b/tools/lib/string.c new file mode 100644 index 000000000000..ecfd43a9b24e --- /dev/null +++ b/tools/lib/string.c @@ -0,0 +1,19 @@ +#include +#include +#include + +/** + * memdup - duplicate region of memory + * + * @src: memory region to duplicate + * @len: memory region length + */ +void *memdup(const void *src, size_t len) +{ + void *p = malloc(len); + + if (p) + memcpy(p, src, len); + + return p; +} diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 39c38cb45b00..2562eac6451d 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -22,6 +22,7 @@ tools/lib/api tools/lib/bpf tools/lib/hweight.c tools/lib/rbtree.c +tools/lib/string.c tools/lib/symbol/kallsyms.c tools/lib/symbol/kallsyms.h tools/lib/util/find_next_bit.c @@ -50,6 +51,7 @@ tools/include/linux/log2.h tools/include/linux/poison.h tools/include/linux/rbtree.h tools/include/linux/rbtree_augmented.h +tools/include/linux/string.h tools/include/linux/types.h tools/include/linux/err.h include/asm-generic/bitops/arch_hweight.h 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) { /* From 7d85c434214ea0b3416f7a62f76a0785b00d8797 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 11:42:05 -0300 Subject: [PATCH 004/271] tools: Clone the kernel's strtobool function Copying it to tools/lib/string.c, the counterpart to the kernel's lib/string.c. This is preparation for enhancing BPF program configuration, which will allow config string like 'inlines=yes'. Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Jonathan Cameron Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-6-git-send-email-wangnan0@huawei.com [ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/string.h | 2 ++ tools/lib/string.c | 43 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index f3a6db6ad732..2e2f736c039c 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -6,4 +6,6 @@ void *memdup(const void *src, size_t len); +int strtobool(const char *s, bool *res); + #endif /* _LINUX_STRING_H_ */ diff --git a/tools/lib/string.c b/tools/lib/string.c index ecfd43a9b24e..065e54f42d8f 100644 --- a/tools/lib/string.c +++ b/tools/lib/string.c @@ -1,5 +1,20 @@ +/* + * linux/tools/lib/string.c + * + * Copied from linux/lib/string.c, where it is: + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * More specifically, the first copied function was strtobool, which + * was introduced by: + * + * d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents") + * Author: Jonathan Cameron + */ + #include #include +#include #include /** @@ -17,3 +32,31 @@ void *memdup(const void *src, size_t len) return p; } + +/** + * strtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0'. + * Otherwise it will return -EINVAL. Value pointed to by res is + * updated upon finding a match. + */ +int strtobool(const char *s, bool *res) +{ + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + break; + case 'n': + case 'N': + case '0': + *res = false; + break; + default: + return -EINVAL; + } + return 0; +} From b580563e38487d9db8e94080149644da71c533c1 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:09 +0000 Subject: [PATCH 005/271] bpf tools: Load a program with different instances using preprocessor This patch is a preparation for BPF prologue support which allows generating a series of BPF bytecode for fetching kernel data before calling program code. With the newly introduced multiple instances support, perf is able to create different prologues for different kprobe points. Before this patch, a bpf_program can be loaded into kernel only once, and get the only resulting fd. What this patch does is to allow creating and loading different variants of one bpf_program, then fetching their fds. Here we describe the basic idea in this patch. The detailed description of the newly introduced APIs can be found in comments in the patch body. The key of this patch is the new mechanism in bpf_program__load(). Instead of loading BPF program into kernel directly, it calls a 'pre-processor' to generate program instances which would be finally loaded into the kernel based on the original code. To enable the generation of multiple instances, libbpf passes an index to the pre-processor so it know which instance is being loaded. Pre-processor should be called from libbpf's user (perf) using bpf_program__set_prep(). The number of instances and the relationship between indices and the target instance should be clear when calling bpf_program__set_prep(). To retrieve a fd for a specific instance of a program, bpf_program__nth_fd() is introduced. It returns the resulting fd according to index. Signed-off-by: He Kuang Cc: Alexei Starovoitov Cc: He Kuang Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-8-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan [ Enclosed multi-line if/else blocks with {}, (*func_ptr)() -> func_ptr() ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 146 ++++++++++++++++++++++++++++++++++++++--- tools/lib/bpf/libbpf.h | 64 ++++++++++++++++++ 2 files changed, 201 insertions(+), 9 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e176bad19bcb..e3f4c3379f14 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -152,7 +152,11 @@ struct bpf_program { } *reloc_desc; int nr_reloc; - int fd; + struct { + int nr; + int *fds; + } instances; + bpf_program_prep_t preprocessor; struct bpf_object *obj; void *priv; @@ -206,10 +210,25 @@ struct bpf_object { static void bpf_program__unload(struct bpf_program *prog) { + int i; + if (!prog) return; - zclose(prog->fd); + /* + * If the object is opened but the program was never loaded, + * it is possible that prog->instances.nr == -1. + */ + if (prog->instances.nr > 0) { + for (i = 0; i < prog->instances.nr; i++) + zclose(prog->instances.fds[i]); + } else if (prog->instances.nr != -1) { + pr_warning("Internal error: instances.nr is %d\n", + prog->instances.nr); + } + + prog->instances.nr = -1; + zfree(&prog->instances.fds); } static void bpf_program__exit(struct bpf_program *prog) @@ -260,7 +279,8 @@ bpf_program__init(void *data, size_t size, char *name, int idx, memcpy(prog->insns, data, prog->insns_cnt * sizeof(struct bpf_insn)); prog->idx = idx; - prog->fd = -1; + prog->instances.fds = NULL; + prog->instances.nr = -1; return 0; errout: @@ -860,13 +880,73 @@ static int bpf_program__load(struct bpf_program *prog, char *license, u32 kern_version) { - int err, fd; + int err = 0, fd, i; - err = load_program(prog->insns, prog->insns_cnt, - license, kern_version, &fd); - if (!err) - prog->fd = fd; + if (prog->instances.nr < 0 || !prog->instances.fds) { + if (prog->preprocessor) { + pr_warning("Internal error: can't load program '%s'\n", + prog->section_name); + return -LIBBPF_ERRNO__INTERNAL; + } + prog->instances.fds = malloc(sizeof(int)); + if (!prog->instances.fds) { + pr_warning("Not enough memory for BPF fds\n"); + return -ENOMEM; + } + prog->instances.nr = 1; + prog->instances.fds[0] = -1; + } + + if (!prog->preprocessor) { + if (prog->instances.nr != 1) { + pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n", + prog->section_name, prog->instances.nr); + } + err = load_program(prog->insns, prog->insns_cnt, + license, kern_version, &fd); + if (!err) + prog->instances.fds[0] = fd; + goto out; + } + + for (i = 0; i < prog->instances.nr; i++) { + struct bpf_prog_prep_result result; + bpf_program_prep_t preprocessor = prog->preprocessor; + + bzero(&result, sizeof(result)); + err = preprocessor(prog, i, prog->insns, + prog->insns_cnt, &result); + if (err) { + pr_warning("Preprocessing the %dth instance of program '%s' failed\n", + i, prog->section_name); + goto out; + } + + if (!result.new_insn_ptr || !result.new_insn_cnt) { + pr_debug("Skip loading the %dth instance of program '%s'\n", + i, prog->section_name); + prog->instances.fds[i] = -1; + if (result.pfd) + *result.pfd = -1; + continue; + } + + err = load_program(result.new_insn_ptr, + result.new_insn_cnt, + license, kern_version, &fd); + + if (err) { + pr_warning("Loading the %dth instance of program '%s' failed\n", + i, prog->section_name); + goto out; + } + + if (result.pfd) + *result.pfd = fd; + prog->instances.fds[i] = fd; + } +out: if (err) pr_warning("failed to load program '%s'\n", prog->section_name); @@ -1121,5 +1201,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy) int bpf_program__fd(struct bpf_program *prog) { - return prog->fd; + return bpf_program__nth_fd(prog, 0); +} + +int bpf_program__set_prep(struct bpf_program *prog, int nr_instances, + bpf_program_prep_t prep) +{ + int *instances_fds; + + if (nr_instances <= 0 || !prep) + return -EINVAL; + + if (prog->instances.nr > 0 || prog->instances.fds) { + pr_warning("Can't set pre-processor after loading\n"); + return -EINVAL; + } + + instances_fds = malloc(sizeof(int) * nr_instances); + if (!instances_fds) { + pr_warning("alloc memory failed for fds\n"); + return -ENOMEM; + } + + /* fill all fd with -1 */ + memset(instances_fds, -1, sizeof(int) * nr_instances); + + prog->instances.nr = nr_instances; + prog->instances.fds = instances_fds; + prog->preprocessor = prep; + return 0; +} + +int bpf_program__nth_fd(struct bpf_program *prog, int n) +{ + int fd; + + if (n >= prog->instances.nr || n < 0) { + pr_warning("Can't get the %dth fd from program %s: only %d instances\n", + n, prog->section_name, prog->instances.nr); + return -EINVAL; + } + + fd = prog->instances.fds[n]; + if (fd < 0) { + pr_warning("%dth instance of program '%s' is invalid\n", + n, prog->section_name); + return -ENOENT; + } + + return fd; } diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index c9a9aef2806c..949df4b346cf 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -88,6 +88,70 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); int bpf_program__fd(struct bpf_program *prog); +struct bpf_insn; + +/* + * Libbpf allows callers to adjust BPF programs before being loaded + * into kernel. One program in an object file can be transform into + * multiple variants to be attached to different code. + * + * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd + * are APIs for this propose. + * + * - bpf_program_prep_t: + * It defines 'preprocessor', which is a caller defined function + * passed to libbpf through bpf_program__set_prep(), and will be + * called before program is loaded. The processor should adjust + * the program one time for each instances according to the number + * passed to it. + * + * - bpf_program__set_prep: + * Attachs a preprocessor to a BPF program. The number of instances + * whould be created is also passed through this function. + * + * - bpf_program__nth_fd: + * After the program is loaded, get resuling fds from bpf program for + * each instances. + * + * If bpf_program__set_prep() is not used, the program whould be loaded + * without adjustment during bpf_object__load(). The program has only + * one instance. In this case bpf_program__fd(prog) is equal to + * bpf_program__nth_fd(prog, 0). + */ + +struct bpf_prog_prep_result { + /* + * If not NULL, load new instruction array. + * If set to NULL, don't load this instance. + */ + struct bpf_insn *new_insn_ptr; + int new_insn_cnt; + + /* If not NULL, result fd is set to it */ + int *pfd; +}; + +/* + * Parameters of bpf_program_prep_t: + * - prog: The bpf_program being loaded. + * - n: Index of instance being generated. + * - insns: BPF instructions array. + * - insns_cnt:Number of instructions in insns. + * - res: Output parameter, result of transformation. + * + * Return value: + * - Zero: pre-processing success. + * - Non-zero: pre-processing, stop loading. + */ +typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n, + struct bpf_insn *insns, int insns_cnt, + struct bpf_prog_prep_result *res); + +int bpf_program__set_prep(struct bpf_program *prog, int nr_instance, + bpf_program_prep_t prep); + +int bpf_program__nth_fd(struct bpf_program *prog, int n); + /* * We don't need __attribute__((packed)) now since it is * unnecessary for 'bpf_map_def' because they are all aligned. From 1c0ed63239012aa881cc811f726b549dca7279e4 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:10 +0000 Subject: [PATCH 006/271] perf bpf: Add BPF_PROLOGUE config options for further patches If both LIBBPF and DWARF are detected, it is possible to create prologue for eBPF programs to help them access kernel data. HAVE_BPF_PROLOGUE and CONFIG_BPF_PROLOGUE are added as flags for this feature. PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET is introduced in commit 63ab024a5b6f295ca17a293ad81b7c728f49a89a ("perf tools: regs_query_register_offset() infrastructure"), which indicates that an architecture supports converting name of a register to its offset in 'struct pt_regs'. Without this support, BPF_PROLOGUE should be turned off. 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-9-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/config/Makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index de89ec574361..6eb9a956a408 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -318,6 +318,18 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_LIBBPF_SUPPORT $(call detected,CONFIG_LIBBPF) endif + + ifndef NO_DWARF + ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET + CFLAGS += -DHAVE_BPF_PROLOGUE + $(call detected,CONFIG_BPF_PROLOGUE) + else + msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset()); + endif + else + msg := $(warning DWARF support is off, BPF prologue is disabled); + endif + endif # NO_LIBBPF endif # NO_LIBELF From 30433a3a52b951faab95944e0f8b9d33a1e322ce Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:11 +0000 Subject: [PATCH 007/271] perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on regs_query_register_offset() in dwarf-regs.c is required by BPF prologue. This patch compiles it if CONFIG_BPF_PROLOGUE is on to avoid build failure when CONFIG_BPF_PROLOGUE is on but CONFIG_DWARF is not set. Signed-off-by: He Kuang Acked-by: Masami Hiramatsu Cc: Alexei Starovoitov Cc: He Kuang Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1447675815-166222-10-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/Build | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build index ff63649fa9ac..465970370f3e 100644 --- a/tools/perf/arch/x86/util/Build +++ b/tools/perf/arch/x86/util/Build @@ -5,6 +5,7 @@ libperf-y += kvm-stat.o libperf-y += perf_regs.o libperf-$(CONFIG_DWARF) += dwarf-regs.o +libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o From 361f2b1d1d7231b8685d990b886f599378a4d5a5 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:05 +0000 Subject: [PATCH 008/271] 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(-) 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, }; From 5dbd16c0c9d17ab1ab2226a5926482c26c0287ed Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:06 +0000 Subject: [PATCH 009/271] 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(+) 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 From 03e01f568759ddbfdaff892e299758e7771a3478 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:08 +0000 Subject: [PATCH 010/271] 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(-) 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; } From bfc077b4cf106793b30bf942e426ee99f1f4ac44 Mon Sep 17 00:00:00 2001 From: He Kuang Date: Mon, 16 Nov 2015 12:10:12 +0000 Subject: [PATCH 011/271] 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 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 */ From a08357d8dc7d3025d1094f727ad1f7e837766f93 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:13 +0000 Subject: [PATCH 012/271] 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(-) 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; From bbb7d4925a05ecd5bbfdbc1147d402b0db203a5a Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 16 Nov 2015 12:10:14 +0000 Subject: [PATCH 013/271] perf test: Test the BPF prologue adding infrastructure This patch introduces a new BPF script to test the BPF prologue adding routines. The new script probes at null_lseek, which is the function pointer used when we try to lseek on '/dev/null'. The null_lseek function is chosen because it is used by function pointers, so we don't need to consider inlining and LTO. By extracting file->f_mode, bpf-script-test-prologue.c should know whether the file is writable or readonly. According to llseek_loop() and bpf-script-test-prologue.c, one fourth of total lseeks should be collected. Committer note: Testing it: # perf test -v BPF Kernel build dir is set to /lib/modules/4.3.0+/build set env: KBUILD_DIR=/lib/modules/4.3.0+/build unset env: KBUILD_OPTS include option is set to -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h set env: NR_CPUS=4 set env: LINUX_VERSION_CODE=0x40300 set env: CLANG_EXEC=/usr/libexec/icecc/bin/clang set env: CLANG_OPTIONS=-xc set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h set env: WORKING_DIR=/lib/modules/4.3.0+/build set env: CLANG_SOURCE=- llvm compiling command template: echo '/* * bpf-script-test-prologue.c * Test BPF prologue */ #ifndef LINUX_VERSION_CODE # error Need LINUX_VERSION_CODE # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' #endif #define SEC(NAME) __attribute__((section(NAME), used)) #include #define FMODE_READ 0x1 #define FMODE_WRITE 0x2 static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = (void *) 6; SEC("func=null_lseek file->f_mode offset orig") int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode, unsigned long offset, unsigned long orig) { if (err) return 0; if (f_mode & FMODE_WRITE) return 0; if (offset & 1) return 0; if (orig == SEEK_CUR) return 0; return 1; } char _license[] SEC("license") = "GPL"; int _version SEC("version") = LINUX_VERSION_CODE; ' | $CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o - libbpf: loading object '[bpf_prologue_test]' from buffer libbpf: section .strtab, size 135, link 0, flags 0, type=3 libbpf: section .text, size 0, link 0, flags 6, type=1 libbpf: section .data, size 0, link 0, flags 3, type=1 libbpf: section .bss, size 0, link 0, flags 3, type=8 libbpf: section func=null_lseek file->f_mode offset orig, size 112, link 0, flags 6, type=1 libbpf: found program func=null_lseek file->f_mode offset orig libbpf: section license, size 4, link 0, flags 3, type=1 libbpf: license of [bpf_prologue_test] is GPL libbpf: section version, size 4, link 0, flags 3, type=1 libbpf: kernel version of [bpf_prologue_test] is 40300 libbpf: section .symtab, size 168, link 1, flags 0, type=2 bpf: config program 'func=null_lseek file->f_mode offset orig' symbol:null_lseek file:(null) line:0 offset:0 return:0 lazy:(null) parsing arg: file->f_mode into file, f_mode(1) parsing arg: offset into offset parsing arg: orig into orig bpf: config 'func=null_lseek file->f_mode offset orig' is ok Looking at the vmlinux_path (7 entries long) Using /lib/modules/4.3.0+/build/vmlinux for symbols Open Debuginfo file: /lib/modules/4.3.0+/build/vmlinux Try to find probe point from debuginfo. Matched function: null_lseek Probe point found: null_lseek+0 Searching 'file' variable in context. Converting variable file into trace event. converting f_mode in file f_mode type is unsigned int. Searching 'offset' variable in context. Converting variable offset into trace event. offset type is long long int. Searching 'orig' variable in context. Converting variable orig into trace event. orig type is int. Found 1 probe_trace_events. Opening /sys/kernel/debug/tracing//kprobe_events write=1 Writing event: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32 libbpf: don't need create maps for [bpf_prologue_test] prologue: pass validation prologue: slow path prologue: fetch arg 0, base reg is %di prologue: arg 0: offset 68 prologue: fetch arg 1, base reg is %si prologue: fetch arg 2, base reg is %dx add bpf event perf_bpf_probe:func and attach bpf program 3 adding perf_bpf_probe:func adding perf_bpf_probe:func to 0x51672c0 mmap size 1052672B Opening /sys/kernel/debug/tracing//kprobe_events write=1 Opening /sys/kernel/debug/tracing//uprobe_events write=1 Parsing probe_events: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32 Group:perf_bpf_probe Event:func probe:p Writing event: -:perf_bpf_probe/func test child finished with 0 ---- end ---- Test BPF filter: Ok # 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-13-git-send-email-wangnan0@huawei.com [ Added tools/perf/tests/llvm-src-prologue.c to .gitignore ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/.gitignore | 1 + tools/perf/tests/Build | 9 +++++- tools/perf/tests/bpf-script-test-prologue.c | 35 +++++++++++++++++++++ tools/perf/tests/bpf.c | 34 ++++++++++++++++++++ tools/perf/tests/llvm.c | 4 +++ tools/perf/tests/llvm.h | 2 ++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tools/perf/tests/bpf-script-test-prologue.c diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore index 489fc9ffbcb0..bf016c439fbd 100644 --- a/tools/perf/tests/.gitignore +++ b/tools/perf/tests/.gitignore @@ -1,2 +1,3 @@ llvm-src-base.c llvm-src-kbuild.c +llvm-src-prologue.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index f41ebf8849fe..0ff8a973b81c 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -31,7 +31,7 @@ perf-y += sample-parsing.o perf-y += parse-no-sample-id-all.o perf-y += kmod-path.o perf-y += thread-map.o -perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o +perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o @@ -49,6 +49,13 @@ $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ $(Q)echo ';' >> $@ +$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c + $(call rule_mkdir) + $(Q)echo '#include ' > $@ + $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@ + $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ + $(Q)echo ';' >> $@ + ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c new file mode 100644 index 000000000000..7230e62c70fc --- /dev/null +++ b/tools/perf/tests/bpf-script-test-prologue.c @@ -0,0 +1,35 @@ +/* + * bpf-script-test-prologue.c + * Test BPF prologue + */ +#ifndef LINUX_VERSION_CODE +# error Need LINUX_VERSION_CODE +# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' +#endif +#define SEC(NAME) __attribute__((section(NAME), used)) + +#include + +#define FMODE_READ 0x1 +#define FMODE_WRITE 0x2 + +static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = + (void *) 6; + +SEC("func=null_lseek file->f_mode offset orig") +int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode, + unsigned long offset, unsigned long orig) +{ + if (err) + return 0; + if (f_mode & FMODE_WRITE) + return 0; + if (offset & 1) + return 0; + if (orig == SEEK_CUR) + return 0; + return 1; +} + +char _license[] SEC("license") = "GPL"; +int _version SEC("version") = LINUX_VERSION_CODE; diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 6ebfdee3e2c6..d58442294e9e 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -19,6 +19,29 @@ static int epoll_pwait_loop(void) return 0; } +#ifdef HAVE_BPF_PROLOGUE + +static int llseek_loop(void) +{ + int fds[2], i; + + fds[0] = open("/dev/null", O_RDONLY); + fds[1] = open("/dev/null", O_RDWR); + + if (fds[0] < 0 || fds[1] < 0) + return -1; + + for (i = 0; i < NR_ITERS; i++) { + lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); + lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); + } + close(fds[0]); + close(fds[1]); + return 0; +} + +#endif + static struct { enum test_llvm__testcase prog_id; const char *desc; @@ -37,6 +60,17 @@ static struct { &epoll_pwait_loop, (NR_ITERS + 1) / 2, }, +#ifdef HAVE_BPF_PROLOGUE + { + LLVM_TESTCASE_BPF_PROLOGUE, + "Test BPF prologue generation", + "[bpf_prologue_test]", + "fix kbuild first", + "check your vmlinux setting?", + &llseek_loop, + (NR_ITERS + 1) / 4, + }, +#endif }; static int do_test(struct bpf_object *obj, int (*func)(void), diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 366e38ba8b49..b4147634fb44 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -44,6 +44,10 @@ static struct { .source = test_llvm__bpf_test_kbuild_prog, .desc = "Test kbuild searching", }, + [LLVM_TESTCASE_BPF_PROLOGUE] = { + .source = test_llvm__bpf_test_prologue_prog, + .desc = "Test BPF prologue generation", + }, }; diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h index d91d8f44efee..5150b4d6ef50 100644 --- a/tools/perf/tests/llvm.h +++ b/tools/perf/tests/llvm.h @@ -6,10 +6,12 @@ extern const char test_llvm__bpf_base_prog[]; extern const char test_llvm__bpf_test_kbuild_prog[]; +extern const char test_llvm__bpf_test_prologue_prog[]; enum test_llvm__testcase { LLVM_TESTCASE_BASE, LLVM_TESTCASE_KBUILD, + LLVM_TESTCASE_BPF_PROLOGUE, __LLVM_TESTCASE_MAX, }; From ad0dd7aed5df8009b3ffa39bec73ad93283332c9 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:46 +0000 Subject: [PATCH 014/271] perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux Two bugs in 'perf test BPF' are found when testing BPF prologue without vmlinux: # mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak} # ./perf test BPF 37: Test BPF filter :Failed to find the path for kernel: No such file or directory Ok Test BPF should fail in this case. After this patch: # ./perf test BPF 37: Test BPF filter :Failed to find the path for kernel: No such file or directory FAILED! # mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,} # ./perf test BPF 37: Test BPF filter : Ok 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-2-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bpf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index d58442294e9e..232043cc232a 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void), err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj); if (err || list_empty(&parse_evlist.list)) { pr_debug("Failed to add events selected by BPF\n"); - if (!err) - return TEST_FAIL; + return TEST_FAIL; } snprintf(pid, sizeof(pid), "%d", getpid()); @@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void), } } - if (count != expect) + if (count != expect) { pr_debug("BPF filter result incorrect\n"); + goto out_delete_evlist; + } ret = TEST_OK; From d35b32891a61f1d3909bdc5280badf309adc4693 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:47 +0000 Subject: [PATCH 015/271] 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(-) 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"); From 721a1f53df6aad3ea941f5fe95519d0d8e02bd65 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 19 Nov 2015 12:01:48 -0300 Subject: [PATCH 016/271] perf tests: Pass the subtest index to each test routine Some tests have sub-tests we want to run, so allow passing this. Wang tried to avoid having to touch all tests, but then, having the test.func in an anonymous union makes the build fail on older compilers, like the one in RHEL6, where: test a = { .func = foo, }; fails. To fix it leave the func pointer in the main structure and pass the subtest index to all tests, end result function is the same, but we have just one function pointer, not two, with and without the subtest index as an argument. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-5genj0ficwdmelpoqlds0u4y@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/include/arch-tests.h | 8 +- tools/perf/arch/x86/tests/insn-x86.c | 2 +- tools/perf/arch/x86/tests/intel-cqm.c | 2 +- tools/perf/arch/x86/tests/perf-time-to-tsc.c | 2 +- tools/perf/arch/x86/tests/rdpmc.c | 2 +- tools/perf/tests/attr.c | 2 +- tools/perf/tests/bp_signal.c | 2 +- tools/perf/tests/bp_signal_overflow.c | 2 +- tools/perf/tests/bpf.c | 2 +- tools/perf/tests/builtin-test.c | 6 +- tools/perf/tests/code-reading.c | 2 +- tools/perf/tests/dso-data.c | 6 +- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/tests/evsel-roundtrip-name.c | 2 +- tools/perf/tests/evsel-tp-sched.c | 2 +- tools/perf/tests/fdarray.c | 4 +- tools/perf/tests/hists_cumulate.c | 2 +- tools/perf/tests/hists_filter.c | 2 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/tests/hists_output.c | 2 +- tools/perf/tests/keep-tracking.c | 2 +- tools/perf/tests/kmod-path.c | 2 +- tools/perf/tests/llvm.c | 2 +- tools/perf/tests/mmap-basic.c | 2 +- tools/perf/tests/mmap-thread-lookup.c | 2 +- tools/perf/tests/openat-syscall-all-cpus.c | 2 +- tools/perf/tests/openat-syscall-tp-fields.c | 2 +- tools/perf/tests/openat-syscall.c | 2 +- tools/perf/tests/parse-events.c | 2 +- tools/perf/tests/parse-no-sample-id-all.c | 2 +- tools/perf/tests/perf-record.c | 2 +- tools/perf/tests/pmu.c | 2 +- tools/perf/tests/python-use.c | 3 +- tools/perf/tests/sample-parsing.c | 2 +- tools/perf/tests/sw-clock.c | 2 +- tools/perf/tests/switch-tracking.c | 2 +- tools/perf/tests/task-exit.c | 2 +- tools/perf/tests/tests.h | 78 ++++++++++---------- tools/perf/tests/thread-map.c | 2 +- tools/perf/tests/thread-mg-share.c | 2 +- tools/perf/tests/topology.c | 2 +- tools/perf/tests/vmlinux-kallsyms.c | 2 +- 42 files changed, 89 insertions(+), 88 deletions(-) diff --git a/tools/perf/arch/x86/include/arch-tests.h b/tools/perf/arch/x86/include/arch-tests.h index 7ed00f4b0908..b48de2f5813c 100644 --- a/tools/perf/arch/x86/include/arch-tests.h +++ b/tools/perf/arch/x86/include/arch-tests.h @@ -2,10 +2,10 @@ #define ARCH_TESTS_H /* Tests */ -int test__rdpmc(void); -int test__perf_time_to_tsc(void); -int test__insn_x86(void); -int test__intel_cqm_count_nmi_context(void); +int test__rdpmc(int subtest); +int test__perf_time_to_tsc(int subtest); +int test__insn_x86(int subtest); +int test__intel_cqm_count_nmi_context(int subtest); #ifdef HAVE_DWARF_UNWIND_SUPPORT struct thread; diff --git a/tools/perf/arch/x86/tests/insn-x86.c b/tools/perf/arch/x86/tests/insn-x86.c index b6115dfd28f0..08d9b2bc185c 100644 --- a/tools/perf/arch/x86/tests/insn-x86.c +++ b/tools/perf/arch/x86/tests/insn-x86.c @@ -171,7 +171,7 @@ static int test_data_set(struct test_data *dat_set, int x86_64) * verbose (-v) option to see all the instructions and whether or not they * decoded successfuly. */ -int test__insn_x86(void) +int test__insn_x86(int subtest __maybe_unused) { int ret = 0; diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c index d28c1b6a3b54..94e0cb7462f9 100644 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ b/tools/perf/arch/x86/tests/intel-cqm.c @@ -33,7 +33,7 @@ static pid_t spawn(void) * the last read counter value to avoid triggering a WARN_ON_ONCE() in * smp_call_function_many() caused by sending IPIs from NMI context. */ -int test__intel_cqm_count_nmi_context(void) +int test__intel_cqm_count_nmi_context(int subtest __maybe_unused) { struct perf_evlist *evlist = NULL; struct perf_evsel *evsel = NULL; diff --git a/tools/perf/arch/x86/tests/perf-time-to-tsc.c b/tools/perf/arch/x86/tests/perf-time-to-tsc.c index 658cd200af74..a289aa8a083a 100644 --- a/tools/perf/arch/x86/tests/perf-time-to-tsc.c +++ b/tools/perf/arch/x86/tests/perf-time-to-tsc.c @@ -35,7 +35,7 @@ * %0 is returned, otherwise %-1 is returned. If TSC conversion is not * supported then then the test passes but " (not supported)" is printed. */ -int test__perf_time_to_tsc(void) +int test__perf_time_to_tsc(int subtest __maybe_unused) { struct record_opts opts = { .mmap_pages = UINT_MAX, diff --git a/tools/perf/arch/x86/tests/rdpmc.c b/tools/perf/arch/x86/tests/rdpmc.c index e7688214c7cf..7bb0d13c235f 100644 --- a/tools/perf/arch/x86/tests/rdpmc.c +++ b/tools/perf/arch/x86/tests/rdpmc.c @@ -149,7 +149,7 @@ out_close: return 0; } -int test__rdpmc(void) +int test__rdpmc(int subtest __maybe_unused) { int status = 0; int wret = 0; diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 638875a0960a..b66730eb94e3 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -153,7 +153,7 @@ static int run_dir(const char *d, const char *perf) return system(cmd); } -int test__attr(void) +int test__attr(int subtest __maybe_unused) { struct stat st; char path_perf[PATH_MAX]; diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index a02b035fd5aa..fb80c9eb6a95 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -111,7 +111,7 @@ static long long bp_count(int fd) return count; } -int test__bp_signal(void) +int test__bp_signal(int subtest __maybe_unused) { struct sigaction sa; long long count1, count2; diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c index e76537724491..89f92fa67cc4 100644 --- a/tools/perf/tests/bp_signal_overflow.c +++ b/tools/perf/tests/bp_signal_overflow.c @@ -58,7 +58,7 @@ static long long bp_count(int fd) #define EXECUTIONS 10000 #define THRESHOLD 100 -int test__bp_signal_overflow(void) +int test__bp_signal_overflow(int subtest __maybe_unused) { struct perf_event_attr pe; struct sigaction sa; diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 232043cc232a..4efdc1607754 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -215,7 +215,7 @@ out: return ret; } -int test__bpf(void) +int test__bpf(int subtest __maybe_unused) { unsigned int i; int err; diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 80c442eab767..9cf4892c061d 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -203,7 +203,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char return false; } -static int run_test(struct test *test) +static int run_test(struct test *test, int subtest) { int status, err = -1, child = fork(); char sbuf[STRERR_BUFSIZE]; @@ -216,7 +216,7 @@ static int run_test(struct test *test) if (!child) { pr_debug("test child forked, pid %d\n", getpid()); - err = test->func(); + err = test->func(subtest); exit(err); } @@ -265,7 +265,7 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) } pr_debug("\n--- start ---\n"); - err = run_test(t); + err = run_test(t, i); pr_debug("---- end ----\n%s:", t->desc); switch (err) { diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index a767a6400c5c..4417b6a079f0 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -601,7 +601,7 @@ out_err: return err; } -int test__code_reading(void) +int test__code_reading(int subtest __maybe_unused) { int ret; diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index a218aeaf56a0..dc673ff7c437 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -110,7 +110,7 @@ static int dso__data_fd(struct dso *dso, struct machine *machine) return fd; } -int test__dso_data(void) +int test__dso_data(int subtest __maybe_unused) { struct machine machine; struct dso *dso; @@ -245,7 +245,7 @@ static int set_fd_limit(int n) return setrlimit(RLIMIT_NOFILE, &rlim); } -int test__dso_data_cache(void) +int test__dso_data_cache(int subtest __maybe_unused) { struct machine machine; long nr_end, nr = open_files_cnt(); @@ -302,7 +302,7 @@ int test__dso_data_cache(void) return 0; } -int test__dso_data_reopen(void) +int test__dso_data_reopen(int subtest __maybe_unused) { struct machine machine; long nr_end, nr = open_files_cnt(); diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 07221793a3ac..01f0b61de53d 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -142,7 +142,7 @@ static int krava_1(struct thread *thread) return krava_2(thread); } -int test__dwarf_unwind(void) +int test__dwarf_unwind(int subtest __maybe_unused) { struct machines machines; struct machine *machine; diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 3fa715987a5e..1da92e1159ee 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -95,7 +95,7 @@ out_delete_evlist: #define perf_evsel__name_array_test(names) \ __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) -int test__perf_evsel__roundtrip_name_test(void) +int test__perf_evsel__roundtrip_name_test(int subtest __maybe_unused) { int err = 0, ret = 0; diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 790e413d9a1f..1984b3bbfe15 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -32,7 +32,7 @@ static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, return ret; } -int test__perf_evsel__tp_sched_test(void) +int test__perf_evsel__tp_sched_test(int subtest __maybe_unused) { struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch"); int ret = 0; diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c index d24b837951d4..c809463edbe5 100644 --- a/tools/perf/tests/fdarray.c +++ b/tools/perf/tests/fdarray.c @@ -25,7 +25,7 @@ static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE return printed + fdarray__fprintf(fda, fp); } -int test__fdarray__filter(void) +int test__fdarray__filter(int subtest __maybe_unused) { int nr_fds, expected_fd[2], fd, err = TEST_FAIL; struct fdarray *fda = fdarray__new(5, 5); @@ -103,7 +103,7 @@ out: return err; } -int test__fdarray__add(void) +int test__fdarray__add(int subtest __maybe_unused) { int err = TEST_FAIL; struct fdarray *fda = fdarray__new(2, 2); diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 7ed737019de7..8292948bc5f9 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -686,7 +686,7 @@ out: return err; } -int test__hists_cumulate(void) +int test__hists_cumulate(int subtest __maybe_unused) { int err = TEST_FAIL; struct machines machines; diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 818acf875dd0..ccb5b4921f25 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -104,7 +104,7 @@ out: return TEST_FAIL; } -int test__hists_filter(void) +int test__hists_filter(int subtest __maybe_unused) { int err = TEST_FAIL; struct machines machines; diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 8c102b011424..6243e2b2a245 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -274,7 +274,7 @@ static int validate_link(struct hists *leader, struct hists *other) return __validate_link(leader, 0) || __validate_link(other, 1); } -int test__hists_link(void) +int test__hists_link(int subtest __maybe_unused) { int err = -1; struct hists *hists, *first_hists; diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index adbebc852cc8..248beec1d917 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -576,7 +576,7 @@ out: return err; } -int test__hists_output(void) +int test__hists_output(int subtest __maybe_unused) { int err = TEST_FAIL; struct machines machines; diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index a2e2269aa093..a337a6da1f39 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -49,7 +49,7 @@ static int find_comm(struct perf_evlist *evlist, const char *comm) * when an event is disabled but a dummy software event is not disabled. If the * test passes %0 is returned, otherwise %-1 is returned. */ -int test__keep_tracking(void) +int test__keep_tracking(int subtest __maybe_unused) { struct record_opts opts = { .mmap_pages = UINT_MAX, diff --git a/tools/perf/tests/kmod-path.c b/tools/perf/tests/kmod-path.c index 08c433b4bf4f..d2af78193153 100644 --- a/tools/perf/tests/kmod-path.c +++ b/tools/perf/tests/kmod-path.c @@ -49,7 +49,7 @@ static int test_is_kernel_module(const char *path, int cpumode, bool expect) #define M(path, c, e) \ TEST_ASSERT_VAL("failed", !test_is_kernel_module(path, c, e)) -int test__kmod_path__parse(void) +int test__kmod_path__parse(int subtest __maybe_unused) { /* path alloc_name alloc_ext kmod comp name ext */ T("/xxxx/xxxx/x-x.ko", true , true , true, false, "[x_x]", NULL); diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index b4147634fb44..4350c455d06c 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -131,7 +131,7 @@ out: return ret; } -int test__llvm(void) +int test__llvm(int subtest __maybe_unused) { enum test_llvm__testcase i; diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 4495493c9431..359e98fcd94c 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c @@ -16,7 +16,7 @@ * Then it checks if the number of syscalls reported as perf events by * the kernel corresponds to the number of syscalls made. */ -int test__basic_mmap(void) +int test__basic_mmap(int subtest __maybe_unused) { int err = -1; union perf_event *event; diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 145050e2e544..6cdb97579c45 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -221,7 +221,7 @@ static int mmap_events(synth_cb synth) * * by using all thread objects. */ -int test__mmap_thread_lookup(void) +int test__mmap_thread_lookup(int subtest __maybe_unused) { /* perf_event__synthesize_threads synthesize */ TEST_ASSERT_VAL("failed with sythesizing all", diff --git a/tools/perf/tests/openat-syscall-all-cpus.c b/tools/perf/tests/openat-syscall-all-cpus.c index 2006485a2859..53c2273e8859 100644 --- a/tools/perf/tests/openat-syscall-all-cpus.c +++ b/tools/perf/tests/openat-syscall-all-cpus.c @@ -7,7 +7,7 @@ #include "debug.h" #include "stat.h" -int test__openat_syscall_event_on_all_cpus(void) +int test__openat_syscall_event_on_all_cpus(int subtest __maybe_unused) { int err = -1, fd, cpu; struct cpu_map *cpus; diff --git a/tools/perf/tests/openat-syscall-tp-fields.c b/tools/perf/tests/openat-syscall-tp-fields.c index 5e811cd8f1c3..eb99a105f31c 100644 --- a/tools/perf/tests/openat-syscall-tp-fields.c +++ b/tools/perf/tests/openat-syscall-tp-fields.c @@ -6,7 +6,7 @@ #include "tests.h" #include "debug.h" -int test__syscall_openat_tp_fields(void) +int test__syscall_openat_tp_fields(int subtest __maybe_unused) { struct record_opts opts = { .target = { diff --git a/tools/perf/tests/openat-syscall.c b/tools/perf/tests/openat-syscall.c index 033b54797b8a..1184f9ba6499 100644 --- a/tools/perf/tests/openat-syscall.c +++ b/tools/perf/tests/openat-syscall.c @@ -5,7 +5,7 @@ #include "debug.h" #include "tests.h" -int test__openat_syscall_event(void) +int test__openat_syscall_event(int subtest __maybe_unused) { int err = -1, fd; struct perf_evsel *evsel; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 636d7b42d844..abe8849d1d70 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -1765,7 +1765,7 @@ static void debug_warn(const char *warn, va_list params) fprintf(stderr, " Warning: %s\n", msg); } -int test__parse_events(void) +int test__parse_events(int subtest __maybe_unused) { int ret1, ret2 = 0; diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index 2c63ea658541..294c76b01b41 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -67,7 +67,7 @@ struct test_attr_event { * * Return: %0 on success, %-1 if the test fails. */ -int test__parse_no_sample_id_all(void) +int test__parse_no_sample_id_all(int subtest __maybe_unused) { int err; diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 7a228a2a070b..9d5f0b57c4c1 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -32,7 +32,7 @@ realloc: return cpu; } -int test__PERF_RECORD(void) +int test__PERF_RECORD(int subtest __maybe_unused) { struct record_opts opts = { .target = { diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index faa04e9d5d5f..1e2ba2602930 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -133,7 +133,7 @@ static struct list_head *test_terms_list(void) return &terms; } -int test__pmu(void) +int test__pmu(int subtest __maybe_unused) { char *format = test_format_dir_get(); LIST_HEAD(formats); diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c index 7760277c6def..7a52834ee0d0 100644 --- a/tools/perf/tests/python-use.c +++ b/tools/perf/tests/python-use.c @@ -4,11 +4,12 @@ #include #include +#include #include "tests.h" extern int verbose; -int test__python_use(void) +int test__python_use(int subtest __maybe_unused) { char *cmd; int ret; diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 30c02181e78b..5f23710b9fee 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -290,7 +290,7 @@ out_free: * checks sample format bits separately and together. If the test passes %0 is * returned, otherwise %-1 is returned. */ -int test__sample_parsing(void) +int test__sample_parsing(int subtest __maybe_unused) { const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; u64 sample_type; diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 5b83f56a3b6f..36e8ce1550e3 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c @@ -122,7 +122,7 @@ out_delete_evlist: return err; } -int test__sw_clock_freq(void) +int test__sw_clock_freq(int subtest __maybe_unused) { int ret; diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index a02af503100c..dfbd8d69ce89 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -305,7 +305,7 @@ out_free_nodes: * evsel->system_wide and evsel->tracking flags (respectively) with other events * sometimes enabled or disabled. */ -int test__switch_tracking(void) +int test__switch_tracking(int subtest __maybe_unused) { const char *sched_switch = "sched:sched_switch"; struct switch_tracking switch_tracking = { .tids = NULL, }; diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index add16385f13e..2dfff7ac8ef3 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c @@ -31,7 +31,7 @@ static void workload_exec_failed_signal(int signo __maybe_unused, * if the number of exit event reported by the kernel is 1 or not * in order to check the kernel returns correct number of event. */ -int test__task_exit(void) +int test__task_exit(int subtest __maybe_unused) { int err = -1; union perf_event *event; diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 3c8734a3abbc..204e4eeadea2 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -26,48 +26,48 @@ enum { struct test { const char *desc; - int (*func)(void); + int (*func)(int subtest); }; /* Tests */ -int test__vmlinux_matches_kallsyms(void); -int test__openat_syscall_event(void); -int test__openat_syscall_event_on_all_cpus(void); -int test__basic_mmap(void); -int test__PERF_RECORD(void); -int test__perf_evsel__roundtrip_name_test(void); -int test__perf_evsel__tp_sched_test(void); -int test__syscall_openat_tp_fields(void); -int test__pmu(void); -int test__attr(void); -int test__dso_data(void); -int test__dso_data_cache(void); -int test__dso_data_reopen(void); -int test__parse_events(void); -int test__hists_link(void); -int test__python_use(void); -int test__bp_signal(void); -int test__bp_signal_overflow(void); -int test__task_exit(void); -int test__sw_clock_freq(void); -int test__code_reading(void); -int test__sample_parsing(void); -int test__keep_tracking(void); -int test__parse_no_sample_id_all(void); -int test__dwarf_unwind(void); -int test__hists_filter(void); -int test__mmap_thread_lookup(void); -int test__thread_mg_share(void); -int test__hists_output(void); -int test__hists_cumulate(void); -int test__switch_tracking(void); -int test__fdarray__filter(void); -int test__fdarray__add(void); -int test__kmod_path__parse(void); -int test__thread_map(void); -int test__llvm(void); -int test__bpf(void); -int test_session_topology(void); +int test__vmlinux_matches_kallsyms(int subtest); +int test__openat_syscall_event(int subtest); +int test__openat_syscall_event_on_all_cpus(int subtest); +int test__basic_mmap(int subtest); +int test__PERF_RECORD(int subtest); +int test__perf_evsel__roundtrip_name_test(int subtest); +int test__perf_evsel__tp_sched_test(int subtest); +int test__syscall_openat_tp_fields(int subtest); +int test__pmu(int subtest); +int test__attr(int subtest); +int test__dso_data(int subtest); +int test__dso_data_cache(int subtest); +int test__dso_data_reopen(int subtest); +int test__parse_events(int subtest); +int test__hists_link(int subtest); +int test__python_use(int subtest); +int test__bp_signal(int subtest); +int test__bp_signal_overflow(int subtest); +int test__task_exit(int subtest); +int test__sw_clock_freq(int subtest); +int test__code_reading(int subtest); +int test__sample_parsing(int subtest); +int test__keep_tracking(int subtest); +int test__parse_no_sample_id_all(int subtest); +int test__dwarf_unwind(int subtest); +int test__hists_filter(int subtest); +int test__mmap_thread_lookup(int subtest); +int test__thread_mg_share(int subtest); +int test__hists_output(int subtest); +int test__hists_cumulate(int subtest); +int test__switch_tracking(int subtest); +int test__fdarray__filter(int subtest); +int test__fdarray__add(int subtest); +int test__kmod_path__parse(int subtest); +int test__thread_map(int subtest); +int test__llvm(int subtest); +int test__bpf(int subtest); +int test_session_topology(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 138a0e3431fa..2be02d303e82 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -4,7 +4,7 @@ #include "thread_map.h" #include "debug.h" -int test__thread_map(void) +int test__thread_map(int subtest __maybe_unused) { struct thread_map *map; diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c index 01fabb19d746..188b63140fc8 100644 --- a/tools/perf/tests/thread-mg-share.c +++ b/tools/perf/tests/thread-mg-share.c @@ -4,7 +4,7 @@ #include "map.h" #include "debug.h" -int test__thread_mg_share(void) +int test__thread_mg_share(int subtest __maybe_unused) { struct machines machines; struct machine *machine; diff --git a/tools/perf/tests/topology.c b/tools/perf/tests/topology.c index f5bb096c3bd9..98fe69ac553c 100644 --- a/tools/perf/tests/topology.c +++ b/tools/perf/tests/topology.c @@ -84,7 +84,7 @@ static int check_cpu_topology(char *path, struct cpu_map *map) return 0; } -int test_session_topology(void) +int test_session_topology(int subtest __maybe_unused) { char path[PATH_MAX]; struct cpu_map *map; diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index d677e018e504..f0bfc9e8fd9f 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -18,7 +18,7 @@ static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, #define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x)) -int test__vmlinux_matches_kallsyms(void) +int test__vmlinux_matches_kallsyms(int subtest __maybe_unused) { int err = -1; struct rb_node *nd; From e8c6d500447c577e669c24ec04cd4173fe9f9afb Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:48 +0000 Subject: [PATCH 017/271] perf test: Print result for each LLVM subtest Currently 'perf test llvm' and 'perf test BPF' have multiple sub-tests, but the result is provided in only one line: # perf test LLVM 35: Test LLVM searching and compiling : Ok This patch introduces sub-tests support, allowing 'perf test' to report result for each sub-tests: # perf test LLVM 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Ok 35.2: Test kbuild searching : Ok 35.3: Compile source for BPF prologue generation test : Ok When a failure happens: # cat ~/.perfconfig [llvm] clang-path = "/bin/false" # perf test LLVM 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : FAILED! 35.2: Test kbuild searching : Skip 35.3: Compile source for BPF prologue generation test : Skip And: # rm ~/.perfconfig # ./perf test LLVM 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Skip 35.2: Test kbuild searching : Skip 35.3: Compile source for BPF prologue generation test : Skip Skip by user: # ./perf test -s 1,`seq -s , 3 42` 1: vmlinux symtab matches kallsyms : Skip (user override) 2: detect openat syscall event : Ok ... 35: Test LLVM searching and compiling : Skip (user override) ... Suggested-and-Tested-by: Arnaldo Carvalho de Melo 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-4-git-send-email-wangnan0@huawei.com [ Changed so that func is not on an anonymous union ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 89 +++++++++++++++++++++++++++------ tools/perf/tests/llvm.c | 65 +++++++++++------------- tools/perf/tests/tests.h | 9 ++++ 3 files changed, 114 insertions(+), 49 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 9cf4892c061d..813660976217 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -160,6 +160,11 @@ static struct test generic_tests[] = { { .desc = "Test LLVM searching and compiling", .func = test__llvm, + .subtest = { + .skip_if_fail = true, + .get_nr = test__llvm_subtest_get_nr, + .get_desc = test__llvm_subtest_get_desc, + }, }, { .desc = "Test topology in session", @@ -237,6 +242,40 @@ static int run_test(struct test *test, int subtest) for (j = 0; j < ARRAY_SIZE(tests); j++) \ for (t = &tests[j][0]; t->func; t++) +static int test_and_print(struct test *t, bool force_skip, int subtest) +{ + int err; + + if (!force_skip) { + pr_debug("\n--- start ---\n"); + err = run_test(t, subtest); + pr_debug("---- end ----\n"); + } else { + pr_debug("\n--- force skipped ---\n"); + err = TEST_SKIP; + } + + if (!t->subtest.get_nr) + pr_debug("%s:", t->desc); + else + pr_debug("%s subtest %d:", t->desc, subtest); + + switch (err) { + case TEST_OK: + pr_info(" Ok\n"); + break; + case TEST_SKIP: + color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n"); + break; + case TEST_FAIL: + default: + color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); + break; + } + + return err; +} + static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) { struct test *t; @@ -264,21 +303,43 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) continue; } - pr_debug("\n--- start ---\n"); - err = run_test(t, i); - pr_debug("---- end ----\n%s:", t->desc); + if (!t->subtest.get_nr) { + test_and_print(t, false, -1); + } else { + int subn = t->subtest.get_nr(); + /* + * minus 2 to align with normal testcases. + * For subtest we print additional '.x' in number. + * for example: + * + * 35: Test LLVM searching and compiling : + * 35.1: Basic BPF llvm compiling test : Ok + */ + int subw = width > 2 ? width - 2 : width; + bool skip = false; + int subi; - switch (err) { - case TEST_OK: - pr_info(" Ok\n"); - break; - case TEST_SKIP: - color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n"); - break; - case TEST_FAIL: - default: - color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); - break; + if (subn <= 0) { + color_fprintf(stderr, PERF_COLOR_YELLOW, + " Skip (not compiled in)\n"); + continue; + } + pr_info("\n"); + + for (subi = 0; subi < subn; subi++) { + int len = strlen(t->subtest.get_desc(subi)); + + if (subw < len) + subw = len; + } + + for (subi = 0; subi < subn; subi++) { + pr_info("%2d.%1d: %-*s:", i, subi + 1, subw, + t->subtest.get_desc(subi)); + err = test_and_print(t, skip, subi); + if (err != TEST_OK && t->subtest.skip_if_fail) + skip = true; + } } } diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 4350c455d06c..06f45c1d4256 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -46,7 +46,7 @@ static struct { }, [LLVM_TESTCASE_BPF_PROLOGUE] = { .source = test_llvm__bpf_test_prologue_prog, - .desc = "Test BPF prologue generation", + .desc = "Compile source for BPF prologue generation test", }, }; @@ -131,44 +131,39 @@ out: return ret; } -int test__llvm(int subtest __maybe_unused) +int test__llvm(int subtest) { - enum test_llvm__testcase i; + int ret; + void *obj_buf = NULL; + size_t obj_buf_sz = 0; - for (i = 0; i < __LLVM_TESTCASE_MAX; i++) { - int ret; - void *obj_buf = NULL; - size_t obj_buf_sz = 0; + if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) + return TEST_FAIL; - ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, - i, false); + ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, + subtest, false); - if (ret == TEST_OK) { - ret = test__bpf_parsing(obj_buf, obj_buf_sz); - if (ret != TEST_OK) - pr_debug("Failed to parse test case '%s'\n", - bpf_source_table[i].desc); - } - free(obj_buf); - - switch (ret) { - case TEST_SKIP: - return TEST_SKIP; - case TEST_OK: - break; - default: - /* - * Test 0 is the basic LLVM test. If test 0 - * fail, the basic LLVM support not functional - * so the whole test should fail. If other test - * case fail, it can be fixed by adjusting - * config so don't report error. - */ - if (i == 0) - return TEST_FAIL; - else - return TEST_SKIP; + if (ret == TEST_OK) { + ret = test__bpf_parsing(obj_buf, obj_buf_sz); + if (ret != TEST_OK) { + pr_debug("Failed to parse test case '%s'\n", + bpf_source_table[subtest].desc); } } - return TEST_OK; + free(obj_buf); + + return ret; +} + +int test__llvm_subtest_get_nr(void) +{ + return __LLVM_TESTCASE_MAX; +} + +const char *test__llvm_subtest_get_desc(int subtest) +{ + if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX)) + return NULL; + + return bpf_source_table[subtest].desc; } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 204e4eeadea2..f92af527f080 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -1,6 +1,8 @@ #ifndef TESTS_H #define TESTS_H +#include + #define TEST_ASSERT_VAL(text, cond) \ do { \ if (!(cond)) { \ @@ -27,6 +29,11 @@ enum { struct test { const char *desc; int (*func)(int subtest); + struct { + bool skip_if_fail; + int (*get_nr)(void); + const char *(*get_desc)(int subtest); + } subtest; }; /* Tests */ @@ -66,6 +73,8 @@ int test__fdarray__add(int subtest); int test__kmod_path__parse(int subtest); int test__thread_map(int subtest); int test__llvm(int subtest); +const char *test__llvm_subtest_get_desc(int subtest); +int test__llvm_subtest_get_nr(void); int test__bpf(int subtest); int test_session_topology(int subtest); From 77a0cf682f7979554e10a6c605a1fef4f4197654 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:49 +0000 Subject: [PATCH 018/271] perf test: Print result for each BPF subtest This patch prints each sub-tests results for BPF testcases. Before: # ./perf test BPF 37: Test BPF filter : Ok After: # ./perf test BPF 37: Test BPF filter : 37.1: Test basic BPF filtering : Ok 37.2: Test BPF prologue generation : Ok When a failure happens: # cat ~/.perfconfig [llvm] clang-path = "/bin/false" # ./perf test BPF 37: Test BPF filter : 37.1: Test basic BPF filtering : Skip 37.2: Test BPF prologue generation : Skip Suggested-and-Tested-by: Arnaldo Carvalho de Melo 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-5-git-send-email-wangnan0@huawei.com [ Fixed up not to use .func in an anonymous union ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bpf.c | 40 ++++++++++++++++++++++++--------- tools/perf/tests/builtin-test.c | 5 +++++ tools/perf/tests/tests.h | 2 ++ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index 4efdc1607754..33689a0cf821 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -215,28 +215,46 @@ out: return ret; } -int test__bpf(int subtest __maybe_unused) +int test__bpf_subtest_get_nr(void) +{ + return (int)ARRAY_SIZE(bpf_testcase_table); +} + +const char *test__bpf_subtest_get_desc(int i) +{ + if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table)) + return NULL; + return bpf_testcase_table[i].desc; +} + +int test__bpf(int i) { - unsigned int i; int err; + if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table)) + return TEST_FAIL; + if (geteuid() != 0) { pr_debug("Only root can run BPF test\n"); return TEST_SKIP; } - for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) { - err = __test__bpf(i); - - if (err != TEST_OK) - return err; - } - - return TEST_OK; + err = __test__bpf(i); + return err; } #else -int test__bpf(void) +int test__bpf_subtest_get_nr(void) +{ + return 0; +} + +const char *test__bpf_subtest_get_desc(int i __maybe_unused) +{ + return NULL; +} + +int test__bpf(int i __maybe_unused) { pr_debug("Skip BPF test because BPF support is not compiled\n"); return TEST_SKIP; diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 813660976217..146ae9821c00 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -173,6 +173,11 @@ static struct test generic_tests[] = { { .desc = "Test BPF filter", .func = test__bpf, + .subtest = { + .skip_if_fail = true, + .get_nr = test__bpf_subtest_get_nr, + .get_desc = test__bpf_subtest_get_desc, + }, }, { .func = NULL, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index f92af527f080..a0733aaad081 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -76,6 +76,8 @@ int test__llvm(int subtest); const char *test__llvm_subtest_get_desc(int subtest); int test__llvm_subtest_get_nr(void); int test__bpf(int subtest); +const char *test__bpf_subtest_get_desc(int subtest); +int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); #if defined(__arm__) || defined(__aarch64__) From 5bcf2fe05318deb6fec209b4028d8a31f9f47221 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 17 Nov 2015 08:32:50 +0000 Subject: [PATCH 019/271] perf test: Mute test cases error messages if verbose == 0 Sometimes error messages in breaks the pretty output of 'perf test'. For example: # mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak} # perf test LLVM BPF 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Ok 35.2: Test kbuild searching : Ok 35.3: Compile source for BPF prologue generation test : Ok 37: Test BPF filter : 37.1: Test basic BPF filtering : Ok 37.2: Test BPF prologue generation :Failed to find the path for kernel: No such file or directory FAILED! This patch mute test cases thoroughly by redirect their stdout and stderr to /dev/null when verbose == 0. After applying this patch: # ./perf test LLVM BPF 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Ok 35.2: Test kbuild searching : Ok 35.3: Compile source for BPF prologue generation test : Ok 37: Test BPF filter : 37.1: Test basic BPF filtering : Ok 37.2: Test BPF prologue generation : FAILED! # ./perf test -v LLVM BPF 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : --- start --- test child forked, pid 13183 Kernel build dir is set to /lib/modules/4.3.0-rc4+/build set env: KBUILD_DIR=/lib/modules/4.3.0-rc4+/build ... bpf: config 'func=null_lseek file->f_mode offset orig' is ok Looking at the vmlinux_path (7 entries long) Failed to find the path for kernel: No such file or directory bpf_probe: failed to convert perf probe eventsFailed to add events selected by BPF test child finished with -1 ---- end ---- Test BPF filter subtest 1: FAILED! 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/1447749170-175898-6-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 146ae9821c00..2b1ade1aafc3 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -226,6 +226,18 @@ static int run_test(struct test *test, int subtest) if (!child) { pr_debug("test child forked, pid %d\n", getpid()); + if (!verbose) { + int nullfd = open("/dev/null", O_WRONLY); + if (nullfd >= 0) { + close(STDERR_FILENO); + close(STDOUT_FILENO); + + dup2(nullfd, STDOUT_FILENO); + dup2(STDOUT_FILENO, STDERR_FILENO); + close(nullfd); + } + } + err = test->func(subtest); exit(err); } From 05c8d802fa52ef17dbcce21c38b72b4a313eb036 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:12 +0900 Subject: [PATCH 020/271] 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(-) 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; From 9afcb420d6cfeadf5d872f395061c611536615fb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:20 +0900 Subject: [PATCH 021/271] 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(+) 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; From e96e4078e9a5ea150b3ad9a296440a7976439e4a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:22 +0900 Subject: [PATCH 022/271] 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(+) 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; } } From ebe9729c8c3171aa46ad5d7af40acdc29806689d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:24 +0900 Subject: [PATCH 023/271] 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(+) 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); From c4068f51d40df151a661a384ab1309b11d7f012e Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 19 Nov 2015 15:04:53 +0900 Subject: [PATCH 024/271] 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(-) 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) { From 8d5c340dfcd48751fdff301bb2a7e3f875652dcb Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:27 +0900 Subject: [PATCH 025/271] 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(+) 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 From 82de26abdc127172fd7453a61d35a9b33bf4f871 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:31 +0900 Subject: [PATCH 026/271] 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(+) 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; } From 1154c957607afdf5936ae14e1be27d7ca4e7bd30 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:33 +0900 Subject: [PATCH 027/271] 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(-) 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) { From 566c69c36e6178774dd484ea4a02b76f6bd0ede4 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 18 Nov 2015 15:40:35 +0900 Subject: [PATCH 028/271] 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(-) 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; } From 26e779245dd6f5270c0696860438e5c03d0780fd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:37 +0900 Subject: [PATCH 029/271] 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/Documentation/perf-report.txt | 1 + tools/perf/ui/stdio/hist.c | 55 ++++++++++++++++++++++++ tools/perf/util/callchain.c | 6 +++ tools/perf/util/callchain.h | 5 ++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 5ce8da1e1256..f7d81aac9188 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -181,6 +181,7 @@ OPTIONS - graph: use a graph tree, displaying absolute overhead rates. (default) - fractal: like graph, but displays relative rates. Each branch of the tree is considered as a new profiled object. + - folded: call chains are displayed in a line, separated by semicolons - none: disable call chain display. threshold is a percentage value which specifies a minimum percent to be diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index dfcbc90146ef..ea7984932d9a 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -260,6 +260,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree, return ret; } +static size_t __callchain__fprintf_folded(FILE *fp, struct callchain_node *node) +{ + const char *sep = symbol_conf.field_sep ?: ";"; + struct callchain_list *chain; + size_t ret = 0; + char bf[1024]; + bool first; + + if (!node) + return 0; + + ret += __callchain__fprintf_folded(fp, node->parent); + + first = (ret == 0); + list_for_each_entry(chain, &node->val, list) { + if (chain->ip >= PERF_CONTEXT_MAX) + continue; + ret += fprintf(fp, "%s%s", first ? "" : sep, + callchain_list__sym_name(chain, + bf, sizeof(bf), false)); + first = false; + } + + return ret; +} + +static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree, + u64 total_samples) +{ + size_t ret = 0; + u32 entries_printed = 0; + struct callchain_node *chain; + struct rb_node *rb_node = rb_first(tree); + + while (rb_node) { + double percent; + + chain = rb_entry(rb_node, struct callchain_node, rb_node); + percent = chain->hit * 100.0 / total_samples; + + ret += fprintf(fp, "%.2f%% ", percent); + ret += __callchain__fprintf_folded(fp, chain); + ret += fprintf(fp, "\n"); + if (++entries_printed == callchain_param.print_limit) + break; + + rb_node = rb_next(rb_node); + } + + return ret; +} + static size_t hist_entry_callchain__fprintf(struct hist_entry *he, u64 total_samples, int left_margin, FILE *fp) @@ -278,6 +330,9 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he, case CHAIN_FLAT: return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); break; + case CHAIN_FOLDED: + return callchain__fprintf_folded(fp, &he->sorted_chain, total_samples); + break; case CHAIN_NONE: break; default: 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 { From 5ab250cafcd884a2638b102239870bddca42ff88 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:39 +0900 Subject: [PATCH 030/271] 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/ui/browsers/hists.c | 8 +++++--- tools/perf/ui/gtk/hists.c | 8 ++------ tools/perf/ui/stdio/hist.c | 35 +++++++++++++++++----------------- tools/perf/util/callchain.c | 29 ++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 4 ++++ 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index fa9eb92c9e24..0b18857a36e8 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -592,7 +592,6 @@ static int hist_browser__show_callchain(struct hist_browser *browser, while (node) { struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); struct rb_node *next = rb_next(node); - u64 cumul = callchain_cumul_hits(child); struct callchain_list *chain; char folded_sign = ' '; int first = true; @@ -619,9 +618,12 @@ static int hist_browser__show_callchain(struct hist_browser *browser, browser->show_dso); if (was_first && need_percent) { - double percent = cumul * 100.0 / total; + char buf[64]; - if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) + callchain_node__scnprintf_value(child, buf, sizeof(buf), + total); + + if (asprintf(&alloc_str, "%s %s", buf, str) < 0) str = "Not enough memory!"; else str = alloc_str; diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 4b3585eed1e8..cff7bb9d9632 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -100,14 +100,10 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, struct callchain_list *chain; GtkTreeIter iter, new_parent; bool need_new_parent; - double percent; - u64 hits, child_total; + u64 child_total; node = rb_entry(nd, struct callchain_node, rb_node); - hits = callchain_cumul_hits(node); - percent = 100.0 * hits / total; - new_parent = *parent; need_new_parent = !has_single_node && (node->val_nr > 1); @@ -116,7 +112,7 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, gtk_tree_store_append(store, &iter, &new_parent); - scnprintf(buf, sizeof(buf), "%5.2f%%", percent); + callchain_node__scnprintf_value(node, buf, sizeof(buf), total); gtk_tree_store_set(store, &iter, 0, buf, -1); callchain_list__sym_name(chain, buf, sizeof(buf), false); diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index ea7984932d9a..f4de055cab9b 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -34,10 +34,10 @@ static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, return ret; } -static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, +static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_node *node, + struct callchain_list *chain, int depth, int depth_mask, int period, - u64 total_samples, u64 hits, - int left_margin) + u64 total_samples, int left_margin) { int i; size_t ret = 0; @@ -50,10 +50,9 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, else ret += fprintf(fp, " "); if (!period && i == depth - 1) { - double percent; - - percent = hits * 100.0 / total_samples; - ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); + ret += fprintf(fp, "--"); + ret += callchain_node__fprintf_value(node, fp, total_samples); + ret += fprintf(fp, "--"); } else ret += fprintf(fp, "%s", " "); } @@ -120,10 +119,9 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, left_margin); i = 0; list_for_each_entry(chain, &child->val, list) { - ret += ipchain__fprintf_graph(fp, chain, depth, + ret += ipchain__fprintf_graph(fp, child, chain, depth, new_depth_mask, i++, total_samples, - cumul, left_margin); } @@ -143,14 +141,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, if (callchain_param.mode == CHAIN_GRAPH_REL && remaining && remaining != total_samples) { + struct callchain_node rem_node = { + .hit = remaining, + }; if (!rem_sq_bracket) return ret; new_depth_mask &= ~(1 << (depth - 1)); - ret += ipchain__fprintf_graph(fp, &rem_hits, depth, + ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth, new_depth_mask, 0, total_samples, - remaining, left_margin); + left_margin); } return ret; @@ -243,12 +244,11 @@ static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *tree, struct rb_node *rb_node = rb_first(tree); while (rb_node) { - double percent; - chain = rb_entry(rb_node, struct callchain_node, rb_node); - percent = chain->hit * 100.0 / total_samples; - ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); + ret += fprintf(fp, " "); + ret += callchain_node__fprintf_value(chain, fp, total_samples); + ret += fprintf(fp, "\n"); ret += __callchain__fprintf_flat(fp, chain, total_samples); ret += fprintf(fp, "\n"); if (++entries_printed == callchain_param.print_limit) @@ -295,12 +295,11 @@ static size_t callchain__fprintf_folded(FILE *fp, struct rb_root *tree, struct rb_node *rb_node = rb_first(tree); while (rb_node) { - double percent; chain = rb_entry(rb_node, struct callchain_node, rb_node); - percent = chain->hit * 100.0 / total_samples; - ret += fprintf(fp, "%.2f%% ", percent); + ret += callchain_node__fprintf_value(chain, fp, total_samples); + ret += fprintf(fp, " "); ret += __callchain__fprintf_folded(fp, chain); ret += fprintf(fp, "\n"); if (++entries_printed == callchain_param.print_limit) 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); From 5e47f8ff406296bd078716d71283796ca5c6544b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:40 +0900 Subject: [PATCH 031/271] 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(+) 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, From f2af008695e0b54a58b76caecd52af7e6c97fb29 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:41 +0900 Subject: [PATCH 032/271] 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/Documentation/perf-report.txt | 13 +++-- tools/perf/builtin-report.c | 4 +- tools/perf/ui/stdio/hist.c | 10 +++- tools/perf/util/callchain.c | 62 ++++++++++++++++++++---- tools/perf/util/callchain.h | 10 +++- tools/perf/util/util.c | 3 +- 6 files changed, 84 insertions(+), 18 deletions(-) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index f7d81aac9188..dab99ed2b339 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -170,11 +170,11 @@ OPTIONS Dump raw trace in ASCII. -g:: ---call-graph=:: +--call-graph=:: Display call chains using type, min percent threshold, print limit, - call order, sort key and branch. Note that ordering of parameters is not - fixed so any parement can be given in an arbitraty order. One exception - is the print_limit which should be preceded by threshold. + call order, sort key, optional branch and value. Note that ordering of + parameters is not fixed so any parement can be given in an arbitraty order. + One exception is the print_limit which should be preceded by threshold. print_type can be either: - flat: single column, linear exposure of call chains. @@ -205,6 +205,11 @@ OPTIONS - branch: include last branch information in callgraph when available. Usually more convenient to use --branch-history for this. + value can be: + - percent: diplay overhead percent (default) + - period: display event period + - count: display event count + --children:: Accumulate callchain of children to parent entry so that then can show up in the output. The output will have a new "Children" column diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f256fac1e722..14428342b47b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -625,7 +625,7 @@ parse_percent_limit(const struct option *opt, const char *str, return 0; } -#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function" +#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent" const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n" CALLCHAIN_REPORT_HELP @@ -708,7 +708,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, "Only display entries with parent-match"), OPT_CALLBACK_DEFAULT('g', "call-graph", &report, - "print_type,threshold[,print_limit],order,sort_key[,branch]", + "print_type,threshold[,print_limit],order,sort_key[,branch],value", report_callchain_help, &report_parse_callchain_opt, callchain_default_opt), OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index f4de055cab9b..7ebc661be267 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -81,13 +81,14 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, int depth_mask, int left_margin) { struct rb_node *node, *next; - struct callchain_node *child; + struct callchain_node *child = NULL; struct callchain_list *chain; int new_depth_mask = depth_mask; u64 remaining; size_t ret = 0; int i; uint entries_printed = 0; + int cumul_count = 0; remaining = total_samples; @@ -99,6 +100,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, child = rb_entry(node, struct callchain_node, rb_node); cumul = callchain_cumul_hits(child); remaining -= cumul; + cumul_count += callchain_cumul_counts(child); /* * The depth mask manages the output of pipes that show @@ -148,6 +150,12 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, if (!rem_sq_bracket) return ret; + if (callchain_param.value == CCVAL_COUNT && child && child->parent) { + rem_node.count = child->parent->children_count - cumul_count; + if (rem_node.count <= 0) + return ret; + } + new_depth_mask &= ~(1 << (depth - 1)); ret += ipchain__fprintf_graph(fp, &rem_node, &rem_hits, depth, new_depth_mask, 0, total_samples, 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, }; /* From 18bb838129b08fb0009b1ba1dc2f748a9537ee89 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:42 +0900 Subject: [PATCH 033/271] perf hists browser: Factor out hist_browser__show_callchain_list() This function is to print a single callchain list entry. As this function will be used by other function, factor out to a separate function. Signed-off-by: Namhyung Kim Cc: Andi Kleen Cc: Brendan Gregg Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 72 +++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 0b18857a36e8..0746d41d9efe 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -574,6 +574,44 @@ static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_u #define LEVEL_OFFSET_STEP 3 +static int hist_browser__show_callchain_list(struct hist_browser *browser, + struct callchain_node *node, + struct callchain_list *chain, + unsigned short row, u64 total, + bool need_percent, int offset, + print_callchain_entry_fn print, + struct callchain_print_arg *arg) +{ + char bf[1024], *alloc_str; + const char *str; + + if (arg->row_offset != 0) { + arg->row_offset--; + return 0; + } + + alloc_str = NULL; + str = callchain_list__sym_name(chain, bf, sizeof(bf), + browser->show_dso); + + if (need_percent) { + char buf[64]; + + callchain_node__scnprintf_value(node, buf, sizeof(buf), + total); + + if (asprintf(&alloc_str, "%s %s", buf, str) < 0) + str = "Not enough memory!"; + else + str = alloc_str; + } + + print(browser, chain, str, offset, row, arg); + + free(alloc_str); + return 1; +} + static int hist_browser__show_callchain(struct hist_browser *browser, struct rb_root *root, int level, unsigned short row, u64 total, @@ -598,8 +636,6 @@ static int hist_browser__show_callchain(struct hist_browser *browser, int extra_offset = 0; list_for_each_entry(chain, &child->val, list) { - char bf[1024], *alloc_str; - const char *str; bool was_first = first; if (first) @@ -608,34 +644,16 @@ static int hist_browser__show_callchain(struct hist_browser *browser, extra_offset = LEVEL_OFFSET_STEP; folded_sign = callchain_list__folded(chain); - if (arg->row_offset != 0) { - arg->row_offset--; - goto do_next; - } - alloc_str = NULL; - str = callchain_list__sym_name(chain, bf, sizeof(bf), - browser->show_dso); + row += hist_browser__show_callchain_list(browser, child, + chain, row, total, + was_first && need_percent, + offset + extra_offset, + print, arg); - if (was_first && need_percent) { - char buf[64]; - - callchain_node__scnprintf_value(child, buf, sizeof(buf), - total); - - if (asprintf(&alloc_str, "%s %s", buf, str) < 0) - str = "Not enough memory!"; - else - str = alloc_str; - } - - print(browser, chain, str, offset + extra_offset, row, arg); - - free(alloc_str); - - if (is_output_full(browser, ++row)) + if (is_output_full(browser, row)) goto out; -do_next: + if (folded_sign == '+') break; } From 4b3a3212233a042f48b7b8fedc64933e1ccd8643 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:43 +0900 Subject: [PATCH 034/271] 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/ui/browsers/hists.c | 122 ++++++++++++++++++++++++++++++++- tools/perf/util/callchain.c | 44 ++++++++++++ tools/perf/util/callchain.h | 2 + 3 files changed, 166 insertions(+), 2 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 0746d41d9efe..c44af461a68f 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -178,12 +178,44 @@ static int callchain_node__count_rows_rb_tree(struct callchain_node *node) return n; } +static int callchain_node__count_flat_rows(struct callchain_node *node) +{ + struct callchain_list *chain; + char folded_sign = 0; + int n = 0; + + list_for_each_entry(chain, &node->parent_val, list) { + if (!folded_sign) { + /* only check first chain list entry */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + return 1; + } + n++; + } + + list_for_each_entry(chain, &node->val, list) { + if (!folded_sign) { + /* node->parent_val list might be empty */ + folded_sign = callchain_list__folded(chain); + if (folded_sign == '+') + return 1; + } + n++; + } + + return n; +} + static int callchain_node__count_rows(struct callchain_node *node) { struct callchain_list *chain; bool unfolded = false; int n = 0; + if (callchain_param.mode == CHAIN_FLAT) + return callchain_node__count_flat_rows(node); + list_for_each_entry(chain, &node->val, list) { ++n; unfolded = chain->unfolded; @@ -263,7 +295,7 @@ static void callchain_node__init_have_children(struct callchain_node *node, chain = list_entry(node->val.next, struct callchain_list, list); chain->has_children = has_sibling; - if (!list_empty(&node->val)) { + if (node->val.next != node->val.prev) { chain = list_entry(node->val.prev, struct callchain_list, list); chain->has_children = !RB_EMPTY_ROOT(&node->rb_root); } @@ -279,6 +311,8 @@ static void callchain__init_have_children(struct rb_root *root) for (nd = rb_first(root); nd; nd = rb_next(nd)) { struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); callchain_node__init_have_children(node, has_sibling); + if (callchain_param.mode == CHAIN_FLAT) + callchain_node__make_parent_list(node); } } @@ -612,6 +646,83 @@ static int hist_browser__show_callchain_list(struct hist_browser *browser, return 1; } +static int hist_browser__show_callchain_flat(struct hist_browser *browser, + struct rb_root *root, + unsigned short row, u64 total, + print_callchain_entry_fn print, + struct callchain_print_arg *arg, + check_output_full_fn is_output_full) +{ + struct rb_node *node; + int first_row = row, offset = LEVEL_OFFSET_STEP; + bool need_percent; + + node = rb_first(root); + need_percent = node && rb_next(node); + + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + struct callchain_list *chain; + char folded_sign = ' '; + int first = true; + int extra_offset = 0; + + list_for_each_entry(chain, &child->parent_val, list) { + bool was_first = first; + + if (first) + first = false; + else if (need_percent) + extra_offset = LEVEL_OFFSET_STEP; + + folded_sign = callchain_list__folded(chain); + + row += hist_browser__show_callchain_list(browser, child, + chain, row, total, + was_first && need_percent, + offset + extra_offset, + print, arg); + + if (is_output_full(browser, row)) + goto out; + + if (folded_sign == '+') + goto next; + } + + list_for_each_entry(chain, &child->val, list) { + bool was_first = first; + + if (first) + first = false; + else if (need_percent) + extra_offset = LEVEL_OFFSET_STEP; + + folded_sign = callchain_list__folded(chain); + + row += hist_browser__show_callchain_list(browser, child, + chain, row, total, + was_first && need_percent, + offset + extra_offset, + print, arg); + + if (is_output_full(browser, row)) + goto out; + + if (folded_sign == '+') + break; + } + +next: + if (is_output_full(browser, row)) + break; + node = next; + } +out: + return row - first_row; +} + static int hist_browser__show_callchain(struct hist_browser *browser, struct rb_root *root, int level, unsigned short row, u64 total, @@ -864,10 +975,17 @@ static int hist_browser__show_entry(struct hist_browser *browser, total = entry->stat.period; } - printed += hist_browser__show_callchain(browser, + if (callchain_param.mode == CHAIN_FLAT) { + printed += hist_browser__show_callchain_flat(browser, + &entry->sorted_chain, row, total, + hist_browser__show_callchain_entry, &arg, + hist_browser__check_output_full); + } else { + printed += hist_browser__show_callchain(browser, &entry->sorted_chain, 1, row, total, hist_browser__show_callchain_entry, &arg, hist_browser__check_output_full); + } if (arg.is_current_entry) browser->he_selection = entry; 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 */ From 8c430a34869946f1f5852f02d910ceef80040be5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:44 +0900 Subject: [PATCH 035/271] perf hists browser: Support folded callchains The folded callchain mode prints all chains in a single line. Currently perf report --tui doesn't support folded callchains. Like flat callchains, only leaf nodes are added to the final rbtree so it should show entries in parent nodes. To do that, add flat_val list to struct callchain_node and show them along with the (normal) val list. For example, folded callchain looks like below: $ perf report -g folded --tui Samples: 234 of event 'cycles:pp', Event count (approx.): 32605268 Overhead Command Shared Object Symbol - 39.93% swapper [kernel.vmlinux] [k] intel_idle + 28.63% intel_idle; cpuidle_enter_state; cpuidle_enter; ... + 11.30% intel_idle; cpuidle_enter_state; cpuidle_enter; ... 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-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 125 ++++++++++++++++++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index c44af461a68f..a211b7b6a81e 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -207,6 +207,11 @@ static int callchain_node__count_flat_rows(struct callchain_node *node) return n; } +static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused) +{ + return 1; +} + static int callchain_node__count_rows(struct callchain_node *node) { struct callchain_list *chain; @@ -215,6 +220,8 @@ static int callchain_node__count_rows(struct callchain_node *node) if (callchain_param.mode == CHAIN_FLAT) return callchain_node__count_flat_rows(node); + else if (callchain_param.mode == CHAIN_FOLDED) + return callchain_node__count_folded_rows(node); list_for_each_entry(chain, &node->val, list) { ++n; @@ -311,7 +318,8 @@ static void callchain__init_have_children(struct rb_root *root) for (nd = rb_first(root); nd; nd = rb_next(nd)) { struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); callchain_node__init_have_children(node, has_sibling); - if (callchain_param.mode == CHAIN_FLAT) + if (callchain_param.mode == CHAIN_FLAT || + callchain_param.mode == CHAIN_FOLDED) callchain_node__make_parent_list(node); } } @@ -723,6 +731,116 @@ out: return row - first_row; } +static char *hist_browser__folded_callchain_str(struct hist_browser *browser, + struct callchain_list *chain, + char *value_str, char *old_str) +{ + char bf[1024]; + const char *str; + char *new; + + str = callchain_list__sym_name(chain, bf, sizeof(bf), + browser->show_dso); + if (old_str) { + if (asprintf(&new, "%s%s%s", old_str, + symbol_conf.field_sep ?: ";", str) < 0) + new = NULL; + } else { + if (value_str) { + if (asprintf(&new, "%s %s", value_str, str) < 0) + new = NULL; + } else { + if (asprintf(&new, "%s", str) < 0) + new = NULL; + } + } + return new; +} + +static int hist_browser__show_callchain_folded(struct hist_browser *browser, + struct rb_root *root, + unsigned short row, u64 total, + print_callchain_entry_fn print, + struct callchain_print_arg *arg, + check_output_full_fn is_output_full) +{ + struct rb_node *node; + int first_row = row, offset = LEVEL_OFFSET_STEP; + bool need_percent; + + node = rb_first(root); + need_percent = node && rb_next(node); + + while (node) { + struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); + struct rb_node *next = rb_next(node); + struct callchain_list *chain, *first_chain = NULL; + int first = true; + char *value_str = NULL, *value_str_alloc = NULL; + char *chain_str = NULL, *chain_str_alloc = NULL; + + if (arg->row_offset != 0) { + arg->row_offset--; + goto next; + } + + if (need_percent) { + char buf[64]; + + callchain_node__scnprintf_value(child, buf, sizeof(buf), total); + if (asprintf(&value_str, "%s", buf) < 0) { + value_str = (char *)"<...>"; + goto do_print; + } + value_str_alloc = value_str; + } + + list_for_each_entry(chain, &child->parent_val, list) { + chain_str = hist_browser__folded_callchain_str(browser, + chain, value_str, chain_str); + if (first) { + first = false; + first_chain = chain; + } + + if (chain_str == NULL) { + chain_str = (char *)"Not enough memory!"; + goto do_print; + } + + chain_str_alloc = chain_str; + } + + list_for_each_entry(chain, &child->val, list) { + chain_str = hist_browser__folded_callchain_str(browser, + chain, value_str, chain_str); + if (first) { + first = false; + first_chain = chain; + } + + if (chain_str == NULL) { + chain_str = (char *)"Not enough memory!"; + goto do_print; + } + + chain_str_alloc = chain_str; + } + +do_print: + print(browser, first_chain, chain_str, offset, row++, arg); + free(value_str_alloc); + free(chain_str_alloc); + +next: + if (is_output_full(browser, row)) + break; + node = next; + } + + return row - first_row; +} + static int hist_browser__show_callchain(struct hist_browser *browser, struct rb_root *root, int level, unsigned short row, u64 total, @@ -980,6 +1098,11 @@ static int hist_browser__show_entry(struct hist_browser *browser, &entry->sorted_chain, row, total, hist_browser__show_callchain_entry, &arg, hist_browser__check_output_full); + } else if (callchain_param.mode == CHAIN_FOLDED) { + printed += hist_browser__show_callchain_folded(browser, + &entry->sorted_chain, row, total, + hist_browser__show_callchain_entry, &arg, + hist_browser__check_output_full); } else { printed += hist_browser__show_callchain(browser, &entry->sorted_chain, 1, row, total, From 3cd99dfd1c87067fb28a19fee76500aed56d7c8f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:45 +0900 Subject: [PATCH 036/271] perf ui/gtk: Support flat callchains The flat callchain mode is to print all chains in a simple flat hierarchy so make it easy to see. Currently perf report --gtk 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. See the previous commit on TUI support for more information. 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: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/gtk/hists.c | 80 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index cff7bb9d9632..0b24cd6d38a4 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -89,8 +89,71 @@ void perf_gtk__init_hpp(void) perf_gtk__hpp_color_overhead_acc; } -static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, - GtkTreeIter *parent, int col, u64 total) +static void perf_gtk__add_callchain_flat(struct rb_root *root, GtkTreeStore *store, + GtkTreeIter *parent, int col, u64 total) +{ + struct rb_node *nd; + bool has_single_node = (rb_first(root) == rb_last(root)); + + for (nd = rb_first(root); nd; nd = rb_next(nd)) { + struct callchain_node *node; + struct callchain_list *chain; + GtkTreeIter iter, new_parent; + bool need_new_parent; + + node = rb_entry(nd, struct callchain_node, rb_node); + + new_parent = *parent; + need_new_parent = !has_single_node; + + callchain_node__make_parent_list(node); + + list_for_each_entry(chain, &node->parent_val, list) { + char buf[128]; + + gtk_tree_store_append(store, &iter, &new_parent); + + callchain_node__scnprintf_value(node, buf, sizeof(buf), total); + gtk_tree_store_set(store, &iter, 0, buf, -1); + + callchain_list__sym_name(chain, buf, sizeof(buf), false); + gtk_tree_store_set(store, &iter, col, buf, -1); + + if (need_new_parent) { + /* + * Only show the top-most symbol in a callchain + * if it's not the only callchain. + */ + new_parent = iter; + need_new_parent = false; + } + } + + list_for_each_entry(chain, &node->val, list) { + char buf[128]; + + gtk_tree_store_append(store, &iter, &new_parent); + + callchain_node__scnprintf_value(node, buf, sizeof(buf), total); + gtk_tree_store_set(store, &iter, 0, buf, -1); + + callchain_list__sym_name(chain, buf, sizeof(buf), false); + gtk_tree_store_set(store, &iter, col, buf, -1); + + if (need_new_parent) { + /* + * Only show the top-most symbol in a callchain + * if it's not the only callchain. + */ + new_parent = iter; + need_new_parent = false; + } + } + } +} + +static void perf_gtk__add_callchain_graph(struct rb_root *root, GtkTreeStore *store, + GtkTreeIter *parent, int col, u64 total) { struct rb_node *nd; bool has_single_node = (rb_first(root) == rb_last(root)); @@ -134,11 +197,20 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, child_total = total; /* Now 'iter' contains info of the last callchain_list */ - perf_gtk__add_callchain(&node->rb_root, store, &iter, col, - child_total); + perf_gtk__add_callchain_graph(&node->rb_root, store, &iter, col, + child_total); } } +static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, + GtkTreeIter *parent, int col, u64 total) +{ + if (callchain_param.mode == CHAIN_FLAT) + perf_gtk__add_callchain_flat(root, store, parent, col, total); + else + perf_gtk__add_callchain_graph(root, store, parent, col, total); +} + static void on_row_activated(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col __maybe_unused, gpointer user_data __maybe_unused) From 2c6caff2b26fde8f3f87183f8c97f2cebfdbcb98 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Nov 2015 14:45:46 +0900 Subject: [PATCH 037/271] perf ui/gtk: Support folded callchains The folded callchain mode is to print all chains in a single line. Currently perf report --gtk doesn't support folded callchains. Like flat callchains, only leaf nodes are added to the final rbtree so it should show entries in parent nodes. 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: Pekka Enberg Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1447047946-1691-11-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/gtk/hists.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 0b24cd6d38a4..467717276ab6 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -152,6 +152,66 @@ static void perf_gtk__add_callchain_flat(struct rb_root *root, GtkTreeStore *sto } } +static void perf_gtk__add_callchain_folded(struct rb_root *root, GtkTreeStore *store, + GtkTreeIter *parent, int col, u64 total) +{ + struct rb_node *nd; + + for (nd = rb_first(root); nd; nd = rb_next(nd)) { + struct callchain_node *node; + struct callchain_list *chain; + GtkTreeIter iter; + char buf[64]; + char *str, *str_alloc = NULL; + bool first = true; + + node = rb_entry(nd, struct callchain_node, rb_node); + + callchain_node__make_parent_list(node); + + list_for_each_entry(chain, &node->parent_val, list) { + char name[1024]; + + callchain_list__sym_name(chain, name, sizeof(name), false); + + if (asprintf(&str, "%s%s%s", + first ? "" : str_alloc, + first ? "" : symbol_conf.field_sep ?: "; ", + name) < 0) + return; + + first = false; + free(str_alloc); + str_alloc = str; + } + + list_for_each_entry(chain, &node->val, list) { + char name[1024]; + + callchain_list__sym_name(chain, name, sizeof(name), false); + + if (asprintf(&str, "%s%s%s", + first ? "" : str_alloc, + first ? "" : symbol_conf.field_sep ?: "; ", + name) < 0) + return; + + first = false; + free(str_alloc); + str_alloc = str; + } + + gtk_tree_store_append(store, &iter, parent); + + callchain_node__scnprintf_value(node, buf, sizeof(buf), total); + gtk_tree_store_set(store, &iter, 0, buf, -1); + + gtk_tree_store_set(store, &iter, col, str, -1); + + free(str_alloc); + } +} + static void perf_gtk__add_callchain_graph(struct rb_root *root, GtkTreeStore *store, GtkTreeIter *parent, int col, u64 total) { @@ -207,6 +267,8 @@ static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, { if (callchain_param.mode == CHAIN_FLAT) perf_gtk__add_callchain_flat(root, store, parent, col, total); + else if (callchain_param.mode == CHAIN_FOLDED) + perf_gtk__add_callchain_folded(root, store, parent, col, total); else perf_gtk__add_callchain_graph(root, store, parent, col, total); } From 10013ebb5d7856c243541870f4e62fed68253e88 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 22 Oct 2015 15:07:20 -0700 Subject: [PATCH 038/271] x86: Add an inlined __copy_from_user_nmi() variant Add a inlined __ variant of copy_from_user_nmi. The inlined variant allows the user to: - batch the access_ok() check for multiple accesses - avoid having a pagefault_disable/enable() on every access if the caller already ensures disabled page faults due to its context. - get all the optimizations in copy_*_user() for small constant sized transfers It is just a define to __copy_from_user_inatomic(). Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1445551641-13379-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uaccess.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 09b1b0ab94b7..660458af425d 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -745,5 +745,14 @@ copy_to_user(void __user *to, const void *from, unsigned long n) #undef __copy_from_user_overflow #undef __copy_to_user_overflow +/* + * We rely on the nested NMI work to allow atomic faults from the NMI path; the + * nested NMI paths are careful to preserve CR2. + * + * Caller must use pagefault_enable/disable, or run in interrupt context, + * and also do a uaccess_ok() check + */ +#define __copy_from_user_nmi __copy_from_user_inatomic + #endif /* _ASM_X86_UACCESS_H */ From 75925e1ad7f5a4e867bd14ff8e7f114ea1596434 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 22 Oct 2015 15:07:21 -0700 Subject: [PATCH 039/271] perf/x86: Optimize stack walk user accesses Change the perf user stack walking to use the new __copy_from_user_nmi(), and split each access into word sized transfer sizes. This allows to inline the complete access and optimize it all into a single load. The main advantage is that this avoids the overhead of double page faults. When normal copy_from_user() fails it reexecutes the copy to compute an accurate number of non copied bytes. This leads to executing the expensive page fault twice. While walking stacks having a fault at some point is relatively common (typically when some part of the program isn't compiled with frame pointers), so this is a large overhead. With the optimized copies we avoid this problem because they only do all accesses once. And of course they're much faster too when the access does not fault because they're just single instructions instead of complex function calls. While profiling a kernel build with -g, the patch brings down the average time of the PMI handler from 966ns to 552ns (-43%). Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1445551641-13379-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 2bf79d7c97df..9dfbba5ce6e8 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -2250,12 +2250,19 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) ss_base = get_segment_base(regs->ss); fp = compat_ptr(ss_base + regs->bp); + pagefault_disable(); while (entry->nr < PERF_MAX_STACK_DEPTH) { unsigned long bytes; frame.next_frame = 0; frame.return_address = 0; - bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); + if (!access_ok(VERIFY_READ, fp, 8)) + break; + + bytes = __copy_from_user_nmi(&frame.next_frame, fp, 4); + if (bytes != 0) + break; + bytes = __copy_from_user_nmi(&frame.return_address, fp+4, 4); if (bytes != 0) break; @@ -2265,6 +2272,7 @@ perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) perf_callchain_store(entry, cs_base + frame.return_address); fp = compat_ptr(ss_base + frame.next_frame); } + pagefault_enable(); return 1; } #else @@ -2302,12 +2310,19 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) if (perf_callchain_user32(regs, entry)) return; + pagefault_disable(); while (entry->nr < PERF_MAX_STACK_DEPTH) { unsigned long bytes; frame.next_frame = NULL; frame.return_address = 0; - bytes = copy_from_user_nmi(&frame, fp, sizeof(frame)); + if (!access_ok(VERIFY_READ, fp, 16)) + break; + + bytes = __copy_from_user_nmi(&frame.next_frame, fp, 8); + if (bytes != 0) + break; + bytes = __copy_from_user_nmi(&frame.return_address, fp+8, 8); if (bytes != 0) break; @@ -2315,8 +2330,9 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) break; perf_callchain_store(entry, frame.return_address); - fp = frame.next_frame; + fp = (void __user *)frame.next_frame; } + pagefault_enable(); } /* From b16a5b52eb90d92b597257778e51e1fdc6423e64 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 20 Oct 2015 11:46:34 -0700 Subject: [PATCH 040/271] perf/x86: Add option to disable reading branch flags/cycles With LBRv5 reading the extra LBR flags like mispredict, TSX, cycles is not free anymore, as it has moved to a separate MSR. For callstack mode we don't need any of this information; so we can avoid the unnecessary MSR read. Add flags to the perf interface where perf record can request not collecting this information. Add branch_sample_type flags for CYCLES and FLAGS. It's a bit unusual for branch_sample_types to be negative (disable), not positive (enable), but since the legacy ABI reported the flags we need some form of explicit disabling to avoid breaking the ABI. After we have the flags the x86 perf code can keep track if any users need the flags. If noone needs it the information is not collected. This cuts down the cost of LBR callstack on Skylake significantly. Profiling a kernel build with LBR call stack the average run time of the PMI handler drops by 43%. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: acme@kernel.org Cc: jolsa@kernel.org Link: http://lkml.kernel.org/r/1445366797-30894-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_lbr.c | 19 +++++++++++++++++-- include/uapi/linux/perf_event.h | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 659f01e165d5..e2fad0cdca2f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -42,6 +42,13 @@ static enum { #define LBR_FAR_BIT 8 /* do not capture far branches */ #define LBR_CALL_STACK_BIT 9 /* enable call stack */ +/* + * Following bit only exists in Linux; we mask it out before writing it to + * the actual MSR. But it helps the constraint perf code to understand + * that this is a separate configuration. + */ +#define LBR_NO_INFO_BIT 63 /* don't read LBR_INFO. */ + #define LBR_KERNEL (1 << LBR_KERNEL_BIT) #define LBR_USER (1 << LBR_USER_BIT) #define LBR_JCC (1 << LBR_JCC_BIT) @@ -52,6 +59,7 @@ static enum { #define LBR_IND_JMP (1 << LBR_IND_JMP_BIT) #define LBR_FAR (1 << LBR_FAR_BIT) #define LBR_CALL_STACK (1 << LBR_CALL_STACK_BIT) +#define LBR_NO_INFO (1ULL << LBR_NO_INFO_BIT) #define LBR_PLM (LBR_KERNEL | LBR_USER) @@ -152,7 +160,7 @@ static void __intel_pmu_lbr_enable(bool pmi) * did not change. */ if (cpuc->lbr_sel) - lbr_select = cpuc->lbr_sel->config; + lbr_select = cpuc->lbr_sel->config & x86_pmu.lbr_sel_mask; if (!pmi) wrmsrl(MSR_LBR_SELECT, lbr_select); @@ -422,6 +430,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) */ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) { + bool need_info = !(cpuc->lbr_sel->config & LBR_NO_INFO); unsigned long mask = x86_pmu.lbr_nr - 1; int lbr_format = x86_pmu.intel_cap.lbr_format; u64 tos = intel_pmu_lbr_tos(); @@ -442,7 +451,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) rdmsrl(x86_pmu.lbr_from + lbr_idx, from); rdmsrl(x86_pmu.lbr_to + lbr_idx, to); - if (lbr_format == LBR_FORMAT_INFO) { + if (lbr_format == LBR_FORMAT_INFO && need_info) { u64 info; rdmsrl(MSR_LBR_INFO_0 + lbr_idx, info); @@ -590,6 +599,7 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) if (v != LBR_IGN) mask |= v; } + reg = &event->hw.branch_reg; reg->idx = EXTRA_REG_LBR; @@ -600,6 +610,11 @@ static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event) */ reg->config = mask ^ x86_pmu.lbr_sel_mask; + if ((br_type & PERF_SAMPLE_BRANCH_NO_CYCLES) && + (br_type & PERF_SAMPLE_BRANCH_NO_FLAGS) && + (x86_pmu.intel_cap.lbr_format == LBR_FORMAT_INFO)) + reg->config |= LBR_NO_INFO; + return 0; } diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index d801bb0d9f6d..1afe9623c1a7 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -171,6 +171,9 @@ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT = 12, /* indirect jumps */ PERF_SAMPLE_BRANCH_CALL_SHIFT = 13, /* direct call */ + PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT = 14, /* no flags */ + PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT = 15, /* no cycles */ + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; @@ -192,6 +195,9 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_IND_JUMP = 1U << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT, PERF_SAMPLE_BRANCH_CALL = 1U << PERF_SAMPLE_BRANCH_CALL_SHIFT, + PERF_SAMPLE_BRANCH_NO_FLAGS = 1U << PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT, + PERF_SAMPLE_BRANCH_NO_CYCLES = 1U << PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT, + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; From 24cc12b17679f8e9046746f92fd377f589efc163 Mon Sep 17 00:00:00 2001 From: Takao Indoh Date: Wed, 4 Nov 2015 14:22:32 +0900 Subject: [PATCH 041/271] perf/x86/intel/pt: Add interface to stop Intel PT logging This patch add a function for external components to stop Intel PT. Basically this function is used when kernel panic occurs. When it is called, the intel_pt driver disables Intel PT and saves its registers using pt_event_stop(), which is also used by pmu.stop handler. This function stops Intel PT on the CPU where it is working, therefore users of it need to call it for each CPU to stop all logging. Signed-off-by: Takao Indoh Signed-off-by: Peter Zijlstra (Intel) Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H.Peter Anvin Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: Vivek Goyal Link: http://lkml.kernel.org/r/1446614553-6072-2-git-send-email-indou.takao@jp.fujitsu.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/intel_pt.h | 10 ++++++++++ arch/x86/kernel/cpu/perf_event_intel_pt.c | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 arch/x86/include/asm/intel_pt.h diff --git a/arch/x86/include/asm/intel_pt.h b/arch/x86/include/asm/intel_pt.h new file mode 100644 index 000000000000..e1a411786bf5 --- /dev/null +++ b/arch/x86/include/asm/intel_pt.h @@ -0,0 +1,10 @@ +#ifndef _ASM_X86_INTEL_PT_H +#define _ASM_X86_INTEL_PT_H + +#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_INTEL) +void cpu_emergency_stop_pt(void); +#else +static inline void cpu_emergency_stop_pt(void) {} +#endif + +#endif /* _ASM_X86_INTEL_PT_H */ diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c index 868e1194337f..c0bbd1033b7c 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_pt.c +++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "perf_event.h" #include "intel_pt.h" @@ -1122,6 +1123,14 @@ static int pt_event_init(struct perf_event *event) return 0; } +void cpu_emergency_stop_pt(void) +{ + struct pt *pt = this_cpu_ptr(&pt_ctx); + + if (pt->handle.event) + pt_event_stop(pt->handle.event, PERF_EF_UPDATE); +} + static __init int pt_init(void) { int ret, cpu, prior_warn = 0; From da06a43d3f3f3df87416f654fe15d29fecb5e321 Mon Sep 17 00:00:00 2001 From: Takao Indoh Date: Wed, 4 Nov 2015 14:22:33 +0900 Subject: [PATCH 042/271] perf, x86: Stop Intel PT before kdump starts This patch stops Intel PT logging and saves its registers in memory before kdump is started. This feature is needed to prevent Intel PT from overwriting its log buffer after panic, and saved registers are needed to find the last position where Intel PT wrote data. After the crash dump is captured by kdump, users can retrieve the log buffer from the vmcore and use it to investigate bad kernel behavior. Signed-off-by: Takao Indoh Signed-off-by: Peter Zijlstra (Intel) Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: H.Peter Anvin Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: Vivek Goyal Link: http://lkml.kernel.org/r/1446614553-6072-3-git-send-email-indou.takao@jp.fujitsu.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/crash.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 2c1910f6717e..58f34319b29a 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -35,6 +35,7 @@ #include #include #include +#include /* Alignment required for elf header segment */ #define ELF_CORE_HEADER_ALIGN 4096 @@ -125,6 +126,11 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs) cpu_emergency_vmxoff(); cpu_emergency_svm_disable(); + /* + * Disable Intel PT to stop its logging + */ + cpu_emergency_stop_pt(); + disable_local_APIC(); } @@ -169,6 +175,11 @@ void native_machine_crash_shutdown(struct pt_regs *regs) cpu_emergency_vmxoff(); cpu_emergency_svm_disable(); + /* + * Disable Intel PT to stop its logging + */ + cpu_emergency_stop_pt(); + #ifdef CONFIG_X86_IO_APIC /* Prevent crash_kexec() from deadlocking on ioapic_lock. */ ioapic_zap_locks(); From b7883a1c4f75edb62fc49da6000c59fb881e3c7b Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 16 Nov 2015 16:21:07 -0800 Subject: [PATCH 043/271] perf/x86: Handle multiple umask bits for BDW CYCLE_ACTIVITY.* The earlier constraint fix for Broadwell CYCLE_ACTIVITY.* forced umask 8 to counter 2. For this it used UEVENT, to match the complete umask. The event list for Broadwell has an additional STALLS_L1D_PENDIND event that uses umask 8, but also sets other bits in the umask. The earlier strict umask match didn't handle this case. Add a new UBIT_EVENT constraint macro that only matches the specified bits in the umask. Then use that macro to handle CYCLE_ACTIVITY.* on Broadwell. The documented event also uses cmask, but there's no need to let the event scheduler know about the cmask, as the scheduling restriction is only tied to the umask. Reported-by: Grant Ayers Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1447719667-9998-1-git-send-email-andi@firstfloor.org [ Filled in the missing email address of Grant Ayers - hopefully I got the right one. ] Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.h | 4 ++++ arch/x86/kernel/cpu/perf_event_intel.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index ab18b8a91583..58402f6c1620 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -318,6 +318,10 @@ struct cpu_hw_events { #define INTEL_UEVENT_CONSTRAINT(c, n) \ EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) +/* Constraint on specific umask bit only + event */ +#define INTEL_UBIT_EVENT_CONSTRAINT(c, n) \ + EVENT_CONSTRAINT(c, n, ARCH_PERFMON_EVENTSEL_EVENT|(c)) + /* Like UEVENT_CONSTRAINT, but match flags too */ #define INTEL_FLAGS_UEVENT_CONSTRAINT(c, n) \ EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK|X86_ALL_EVENT_FLAGS) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index f63360be2238..61f25775eb1f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -255,7 +255,7 @@ struct event_constraint intel_bdw_event_constraints[] = { FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ FIXED_EVENT_CONSTRAINT(0x0300, 2), /* CPU_CLK_UNHALTED.REF */ INTEL_UEVENT_CONSTRAINT(0x148, 0x4), /* L1D_PEND_MISS.PENDING */ - INTEL_UEVENT_CONSTRAINT(0x8a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_MISS */ + INTEL_UBIT_EVENT_CONSTRAINT(0x8a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_MISS */ EVENT_CONSTRAINT_END }; From b26b218a1e9c5815cb8964e180b7fba3cd9bd509 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 17 Nov 2015 16:05:37 +0100 Subject: [PATCH 044/271] 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(-) 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; + if (max_stack <= 0) + return -EINVAL; - ret = entry(ip, thread, cb, arg); - if (ret) - return -ENOMEM; - - return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0; + return get_entries(&ui, cb, arg, max_stack); } From cb1dc22dce6e54dbd1eac213c9216e1aa57084da Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 18 Nov 2015 08:52:47 +0100 Subject: [PATCH 045/271] 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 | 45 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) 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; + /* + * 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; - 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); - 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; + } - while (!ret && (unw_step(&c) > 0) && max_stack--) { - unw_word_t ip; + max_stack = i; + } - unw_get_reg(&c, UNW_REG_IP, &ip); - ret = ip ? entry(ip, ui->thread, cb, arg) : 0; + /* + * Display what we got based on the order setup. + */ + for (i = 0; i < max_stack && !ret; i++) { + int j = i; + + if (callchain_param.order == ORDER_CALLER) + j = max_stack - i - 1; + ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; } return ret; From 8dc0564d809e3903834950e2d12f6d1d2fcff708 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 17 Nov 2015 16:05:39 +0100 Subject: [PATCH 046/271] perf test: Add callchain order setup for DWARF unwinder test Adding callchain order setup for DWARF unwinder test. The test now runs unwinder for both callee and caller orders. 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-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/dwarf-unwind.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 01f0b61de53d..b2357e8115a2 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) "krava_1", "test__dwarf_unwind" }; + /* + * The funcs[MAX_STACK] array index, based on the + * callchain order setup. + */ + int idx = callchain_param.order == ORDER_CALLER ? + MAX_STACK - *cnt - 1 : *cnt; if (*cnt >= MAX_STACK) { pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); @@ -63,8 +69,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) return -1; } - pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip); - return strcmp((const char *) symbol, funcs[(*cnt)++]); + (*cnt)++; + pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n", + symbol, entry->ip, funcs[idx]); + return strcmp((const char *) symbol, funcs[idx]); } __attribute__ ((noinline)) @@ -105,8 +113,16 @@ static int compare(void *p1, void *p2) /* Any possible value should be 'thread' */ struct thread *thread = *(struct thread **)p1; - if (global_unwind_retval == -INT_MAX) + if (global_unwind_retval == -INT_MAX) { + /* Call unwinder twice for both callchain orders. */ + callchain_param.order = ORDER_CALLER; + global_unwind_retval = unwind_thread(thread); + if (!global_unwind_retval) { + callchain_param.order = ORDER_CALLEE; + global_unwind_retval = unwind_thread(thread); + } + } return p1 - p2; } From 8bd508b001629a5d836987d9a0702a6bfc4fc705 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Nov 2015 14:01:19 +0100 Subject: [PATCH 047/271] 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(-) 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 */ From 30862f2c5725c46afcfab5af710fdf5163bf0f81 Mon Sep 17 00:00:00 2001 From: Taeung Song Date: Tue, 17 Nov 2015 22:53:21 +0900 Subject: [PATCH 048/271] perf tools: Add 'perf config' command The perf configuration file contains many variables to change various aspects of each of its tools, including output, disk usage, etc. But looking at the state of configuration is difficult and there's no documentation about config variables except for the variables in perfconfig.example exist. So this patch adds a 'perf-config' command with a '--list' option. perf config [options] display current perf config variables. # perf config -l | --list Signed-off-by: Taeung Song Acked-by: Namhyung Kim Cc: Jiri Olsa Link: http://lkml.kernel.org/r/1447768424-17327-1-git-send-email-treeze.taeung@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Build | 1 + tools/perf/builtin-config.c | 66 +++++++++++++++++++++++++++++++++++++ tools/perf/builtin.h | 1 + tools/perf/command-list.txt | 1 + tools/perf/perf.c | 1 + 5 files changed, 70 insertions(+) create mode 100644 tools/perf/builtin-config.c diff --git a/tools/perf/Build b/tools/perf/Build index 72237455b400..2c7aaf2ba119 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -1,5 +1,6 @@ perf-y += builtin-bench.o perf-y += builtin-annotate.o +perf-y += builtin-config.o perf-y += builtin-diff.o perf-y += builtin-evlist.o perf-y += builtin-help.o diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c new file mode 100644 index 000000000000..427ea7a705b8 --- /dev/null +++ b/tools/perf/builtin-config.c @@ -0,0 +1,66 @@ +/* + * builtin-config.c + * + * Copyright (C) 2015, Taeung Song + * + */ +#include "builtin.h" + +#include "perf.h" + +#include "util/cache.h" +#include "util/parse-options.h" +#include "util/util.h" +#include "util/debug.h" + +static const char * const config_usage[] = { + "perf config [options]", + NULL +}; + +enum actions { + ACTION_LIST = 1 +} actions; + +static struct option config_options[] = { + OPT_SET_UINT('l', "list", &actions, + "show current config variables", ACTION_LIST), + OPT_END() +}; + +static int show_config(const char *key, const char *value, + void *cb __maybe_unused) +{ + if (value) + printf("%s=%s\n", key, value); + else + printf("%s\n", key); + + return 0; +} + +int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) +{ + int ret = 0; + + argc = parse_options(argc, argv, config_options, config_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + switch (actions) { + case ACTION_LIST: + if (argc) { + pr_err("Error: takes no arguments\n"); + parse_options_usage(config_usage, config_options, "l", 1); + } else { + ret = perf_config(show_config, NULL); + if (ret < 0) + pr_err("Nothing configured, " + "please check your ~/.perfconfig file\n"); + } + break; + default: + usage_with_options(config_usage, config_options); + } + + return ret; +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 3688ad29085f..3f871b54e261 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -17,6 +17,7 @@ extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_bench(int argc, const char **argv, const char *prefix); extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); +extern int cmd_config(int argc, const char **argv, const char *prefix); extern int cmd_diff(int argc, const char **argv, const char *prefix); extern int cmd_evlist(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix); diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 00fcaf8a5b8d..acc3ea7d90b7 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common perf-buildid-list mainporcelain common perf-data mainporcelain common perf-diff mainporcelain common +perf-config mainporcelain common perf-evlist mainporcelain common perf-inject mainporcelain common perf-kmem mainporcelain common diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 3d4c7c09adea..4bee53c3f796 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -39,6 +39,7 @@ struct cmd_struct { static struct cmd_struct commands[] = { { "buildid-cache", cmd_buildid_cache, 0 }, { "buildid-list", cmd_buildid_list, 0 }, + { "config", cmd_config, 0 }, { "diff", cmd_diff, 0 }, { "evlist", cmd_evlist, 0 }, { "help", cmd_help, 0 }, From 7d6852432acb3b09fc3ec45dd65421d34eebe3b5 Mon Sep 17 00:00:00 2001 From: Taeung Song Date: Sun, 22 Nov 2015 19:11:56 +0900 Subject: [PATCH 049/271] perf config: Add initial man page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add perf-config document to describe the perf configuration and a 'list’ subcommand. Signed-off-by: Taeung Song Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/63AD9B57-7B8C-46F8-8F18-0FFEB9A6A1BC@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-config.txt | 103 +++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tools/perf/Documentation/perf-config.txt diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt new file mode 100644 index 000000000000..b9ca1e304158 --- /dev/null +++ b/tools/perf/Documentation/perf-config.txt @@ -0,0 +1,103 @@ +perf-config(1) +============== + +NAME +---- +perf-config - Get and set variables in a configuration file. + +SYNOPSIS +-------- +[verse] +'perf config' -l | --list + +DESCRIPTION +----------- +You can manage variables in a configuration file with this command. + +OPTIONS +------- + +-l:: +--list:: + Show current config variables, name and value, for all sections. + +CONFIGURATION FILE +------------------ + +The perf configuration file contains many variables to change various +aspects of each of its tools, including output, disk usage, etc. +The '$HOME/.perfconfig' file is used to store a per-user configuration. +The file '$(sysconfdir)/perfconfig' can be used to +store a system-wide default configuration. + +Syntax +~~~~~~ + +The file consist of sections. A section starts with its name +surrounded by square brackets and continues till the next section +begins. Each variable must be in a section, and have the form +'name = value', for example: + + [section] + name1 = value1 + name2 = value2 + +Section names are case sensitive and can contain any characters except +newline (double quote `"` and backslash have to be escaped as `\"` and `\\`, +respectively). Section headers can't span multiple lines. + +Example +~~~~~~~ + +Given a $HOME/.perfconfig like this: + +# +# This is the config file, and +# a '#' and ';' character indicates a comment +# + + [colors] + # Color variables + top = red, default + medium = green, default + normal = lightgray, default + selected = white, lightgray + code = blue, default + addr = magenta, default + root = white, blue + + [tui] + # Defaults if linked with libslang + report = on + annotate = on + top = on + + [buildid] + # Default, disable using /dev/null + dir = ~/.debug + + [annotate] + # Defaults + hide_src_code = false + use_offset = true + jump_arrows = true + show_nr_jumps = false + + [help] + # Format can be man, info, web or html + format = man + autocorrect = 0 + + [ui] + show-headers = true + + [call-graph] + # fp (framepointer), dwarf + record-mode = fp + print-type = graph + order = caller + sort-key = function + +SEE ALSO +-------- +linkperf:perf[1] From 646a6e846c4dc3812c614fd061603b6db5b8d380 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sat, 21 Nov 2015 11:23:55 +0100 Subject: [PATCH 050/271] 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(+) 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; From 32abc2ede536aae52978d6c0a8944eb1df14f460 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 16 Nov 2015 17:25:16 -0500 Subject: [PATCH 051/271] tools lib traceevent: Fix output of %llu for 64 bit values read on 32 bit machines When a long value is read on 32 bit machines for 64 bit output, the parsing needs to change "%lu" into "%llu", as the value is read natively. Unfortunately, if "%llu" is already there, the code will add another "l" to it and fail to parse it properly. Signed-off-by: Steven Rostedt Acked-by: Namhyung Kim Cc: stable@vger.kernel.org Link: http://lkml.kernel.org/r/20151116172516.4b79b109@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/traceevent/event-parse.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 2a912df6771b..68276f35e323 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -4968,13 +4968,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event sizeof(long) != 8) { char *p; - ls = 2; /* make %l into %ll */ - p = strchr(format, 'l'); - if (p) + if (ls == 1 && (p = strchr(format, 'l'))) memmove(p+1, p, strlen(p)+1); else if (strcmp(format, "%p") == 0) strcpy(format, "0x%llx"); + ls = 2; } switch (ls) { case -2: From 4d3b16269059eee12dc572848191c8e4e7bd24b3 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 25 Nov 2015 19:34:32 +0900 Subject: [PATCH 052/271] 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(-) 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; From 5725dd8fa888b4dcdff58241f9d3d3ac42a048e2 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 24 Nov 2015 13:36:06 +0000 Subject: [PATCH 053/271] tools build: Clean CFLAGS and LDFLAGS for fixdep Sometimes passing variables to tools/build is dangerous. For example, on my platform there is a gcc problem (gcc 4.8.1): It passes the stackprotector-all feature check: $ gcc -fstack-protector-all -c ./test.c $ echo $? 0 But requires LDFLAGS support if separate compiling and linking: $ gcc -fstack-protector-all -c ./test.c $ gcc ./test.o ./test.o: In function `main': test.c:(.text+0xb): undefined reference to `__stack_chk_guard' test.c:(.text+0x21): undefined reference to `__stack_chk_guard' collect2: error: ld returned 1 exit status $ gcc -fstack-protector-all ./test.o $ echo $? 0 $ gcc ./test.o -lssp $ echo $? 0 $ In this environment building perf throws an error: $ make BUILD: Doing 'make -j24' parallel build config/Makefile:344: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR config/Makefile:403: No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev config/Makefile:418: slang not found, disables TUI support. Please install slang-devel or libslang-dev config/Makefile:432: GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev config/Makefile:564: No bfd.h/libbfd found, please install binutils-dev[el]/zlib-static/libiberty-dev to gain symbol demangling config/Makefile:606: No numa.h found, disables 'perf bench numa mem' benchmark, please install numactl-devel/libnuma-devel/libnuma-dev CC fixdep.o LD fixdep-in.o LINK fixdep fixdep-in.o: In function `parse_dep_file': /kernel/tools/build/fixdep.c:47: undefined reference to `__stack_chk_guard' /kernel/tools/build/fixdep.c:117: undefined reference to `__stack_chk_guard' fixdep-in.o: In function `main': /kernel-hydrogen/tools/build/fixdep.c:156: undefined reference to `__stack_chk_guard' /kernel/tools/build/fixdep.c:168: undefined reference to `__stack_chk_guard' collect2: error: ld returned 1 exit status make[2]: *** [fixdep] Error 1 make[1]: *** [fixdep] Error 2 make: *** [all] Error 2 This is because the CFLAGS used in building perf pollutes the CFLAGS used for fixdep, passing -fstack-protector-all to buiold fixdep which is obviously not required. Since fixdep is a small host side tool, we should keep its CFLAGS/LDFLAGS simple and clean. This patch clears the CFLAGS and LDFLAGS passed when building fixdep, so such gcc problem won't block the perf build process. Signed-off-by: Wang Nan Acked-by: Jiri Olsa Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448372181-151723-2-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index 4e09ad617a60..6254760290c9 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include @@ -4,7 +4,7 @@ ifdef CROSS_COMPILE fixdep: else fixdep: - $(Q)$(MAKE) -C $(srctree)/tools/build fixdep + $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= fixdep endif .PHONY: fixdep From d8ad6a15cc3a364de6c8010378adc3fb06ce3ff1 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 24 Nov 2015 13:36:07 +0000 Subject: [PATCH 054/271] tools lib bpf: Don't do a feature check when cleaning Before this patch libbpf always do feature check even when cleaning. For example: $ cd kernel/tools/lib/bpf $ make Auto-detecting system features: ... libelf: [ on ] ... bpf: [ on ] CC libbpf.o CC bpf.o LD libbpf-in.o LINK libbpf.a LINK libbpf.so $ make clean CLEAN libbpf CLEAN core-gen $ make clean Auto-detecting system features: ... libelf: [ on ] ... bpf: [ on ] CLEAN libbpf CLEAN core-gen $ Although the first 'make clean' doesn't show feature check result, it still does the check. No output because check result is similar to FEATURE-DUMP.libbpf. This patch uses same method as perf to turn off feature checking when 'make clean'. Reported-and-Tested-by: Arnaldo Carvalho de Melo Signed-off-by: Wang Nan Cc: Alexei Starovoitov Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448372181-151723-3-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index a3caaf3eafbd..636e3ddb93a1 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -71,7 +71,17 @@ FEATURE_DISPLAY = libelf bpf INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES) +check_feat := 1 +NON_CHECK_FEAT_TARGETS := clean TAGS tags cscope help +ifdef MAKECMDGOALS +ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),) + check_feat := 0 +endif +endif + +ifeq ($(check_feat),1) include $(srctree)/tools/build/Makefile.feature +endif export prefix libdir src obj From b49a8fe52626814968b9a9d27d7ad1cadc5532ed Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Nov 2015 16:08:20 +0900 Subject: [PATCH 055/271] 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/builtin-report.c | 7 +++---- tools/perf/util/machine.c | 5 +++++ tools/perf/util/symbol.h | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 14428342b47b..8a9c6908f54e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -45,7 +45,6 @@ struct report { struct perf_tool tool; struct perf_session *session; bool use_tui, use_gtk, use_stdio; - bool hide_unresolved; bool dont_use_callchains; bool show_full_info; bool show_threads; @@ -146,7 +145,7 @@ static int process_sample_event(struct perf_tool *tool, struct hist_entry_iter iter = { .evsel = evsel, .sample = sample, - .hide_unresolved = rep->hide_unresolved, + .hide_unresolved = symbol_conf.hide_unresolved, .add_entry_cb = hist_iter__report_callback, }; int ret = 0; @@ -157,7 +156,7 @@ static int process_sample_event(struct perf_tool *tool, return -1; } - if (rep->hide_unresolved && al.sym == NULL) + if (symbol_conf.hide_unresolved && al.sym == NULL) goto out_put; if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) @@ -740,7 +739,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING_NOEMPTY('t', "field-separator", &symbol_conf.field_sep, "separator", "separator for columns, no spaces will be added between " "columns '.' is reserved."), - OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved, + OPT_BOOLEAN('U', "hide-unresolved", &symbol_conf.hide_unresolved, "Only display entries resolved to a symbol"), OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), 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, From 0356218a68551f051998f4fb5074a1eed7a346fe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 26 Nov 2015 16:08:18 +0900 Subject: [PATCH 056/271] 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(+) 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); } From 2aaecfc51bc65532152e141df3268fda06cae029 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 26 Nov 2015 14:55:23 +0100 Subject: [PATCH 057/271] perf script: Remove default_scripting_ops The default script handler (the one that displays samples on screen) is implemented scripting_ops instance with process_event callback. This way we can't pass any script config into display function, because we don't want perl or python handlers to be depended on perf script internals. Removing the default_scripting_ops and calling process event function directly. This way it's possible to pass perf_script struct and process configuration data in following commit. Signed-off-by: Jiri Olsa Acked-by: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448546125-29245-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 43 ++++++------------------------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 72b5deb4bd79..8e3f8048d2d0 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -643,57 +643,24 @@ static void process_event(union perf_event *event, struct perf_sample *sample, printf("\n"); } -static int default_start_script(const char *script __maybe_unused, - int argc __maybe_unused, - const char **argv __maybe_unused) -{ - return 0; -} - -static int default_flush_script(void) -{ - return 0; -} - -static int default_stop_script(void) -{ - return 0; -} - -static int default_generate_script(struct pevent *pevent __maybe_unused, - const char *outfile __maybe_unused) -{ - return 0; -} - -static struct scripting_ops default_scripting_ops = { - .start_script = default_start_script, - .flush_script = default_flush_script, - .stop_script = default_stop_script, - .process_event = process_event, - .generate_script = default_generate_script, -}; - static struct scripting_ops *scripting_ops; static void setup_scripting(void) { setup_perl_scripting(); setup_python_scripting(); - - scripting_ops = &default_scripting_ops; } static int flush_scripting(void) { - return scripting_ops->flush_script(); + return scripting_ops ? scripting_ops->flush_script() : 0; } static int cleanup_scripting(void) { pr_debug("\nperf script stopped\n"); - return scripting_ops->stop_script(); + return scripting_ops ? scripting_ops->stop_script() : 0; } static int process_sample_event(struct perf_tool *tool __maybe_unused, @@ -727,7 +694,11 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) goto out_put; - scripting_ops->process_event(event, sample, evsel, &al); + if (scripting_ops) + scripting_ops->process_event(event, sample, evsel, &al); + else + process_event(event, sample, evsel, &al); + out_put: addr_location__put(&al); return 0; From 67befc652845c8ffbefc8d173a6e6ced14d472f1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 26 Nov 2015 14:54:04 +0100 Subject: [PATCH 058/271] perf build: Fix traceevent plugins build race Ingo reported following build failure: $ make clean install ... CC plugin_kmem.o fixdep: error opening depfile: ./.plugin_hrtimer.o.d: No such file or directory /home/mingo/tip/tools/build/Makefile.build:77: recipe for target 'plugin_hrtimer.o' failed make[3]: *** [plugin_hrtimer.o] Error 2 Makefile:189: recipe for target 'plugin_hrtimer-in.o' failed make[2]: *** [plugin_hrtimer-in.o] Error 2 Makefile.perf:414: recipe for target 'libtraceevent_plugins' failed make[1]: *** [libtraceevent_plugins] Error 2 make[1]: *** Waiting for unfinished jobs.... Currently we have the install-traceevent-plugins target being dependent on $(LIBTRACEEVENT), which will actualy not build any plugin. So the install-traceevent-plugins target itself will try to build plugins, but.. Plugins built is also triggered by perf build itself via libtraceevent_plugins target. This might cause a race having one make thread removing temp files from another and result in above error. Fixing this by having proper plugins build dependency before installing plugins. Reported-and-Tested-by:: Ingo Molnar Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448546044-28973-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 0d19d5447d6c..929a32ba15f5 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -420,7 +420,7 @@ $(LIBTRACEEVENT)-clean: $(call QUIET_CLEAN, libtraceevent) $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) O=$(OUTPUT) clean >/dev/null -install-traceevent-plugins: $(LIBTRACEEVENT) +install-traceevent-plugins: libtraceevent_plugins $(Q)$(MAKE) -C $(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) O=$(OUTPUT) install_plugins $(LIBAPI): fixdep FORCE From c03d5184f0e92fa696e4b57f54ffc3b19a92f704 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Thu, 26 Nov 2015 03:59:57 +0000 Subject: [PATCH 059/271] 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(-) 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) From b2be5451f660e0ee230969cc24121d9e210a91de Mon Sep 17 00:00:00 2001 From: Yannick Brosseau Date: Thu, 26 Nov 2015 03:42:32 -0800 Subject: [PATCH 060/271] 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(-) 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) From 809e9423d7bc72e50d94d8267bab010a007a6137 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 26 Nov 2015 18:55:21 +0100 Subject: [PATCH 061/271] perf script: Pass perf_script into process_event Passing perf_script struct into process_event function, so we could process configuration data for event printing. It will be used in following patch to get event name string width. Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151126175521.GA18979@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 8e3f8048d2d0..3c3f8d0e3064 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -588,8 +588,17 @@ static void print_sample_flags(u32 flags) printf(" %-4s ", str); } -static void process_event(union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel, struct addr_location *al) +struct perf_script { + struct perf_tool tool; + struct perf_session *session; + bool show_task_events; + bool show_mmap_events; + bool show_switch_events; +}; + +static void process_event(struct perf_script *script __maybe_unused, union perf_event *event, + struct perf_sample *sample, struct perf_evsel *evsel, + struct addr_location *al) { struct thread *thread = al->thread; struct perf_event_attr *attr = &evsel->attr; @@ -663,12 +672,13 @@ static int cleanup_scripting(void) return scripting_ops ? scripting_ops->stop_script() : 0; } -static int process_sample_event(struct perf_tool *tool __maybe_unused, +static int process_sample_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine) { + struct perf_script *scr = container_of(tool, struct perf_script, tool); struct addr_location al; if (debug_mode) { @@ -697,21 +707,13 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, if (scripting_ops) scripting_ops->process_event(event, sample, evsel, &al); else - process_event(event, sample, evsel, &al); + process_event(scr, event, sample, evsel, &al); out_put: addr_location__put(&al); return 0; } -struct perf_script { - struct perf_tool tool; - struct perf_session *session; - bool show_task_events; - bool show_mmap_events; - bool show_switch_events; -}; - static int process_attr(struct perf_tool *tool, union perf_event *event, struct perf_evlist **pevlist) { From 5e50426d5d9049dfdb8b2b18e761717e7e80a6ad Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 26 Nov 2015 19:50:55 +0100 Subject: [PATCH 062/271] tools build: Use fixdep with OUTPUT path prefix Adding OUTPUT path prefix for fixdep target so we use it properly in out of tree builds. If the fixdep already existed in the tree, the out of tree build would see it already exist and did not build the out of tree version, as reported by Arnaldo: [acme@zoo linux]$ make O=/tmp/build/perf -C tools/perf make: Entering directory '/home/git/linux/tools/perf' BUILD: Doing 'make -j4' parallel build make[2]: Nothing to be done for 'fixdep'. make: Leaving directory '/home/git/linux/tools/perf' Reported-and-Tested-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/20151126185055.GC19410@krava.brq.redhat.com [ Fixed conflict with 5725dd8fa888 ("tools build: Clean CFLAGS and LDFLAGS for fixdep") ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile | 2 +- tools/build/Makefile.include | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/build/Makefile b/tools/build/Makefile index a93036272d43..0d5a0e3a8fa9 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -25,7 +25,7 @@ export Q srctree CC LD MAKEFLAGS := --no-print-directory build := -f $(srctree)/tools/build/Makefile.build dir=. obj -all: fixdep +all: $(OUTPUT)fixdep clean: $(call QUIET_CLEAN, fixdep) diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index 6254760290c9..be630bed66d2 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include @@ -4,7 +4,7 @@ ifdef CROSS_COMPILE fixdep: else fixdep: - $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= fixdep + $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep endif .PHONY: fixdep From aac4864727f4b3838ec1c03277bbc47a237b7516 Mon Sep 17 00:00:00 2001 From: Ekaterina Tumanova Date: Wed, 25 Nov 2015 17:32:45 +0100 Subject: [PATCH 063/271] 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(-) 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; From f55ae9540d16a355e61cb57b035aab9e1ae2da28 Mon Sep 17 00:00:00 2001 From: Ekaterina Tumanova Date: Wed, 25 Nov 2015 17:32:46 +0100 Subject: [PATCH 064/271] 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(-) 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) From 6acd8e9271cdeaec458fd4eec4a6765d16e0e61c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 25 Nov 2015 16:36:54 +0100 Subject: [PATCH 065/271] perf stat: Clear sample_(type|period) for counting Clear sample_(type|period) for counting, as it only confuses debug output with unwanted sampling details: Before: $ sudo perf stat -e 'raw_syscalls:sys_enter' -vv ls ------------------------------------------------------------ perf_event_attr: type 2 size 112 config 0x11 { sample_period, sample_freq } 1 sample_type TIME|CPU|PERIOD|RAW read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING disabled 1 inherit 1 enable_on_exec 1 exclude_guest 1 ... After: $ sudo perf stat -e 'raw_syscalls:sys_enter' -vv ls ------------------------------------------------------------ perf_event_attr: type 2 size 112 config 0x11 read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING disabled 1 inherit 1 enable_on_exec 1 exclude_guest 1 ... 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-1-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e77880b5094d..df2fbf046ee2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -161,6 +161,13 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->inherit = !no_inherit; + /* + * Some events get initialized with sample_(period/type) set, + * like tracepoints. Clear it up for counting. + */ + attr->sample_period = 0; + attr->sample_type = 0; + if (target__has_cpu(&target)) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); From dcdd184b60c3943fb678dcbaf899a26f845901ad Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 25 Nov 2015 16:36:55 +0100 Subject: [PATCH 066/271] 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(+) 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 From 43798bf37215fe242e592fd4605d804e2da0781b Mon Sep 17 00:00:00 2001 From: He Kuang Date: Tue, 24 Nov 2015 13:36:08 +0000 Subject: [PATCH 067/271] bpf tools: Add helper function for updating bpf maps elements Add bpf_map_update_elem() helper function which calls the sys_bpf syscall to update elements in bpf maps. Upcoming patches will use it to adjust data in map through the perf command line. Signed-off-by: He Kuang Cc: Alexei Starovoitov Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Wang Nan Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448372181-151723-4-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/bpf.c | 14 ++++++++++++++ tools/lib/bpf/bpf.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c index a6331050ab79..5bdc6eab6852 100644 --- a/tools/lib/bpf/bpf.c +++ b/tools/lib/bpf/bpf.c @@ -83,3 +83,17 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, log_buf[0] = 0; return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); } + +int bpf_map_update_elem(int fd, void *key, void *value, + u64 flags) +{ + union bpf_attr attr; + + bzero(&attr, sizeof(attr)); + attr.map_fd = fd; + attr.key = ptr_to_u64(key); + attr.value = ptr_to_u64(value); + attr.flags = flags; + + return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr)); +} diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h index 854b7361b784..a76465541292 100644 --- a/tools/lib/bpf/bpf.h +++ b/tools/lib/bpf/bpf.h @@ -20,4 +20,6 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, u32 kern_version, char *log_buf, size_t log_buf_sz); +int bpf_map_update_elem(int fd, void *key, void *value, + u64 flags); #endif From c4e079494f9258a3d2045779a3c9d6599c773dd9 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Fri, 27 Nov 2015 14:48:09 +0100 Subject: [PATCH 068/271] 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(-) 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) From 9bdcede563a831f139b5fc872f028ef844a7462e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 27 Nov 2015 09:21:21 +0100 Subject: [PATCH 069/271] perf test: 'unwind' test should create kernel maps The 'perf test unwind' is failing because it forgot to create the kernel maps, fix it. After the patch: # perf test unwind 40: Test dwarf unwind : Ok Reported-and-Tested-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Masami Hiramatsu Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20151127082121.GA24503@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/dwarf-unwind.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index b2357e8115a2..3cce13b19cbb 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -173,6 +173,11 @@ int test__dwarf_unwind(int subtest __maybe_unused) return -1; } + if (machine__create_kernel_maps(machine)) { + pr_err("Failed to create kernel maps\n"); + return -1; + } + callchain_param.record_mode = CALLCHAIN_DWARF; if (init_live_machine(machine)) { From bae9cc41105b9edd74d68a9636be2ba240e74b9e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Nov 2015 15:54:33 -0300 Subject: [PATCH 070/271] 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(+) 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) \ From e37df6c76cb19971f1228bfaff504d8a3ea6f748 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 27 Nov 2015 16:04:58 -0300 Subject: [PATCH 071/271] 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(-) 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; From 25b1606be1a910a63a23c3d1006581c9aad4e6e3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 28 Nov 2015 02:32:37 +0900 Subject: [PATCH 072/271] perf report: Show error message when processing sample fails Currently when perf fails to process samples for some reason, it doesn't show any message about the failure. This is very inconvenient for users especially on TUI as screen is reset after the failure. Reported-by: Ingo Molnar Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1448645559-31167-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8a9c6908f54e..af5db885ea9c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -513,20 +513,26 @@ static int __cmd_report(struct report *rep) if (rep->cpu_list) { ret = perf_session__cpu_bitmap(session, rep->cpu_list, rep->cpu_bitmap); - if (ret) + if (ret) { + ui__error("failed to set cpu bitmap\n"); return ret; + } } if (rep->show_threads) perf_read_values_init(&rep->show_threads_values); ret = report__setup_sample_type(rep); - if (ret) + if (ret) { + /* report__setup_sample_type() already showed error message */ return ret; + } ret = perf_session__process_events(session); - if (ret) + if (ret) { + ui__error("failed to process sample\n"); return ret; + } report__warn_kptr_restrict(rep); From e72655d97d24fff559b4ab59de791c3741a74c8c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 28 Nov 2015 02:32:38 +0900 Subject: [PATCH 073/271] 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(-) 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; From 039050482573e168690d365b8ea1d4f599ebbbd8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 28 Nov 2015 02:32:39 +0900 Subject: [PATCH 074/271] perf hists browser: Update nr entries regardless of min percent When perf report on TUI was called with -S symbol filter, it should update nr entries even if min_pcnt is 0. IIRC the reason was to update nr entries after applying minimum percent threshold. But if symbol filter was given on command line (with -S option), it should use hists->nr_non_filtered_entries instead of hists->nr_entries. So this patch fixes a bug of navigating hists browser that the cursor goes beyond the number of entries when -S (or similar) option is used. 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-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a211b7b6a81e..dcdcbafb078b 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2055,10 +2055,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, SLang_reset_tty(); SLang_init_tty(0, 0, 0); - if (min_pcnt) { + if (min_pcnt) browser->min_pcnt = min_pcnt; - hist_browser__update_nr_entries(browser); - } + hist_browser__update_nr_entries(browser); browser->pstack = pstack__new(3); if (browser->pstack == NULL) From 9d759a9b4ac2690077d8b21258e6e95c3e34bfa9 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 27 Nov 2015 08:47:35 +0000 Subject: [PATCH 075/271] tools lib bpf: Collect map definition in bpf_object This patch collects more information from maps sections in BPF object files into 'struct bpf_object', enables later patches access those information (such as the type and size of the map). In this patch, a new handler 'struct bpf_map' is extracted in parallel with bpf_object and bpf_program. Its iterator and accessor is also created. 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-2-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 187 ++++++++++++++++++++++++++++------------- tools/lib/bpf/libbpf.h | 21 +++++ 2 files changed, 148 insertions(+), 60 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index e3f4c3379f14..f50982579aa8 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -163,22 +163,24 @@ struct bpf_program { bpf_program_clear_priv_t clear_priv; }; +struct bpf_map { + int fd; + struct bpf_map_def def; + void *priv; + bpf_map_clear_priv_t clear_priv; +}; + static LIST_HEAD(bpf_objects_list); struct bpf_object { char license[64]; u32 kern_version; - void *maps_buf; - size_t maps_buf_sz; struct bpf_program *programs; size_t nr_programs; - int *map_fds; - /* - * This field is required because maps_buf will be freed and - * maps_buf_sz will be set to 0 after loaded. - */ - size_t nr_map_fds; + struct bpf_map *maps; + size_t nr_maps; + bool loaded; /* @@ -489,21 +491,38 @@ static int bpf_object__init_maps(struct bpf_object *obj, void *data, size_t size) { - if (size == 0) { + size_t nr_maps; + int i; + + nr_maps = size / sizeof(struct bpf_map_def); + if (!data || !nr_maps) { pr_debug("%s doesn't need map definition\n", obj->path); return 0; } - obj->maps_buf = malloc(size); - if (!obj->maps_buf) { - pr_warning("malloc maps failed: %s\n", obj->path); + pr_debug("maps in %s: %zd bytes\n", obj->path, size); + + obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); + if (!obj->maps) { + pr_warning("alloc maps for object failed\n"); return -ENOMEM; } + obj->nr_maps = nr_maps; - obj->maps_buf_sz = size; - memcpy(obj->maps_buf, data, size); - pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size); + for (i = 0; i < nr_maps; i++) { + struct bpf_map_def *def = &obj->maps[i].def; + + /* + * fill all fd with -1 so won't close incorrect + * fd (fd=0 is stdin) when failure (zclose won't close + * negative fd)). + */ + obj->maps[i].fd = -1; + + /* Save map definition into obj->maps */ + *def = ((struct bpf_map_def *)data)[i]; + } return 0; } @@ -688,37 +707,15 @@ static int bpf_object__create_maps(struct bpf_object *obj) { unsigned int i; - size_t nr_maps; - int *pfd; - nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def); - if (!obj->maps_buf || !nr_maps) { - pr_debug("don't need create maps for %s\n", - obj->path); - return 0; - } + for (i = 0; i < obj->nr_maps; i++) { + struct bpf_map_def *def = &obj->maps[i].def; + int *pfd = &obj->maps[i].fd; - obj->map_fds = malloc(sizeof(int) * nr_maps); - if (!obj->map_fds) { - pr_warning("realloc perf_bpf_map_fds failed\n"); - return -ENOMEM; - } - obj->nr_map_fds = nr_maps; - - /* fill all fd with -1 */ - memset(obj->map_fds, -1, sizeof(int) * nr_maps); - - pfd = obj->map_fds; - for (i = 0; i < nr_maps; i++) { - struct bpf_map_def def; - - def = *(struct bpf_map_def *)(obj->maps_buf + - i * sizeof(struct bpf_map_def)); - - *pfd = bpf_create_map(def.type, - def.key_size, - def.value_size, - def.max_entries); + *pfd = bpf_create_map(def->type, + def->key_size, + def->value_size, + def->max_entries); if (*pfd < 0) { size_t j; int err = *pfd; @@ -726,22 +723,17 @@ bpf_object__create_maps(struct bpf_object *obj) pr_warning("failed to create map: %s\n", strerror(errno)); for (j = 0; j < i; j++) - zclose(obj->map_fds[j]); - obj->nr_map_fds = 0; - zfree(&obj->map_fds); + zclose(obj->maps[j].fd); return err; } pr_debug("create map: fd=%d\n", *pfd); - pfd++; } - zfree(&obj->maps_buf); - obj->maps_buf_sz = 0; return 0; } static int -bpf_program__relocate(struct bpf_program *prog, int *map_fds) +bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj) { int i; @@ -761,7 +753,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds) return -LIBBPF_ERRNO__RELOC; } insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; - insns[insn_idx].imm = map_fds[map_idx]; + insns[insn_idx].imm = obj->maps[map_idx].fd; } zfree(&prog->reloc_desc); @@ -780,7 +772,7 @@ bpf_object__relocate(struct bpf_object *obj) for (i = 0; i < obj->nr_programs; i++) { prog = &obj->programs[i]; - err = bpf_program__relocate(prog, obj->map_fds); + err = bpf_program__relocate(prog, obj); if (err) { pr_warning("failed to relocate '%s'\n", prog->section_name); @@ -804,8 +796,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) Elf_Data *data = obj->efile.reloc[i].data; int idx = shdr->sh_info; struct bpf_program *prog; - size_t nr_maps = obj->maps_buf_sz / - sizeof(struct bpf_map_def); + size_t nr_maps = obj->nr_maps; if (shdr->sh_type != SHT_REL) { pr_warning("internal error at %d\n", __LINE__); @@ -1050,10 +1041,8 @@ int bpf_object__unload(struct bpf_object *obj) if (!obj) return -EINVAL; - for (i = 0; i < obj->nr_map_fds; i++) - zclose(obj->map_fds[i]); - zfree(&obj->map_fds); - obj->nr_map_fds = 0; + for (i = 0; i < obj->nr_maps; i++) + zclose(obj->maps[i].fd); for (i = 0; i < obj->nr_programs; i++) bpf_program__unload(&obj->programs[i]); @@ -1096,7 +1085,15 @@ void bpf_object__close(struct bpf_object *obj) bpf_object__elf_finish(obj); bpf_object__unload(obj); - zfree(&obj->maps_buf); + for (i = 0; i < obj->nr_maps; i++) { + if (obj->maps[i].clear_priv) + obj->maps[i].clear_priv(&obj->maps[i], + obj->maps[i].priv); + obj->maps[i].priv = NULL; + obj->maps[i].clear_priv = NULL; + } + zfree(&obj->maps); + obj->nr_maps = 0; if (obj->programs && obj->nr_programs) { for (i = 0; i < obj->nr_programs; i++) @@ -1251,3 +1248,73 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n) return fd; } + +int bpf_map__get_fd(struct bpf_map *map) +{ + if (!map) + return -EINVAL; + + return map->fd; +} + +int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef) +{ + if (!map || !pdef) + return -EINVAL; + + *pdef = map->def; + return 0; +} + +int bpf_map__set_private(struct bpf_map *map, void *priv, + bpf_map_clear_priv_t clear_priv) +{ + if (!map) + return -EINVAL; + + if (map->priv) { + if (map->clear_priv) + map->clear_priv(map, map->priv); + } + + map->priv = priv; + map->clear_priv = clear_priv; + return 0; +} + +int bpf_map__get_private(struct bpf_map *map, void **ppriv) +{ + if (!map) + return -EINVAL; + + if (ppriv) + *ppriv = map->priv; + return 0; +} + +struct bpf_map * +bpf_map__next(struct bpf_map *prev, struct bpf_object *obj) +{ + size_t idx; + struct bpf_map *s, *e; + + if (!obj || !obj->maps) + return NULL; + + s = obj->maps; + e = obj->maps + obj->nr_maps; + + if (prev == NULL) + return s; + + if ((prev < s) || (prev >= e)) { + pr_warning("error in %s: map handler doesn't belong to object\n", + __func__); + return NULL; + } + + idx = (prev - obj->maps) + 1; + if (idx >= obj->nr_maps) + return NULL; + return &obj->maps[idx]; +} diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index 949df4b346cf..ef631255dfaa 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -165,4 +165,25 @@ struct bpf_map_def { unsigned int max_entries; }; +/* + * There is another 'struct bpf_map' in include/linux/map.h. However, + * it is not a uapi header so no need to consider name clash. + */ +struct bpf_map; + +struct bpf_map * +bpf_map__next(struct bpf_map *map, struct bpf_object *obj); +#define bpf_map__for_each(pos, obj) \ + for ((pos) = bpf_map__next(NULL, (obj)); \ + (pos) != NULL; \ + (pos) = bpf_map__next((pos), (obj))) + +int bpf_map__get_fd(struct bpf_map *map); +int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef); + +typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); +int bpf_map__set_private(struct bpf_map *map, void *priv, + bpf_map_clear_priv_t clear_priv); +int bpf_map__get_private(struct bpf_map *map, void **ppriv); + #endif From 561bbccac72d08babafaa33fd7fa9100ec4c9fb6 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 27 Nov 2015 08:47:36 +0000 Subject: [PATCH 076/271] tools lib bpf: Extract and collect map names from BPF object file This patch collects name of maps in BPF object files and saves them into 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is introduced to retrive fd and definitions of a map through its name. Signed-off-by: He Kuang Cc: Alexei Starovoitov Cc: He Kuang Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1448614067-197576-3-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 65 ++++++++++++++++++++++++++++++++++++++++-- tools/lib/bpf/libbpf.h | 3 ++ 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index f50982579aa8..a298614ad091 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -165,6 +165,7 @@ struct bpf_program { struct bpf_map { int fd; + char *name; struct bpf_map_def def; void *priv; bpf_map_clear_priv_t clear_priv; @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data, return 0; } +static void +bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx) +{ + int i; + Elf_Data *symbols = obj->efile.symbols; + + if (!symbols || maps_shndx < 0) + return; + + for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { + GElf_Sym sym; + size_t map_idx; + const char *map_name; + + if (!gelf_getsym(symbols, i, &sym)) + continue; + if (sym.st_shndx != maps_shndx) + continue; + + map_name = elf_strptr(obj->efile.elf, + obj->efile.ehdr.e_shstrndx, + sym.st_name); + map_idx = sym.st_value / sizeof(struct bpf_map_def); + if (map_idx >= obj->nr_maps) { + pr_warning("index of map \"%s\" is buggy: %zu > %zu\n", + map_name, map_idx, obj->nr_maps); + continue; + } + obj->maps[map_idx].name = strdup(map_name); + pr_debug("map %zu is \"%s\"\n", map_idx, + obj->maps[map_idx].name); + } +} + static int bpf_object__elf_collect(struct bpf_object *obj) { Elf *elf = obj->efile.elf; GElf_Ehdr *ep = &obj->efile.ehdr; Elf_Scn *scn = NULL; - int idx = 0, err = 0; + int idx = 0, err = 0, maps_shndx = -1; /* Elf is corrupted/truncated, avoid calling elf_strptr. */ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { @@ -581,10 +616,11 @@ static int bpf_object__elf_collect(struct bpf_object *obj) err = bpf_object__init_kversion(obj, data->d_buf, data->d_size); - else if (strcmp(name, "maps") == 0) + else if (strcmp(name, "maps") == 0) { err = bpf_object__init_maps(obj, data->d_buf, data->d_size); - else if (sh.sh_type == SHT_SYMTAB) { + maps_shndx = idx; + } else if (sh.sh_type == SHT_SYMTAB) { if (obj->efile.symbols) { pr_warning("bpf: multiple SYMTAB in %s\n", obj->path); @@ -625,6 +661,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj) if (err) goto out; } + + if (maps_shndx >= 0) + bpf_object__init_maps_name(obj, maps_shndx); out: return err; } @@ -1086,6 +1125,7 @@ void bpf_object__close(struct bpf_object *obj) bpf_object__unload(obj); for (i = 0; i < obj->nr_maps; i++) { + zfree(&obj->maps[i].name); if (obj->maps[i].clear_priv) obj->maps[i].clear_priv(&obj->maps[i], obj->maps[i].priv); @@ -1266,6 +1306,13 @@ int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef) return 0; } +const char *bpf_map__get_name(struct bpf_map *map) +{ + if (!map) + return NULL; + return map->name; +} + int bpf_map__set_private(struct bpf_map *map, void *priv, bpf_map_clear_priv_t clear_priv) { @@ -1318,3 +1365,15 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj) return NULL; return &obj->maps[idx]; } + +struct bpf_map * +bpf_object__get_map_by_name(struct bpf_object *obj, const char *name) +{ + struct bpf_map *pos; + + bpf_map__for_each(pos, obj) { + if (strcmp(pos->name, name) == 0) + return pos; + } + return NULL; +} diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index ef631255dfaa..a51594c7b518 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -170,6 +170,8 @@ struct bpf_map_def { * it is not a uapi header so no need to consider name clash. */ struct bpf_map; +struct bpf_map * +bpf_object__get_map_by_name(struct bpf_object *obj, const char *name); struct bpf_map * bpf_map__next(struct bpf_map *map, struct bpf_object *obj); @@ -180,6 +182,7 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj); int bpf_map__get_fd(struct bpf_map *map); int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef); +const char *bpf_map__get_name(struct bpf_map *map); typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *); int bpf_map__set_private(struct bpf_map *map, void *priv, From 0bb93490170477224f8bd4cc9ce8920517461643 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 27 Nov 2015 08:47:37 +0000 Subject: [PATCH 077/271] 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(-) 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 */ From 0017960f38a2470e70d9f1991228e2b55b2abe0c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Mon, 30 Nov 2015 16:26:35 +0100 Subject: [PATCH 078/271] perf/core: Collapse common IPI pattern Various functions implement the same pattern to send IPIs to an event's CPU. Collapse the easy ones in a common helper function to reduce duplication. Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Signed-off-by: Ingo Molnar --- kernel/events/core.c | 180 ++++++++++++++++++------------------------- 1 file changed, 76 insertions(+), 104 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 39cf4a40aa4c..c3d61b92d805 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -126,6 +126,37 @@ static int cpu_function_call(int cpu, remote_function_f func, void *info) return data.ret; } +static void event_function_call(struct perf_event *event, + int (*active)(void *), + void (*inactive)(void *), + void *data) +{ + struct perf_event_context *ctx = event->ctx; + struct task_struct *task = ctx->task; + + if (!task) { + cpu_function_call(event->cpu, active, data); + return; + } + +again: + if (!task_function_call(task, active, data)) + return; + + raw_spin_lock_irq(&ctx->lock); + if (ctx->is_active) { + /* + * Reload the task pointer, it might have been changed by + * a concurrent perf_event_context_sched_out(). + */ + task = ctx->task; + raw_spin_unlock_irq(&ctx->lock); + goto again; + } + inactive(data); + raw_spin_unlock_irq(&ctx->lock); +} + #define EVENT_OWNER_KERNEL ((void *) -1) static bool is_kernel_event(struct perf_event *event) @@ -1629,6 +1660,17 @@ struct remove_event { bool detach_group; }; +static void ___perf_remove_from_context(void *info) +{ + struct remove_event *re = info; + struct perf_event *event = re->event; + struct perf_event_context *ctx = event->ctx; + + if (re->detach_group) + perf_group_detach(event); + list_del_event(event, ctx); +} + /* * Cross CPU call to remove a performance event * @@ -1656,7 +1698,6 @@ static int __perf_remove_from_context(void *info) return 0; } - /* * Remove the event from a task's (or a CPU's) list of events. * @@ -1673,7 +1714,6 @@ static int __perf_remove_from_context(void *info) static void perf_remove_from_context(struct perf_event *event, bool detach_group) { struct perf_event_context *ctx = event->ctx; - struct task_struct *task = ctx->task; struct remove_event re = { .event = event, .detach_group = detach_group, @@ -1681,44 +1721,8 @@ static void perf_remove_from_context(struct perf_event *event, bool detach_group lockdep_assert_held(&ctx->mutex); - if (!task) { - /* - * Per cpu events are removed via an smp call. The removal can - * fail if the CPU is currently offline, but in that case we - * already called __perf_remove_from_context from - * perf_event_exit_cpu. - */ - cpu_function_call(event->cpu, __perf_remove_from_context, &re); - return; - } - -retry: - if (!task_function_call(task, __perf_remove_from_context, &re)) - return; - - raw_spin_lock_irq(&ctx->lock); - /* - * If we failed to find a running task, but find the context active now - * that we've acquired the ctx->lock, retry. - */ - if (ctx->is_active) { - raw_spin_unlock_irq(&ctx->lock); - /* - * Reload the task pointer, it might have been changed by - * a concurrent perf_event_context_sched_out(). - */ - task = ctx->task; - goto retry; - } - - /* - * Since the task isn't running, its safe to remove the event, us - * holding the ctx->lock ensures the task won't get scheduled in. - */ - if (detach_group) - perf_group_detach(event); - list_del_event(event, ctx); - raw_spin_unlock_irq(&ctx->lock); + event_function_call(event, __perf_remove_from_context, + ___perf_remove_from_context, &re); } /* @@ -2067,6 +2071,18 @@ static void perf_event_sched_in(struct perf_cpu_context *cpuctx, ctx_sched_in(ctx, cpuctx, EVENT_FLEXIBLE, task); } +static void ___perf_install_in_context(void *info) +{ + struct perf_event *event = info; + struct perf_event_context *ctx = event->ctx; + + /* + * Since the task isn't running, its safe to add the event, us holding + * the ctx->lock ensures the task won't get scheduled in. + */ + add_event_to_ctx(event, ctx); +} + /* * Cross CPU call to install and enable a performance event * @@ -2143,48 +2159,14 @@ perf_install_in_context(struct perf_event_context *ctx, struct perf_event *event, int cpu) { - struct task_struct *task = ctx->task; - lockdep_assert_held(&ctx->mutex); event->ctx = ctx; if (event->cpu != -1) event->cpu = cpu; - if (!task) { - /* - * Per cpu events are installed via an smp call and - * the install is always successful. - */ - cpu_function_call(cpu, __perf_install_in_context, event); - return; - } - -retry: - if (!task_function_call(task, __perf_install_in_context, event)) - return; - - raw_spin_lock_irq(&ctx->lock); - /* - * If we failed to find a running task, but find the context active now - * that we've acquired the ctx->lock, retry. - */ - if (ctx->is_active) { - raw_spin_unlock_irq(&ctx->lock); - /* - * Reload the task pointer, it might have been changed by - * a concurrent perf_event_context_sched_out(). - */ - task = ctx->task; - goto retry; - } - - /* - * Since the task isn't running, its safe to add the event, us holding - * the ctx->lock ensures the task won't get scheduled in. - */ - add_event_to_ctx(event, ctx); - raw_spin_unlock_irq(&ctx->lock); + event_function_call(event, __perf_install_in_context, + ___perf_install_in_context, event); } /* @@ -4154,6 +4136,22 @@ struct period_event { u64 value; }; +static void ___perf_event_period(void *info) +{ + struct period_event *pe = info; + struct perf_event *event = pe->event; + u64 value = pe->value; + + if (event->attr.freq) { + event->attr.sample_freq = value; + } else { + event->attr.sample_period = value; + event->hw.sample_period = value; + } + + local64_set(&event->hw.period_left, 0); +} + static int __perf_event_period(void *info) { struct period_event *pe = info; @@ -4190,8 +4188,6 @@ static int __perf_event_period(void *info) static int perf_event_period(struct perf_event *event, u64 __user *arg) { struct period_event pe = { .event = event, }; - struct perf_event_context *ctx = event->ctx; - struct task_struct *task; u64 value; if (!is_sampling_event(event)) @@ -4206,34 +4202,10 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) if (event->attr.freq && value > sysctl_perf_event_sample_rate) return -EINVAL; - task = ctx->task; pe.value = value; - if (!task) { - cpu_function_call(event->cpu, __perf_event_period, &pe); - return 0; - } - -retry: - if (!task_function_call(task, __perf_event_period, &pe)) - return 0; - - raw_spin_lock_irq(&ctx->lock); - if (ctx->is_active) { - raw_spin_unlock_irq(&ctx->lock); - task = ctx->task; - goto retry; - } - - if (event->attr.freq) { - event->attr.sample_freq = value; - } else { - event->attr.sample_period = value; - event->hw.sample_period = value; - } - - local64_set(&event->hw.period_left, 0); - raw_spin_unlock_irq(&ctx->lock); + event_function_call(event, __perf_event_period, + ___perf_event_period, &pe); return 0; } From da008ee72cabdee0ee98d3a3580ca5cfb8d2d1f1 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 30 Nov 2015 09:48:42 -0800 Subject: [PATCH 079/271] perf/x86/intel: Fix __initconst declaration in the RAPL perf driver Fix a definition in the perf rapl driver. __initconst must be applied to a const object, but to declare a const pointer you need to use * const ..., not const ... * This fixes a section attribute conflict with LTO builds. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1448905722-2767-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_rapl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c index ed446bdcbf31..fb5843dc7b83 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c +++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c @@ -63,7 +63,7 @@ #define INTEL_RAPL_PP1 0x4 /* pseudo-encoding */ #define NR_RAPL_DOMAINS 0x4 -static const char *rapl_domain_names[NR_RAPL_DOMAINS] __initconst = { +static const char *const rapl_domain_names[NR_RAPL_DOMAINS] __initconst = { "pp0-core", "package", "dram", From 153a4334c439cfb62e1d31cee0c790ba4157813d Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Dec 2015 17:00:57 -0800 Subject: [PATCH 080/271] x86/headers: Don't include asm/processor.h in asm/atomic.h asm/atomic.h doesn't really need asm/processor.h anymore. Everything it uses has moved to other header files. So remove that include. processor.h is a nasty header that includes lots of other headers and makes it prone to include loops. Removing the include here makes asm/atomic.h a "leaf" header that can be safely included in most other headers. The only fallout is in the lib/atomic tester which relied on this implicit include. Give it an explicit include. (the include is in ifdef because the user is also in ifdef) Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: rostedt@goodmis.org Link: http://lkml.kernel.org/r/1449018060-1742-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/include/asm/atomic.h | 1 - arch/x86/include/asm/atomic64_32.h | 1 - lib/atomic64_test.c | 4 ++++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h index ae5fb83e6d91..3e8674288198 100644 --- a/arch/x86/include/asm/atomic.h +++ b/arch/x86/include/asm/atomic.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include diff --git a/arch/x86/include/asm/atomic64_32.h b/arch/x86/include/asm/atomic64_32.h index a11c30b77fb5..a984111135b1 100644 --- a/arch/x86/include/asm/atomic64_32.h +++ b/arch/x86/include/asm/atomic64_32.h @@ -3,7 +3,6 @@ #include #include -#include //#include /* An 64bit atomic type */ diff --git a/lib/atomic64_test.c b/lib/atomic64_test.c index 83c33a5bcffb..d51e25aa5f1d 100644 --- a/lib/atomic64_test.c +++ b/lib/atomic64_test.c @@ -16,6 +16,10 @@ #include #include +#ifdef CONFIG_X86 +#include /* for boot_cpu_has below */ +#endif + #define TEST(bit, op, c_op, val) \ do { \ atomic##bit##_set(&v, v0); \ From bd2a634d9e852b9b6100f9ae9c3c790b0ff91ce0 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Dec 2015 17:00:58 -0800 Subject: [PATCH 081/271] tracepoints: Move struct tracepoint to new tracepoint-defs.h header Steven recommended open coding access to tracepoint->key to add trace points to headers. Unfortunately this is difficult for some headers (such as x86 asm/msr.h) because including tracepoint.h includes so many other headers that it causes include loops. The main problem is the include of linux/rcupdate.h, which pulls in a lot of other headers. The rcu header is only needed when actually defining trace points. Move the struct tracepoint into a separate tracepoint-defs.h header that can be included without pulling in all of RCU. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Acked-by: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449018060-1742-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- include/linux/tracepoint-defs.h | 27 +++++++++++++++++++++++++++ include/linux/tracepoint.h | 16 +--------------- 2 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 include/linux/tracepoint-defs.h diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h new file mode 100644 index 000000000000..e1ee97c713bf --- /dev/null +++ b/include/linux/tracepoint-defs.h @@ -0,0 +1,27 @@ +#ifndef TRACEPOINT_DEFS_H +#define TRACEPOINT_DEFS_H 1 + +/* + * File can be included directly by headers who only want to access + * tracepoint->key to guard out of line trace calls. Otherwise + * linux/tracepoint.h should be used. + */ + +#include +#include + +struct tracepoint_func { + void *func; + void *data; + int prio; +}; + +struct tracepoint { + const char *name; /* Tracepoint name */ + struct static_key key; + void (*regfunc)(void); + void (*unregfunc)(void); + struct tracepoint_func __rcu *funcs; +}; + +#endif diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 696a339c592c..f7c732bd5cbd 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -17,26 +17,12 @@ #include #include #include -#include +#include struct module; struct tracepoint; struct notifier_block; -struct tracepoint_func { - void *func; - void *data; - int prio; -}; - -struct tracepoint { - const char *name; /* Tracepoint name */ - struct static_key key; - void (*regfunc)(void); - void (*unregfunc)(void); - struct tracepoint_func __rcu *funcs; -}; - struct trace_enum_map { const char *system; const char *enum_string; From 7f47d8cc039f8746e0038fe05f1ddcb15a2e27f0 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Dec 2015 17:00:59 -0800 Subject: [PATCH 082/271] x86, tracing, perf: Add trace point for MSR accesses For debugging low level code interacting with the CPU it is often useful to trace the MSR read/writes. This gives a concise summary of PMU and other operations. perf has an ad-hoc way to do this using trace_printk, but it's somewhat limited (and also now spews ugly boot messages when enabled) Instead define real trace points for all MSR accesses. This adds three new trace points: read_msr and write_msr and rdpmc. They also report if the access faulted (if *_safe is used) This allows filtering and triggering on specific MSR values, which allows various more advanced debugging techniques. All the values are well defined in the CPU documentation. The trace can be post processed with Documentation/trace/postprocess/decode_msr.py to add symbolic MSR names to the trace. I only added it to native MSR accesses in C, not paravirtualized or in entry*.S (which is not too interesting) Originally the patch kit moved the MSRs out of line. This uses an alternative approach recommended by Steven Rostedt of only moving the trace calls out of line, but open coding the access to the jump label. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Acked-by: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449018060-1742-3-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- Documentation/trace/events-msr.txt | 37 ++++++++++++ Documentation/trace/postprocess/decode_msr.py | 37 ++++++++++++ arch/x86/include/asm/msr-trace.h | 57 +++++++++++++++++++ arch/x86/include/asm/msr.h | 31 ++++++++++ arch/x86/lib/msr.c | 26 +++++++++ 5 files changed, 188 insertions(+) create mode 100644 Documentation/trace/events-msr.txt create mode 100644 Documentation/trace/postprocess/decode_msr.py create mode 100644 arch/x86/include/asm/msr-trace.h diff --git a/Documentation/trace/events-msr.txt b/Documentation/trace/events-msr.txt new file mode 100644 index 000000000000..78c383bf06aa --- /dev/null +++ b/Documentation/trace/events-msr.txt @@ -0,0 +1,37 @@ + +The x86 kernel supports tracing most MSR (Model Specific Register) accesses. +To see the definition of the MSRs on Intel systems please see the SDM +at http://www.intel.com/sdm (Volume 3) + +Available trace points: + +/sys/kernel/debug/tracing/events/msr/ + +Trace MSR reads + +read_msr + +msr: MSR number +val: Value written +failed: 1 if the access failed, otherwise 0 + + +Trace MSR writes + +write_msr + +msr: MSR number +val: Value written +failed: 1 if the access failed, otherwise 0 + + +Trace RDPMC in kernel + +rdpmc + +The trace data can be post processed with the postprocess/decode_msr.py script + +cat /sys/kernel/debug/tracing/trace | decode_msr.py /usr/src/linux/include/asm/msr-index.h + +to add symbolic MSR names. + diff --git a/Documentation/trace/postprocess/decode_msr.py b/Documentation/trace/postprocess/decode_msr.py new file mode 100644 index 000000000000..0ab40e0db580 --- /dev/null +++ b/Documentation/trace/postprocess/decode_msr.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +# add symbolic names to read_msr / write_msr in trace +# decode_msr msr-index.h < trace +import sys +import re + +msrs = dict() + +with open(sys.argv[1] if len(sys.argv) > 1 else "msr-index.h", "r") as f: + for j in f: + m = re.match(r'#define (MSR_\w+)\s+(0x[0-9a-fA-F]+)', j) + if m: + msrs[int(m.group(2), 16)] = m.group(1) + +extra_ranges = ( + ( "MSR_LASTBRANCH_%d_FROM_IP", 0x680, 0x69F ), + ( "MSR_LASTBRANCH_%d_TO_IP", 0x6C0, 0x6DF ), + ( "LBR_INFO_%d", 0xdc0, 0xddf ), +) + +for j in sys.stdin: + m = re.search(r'(read|write)_msr:\s+([0-9a-f]+)', j) + if m: + r = None + num = int(m.group(2), 16) + if num in msrs: + r = msrs[num] + else: + for er in extra_ranges: + if er[1] <= num <= er[2]: + r = er[0] % (num - er[1],) + break + if r: + j = j.replace(" " + m.group(2), " " + r + "(" + m.group(2) + ")") + print j, + + diff --git a/arch/x86/include/asm/msr-trace.h b/arch/x86/include/asm/msr-trace.h new file mode 100644 index 000000000000..7567225747d8 --- /dev/null +++ b/arch/x86/include/asm/msr-trace.h @@ -0,0 +1,57 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM msr + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE msr-trace + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH asm/ + +#if !defined(_TRACE_MSR_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_MSR_H + +#include + +/* + * Tracing for x86 model specific registers. Directly maps to the + * RDMSR/WRMSR instructions. + */ + +DECLARE_EVENT_CLASS(msr_trace_class, + TP_PROTO(unsigned msr, u64 val, int failed), + TP_ARGS(msr, val, failed), + TP_STRUCT__entry( + __field( unsigned, msr ) + __field( u64, val ) + __field( int, failed ) + ), + TP_fast_assign( + __entry->msr = msr; + __entry->val = val; + __entry->failed = failed; + ), + TP_printk("%x, value %llx%s", + __entry->msr, + __entry->val, + __entry->failed ? " #GP" : "") +); + +DEFINE_EVENT(msr_trace_class, read_msr, + TP_PROTO(unsigned msr, u64 val, int failed), + TP_ARGS(msr, val, failed) +); + +DEFINE_EVENT(msr_trace_class, write_msr, + TP_PROTO(unsigned msr, u64 val, int failed), + TP_ARGS(msr, val, failed) +); + +DEFINE_EVENT(msr_trace_class, rdpmc, + TP_PROTO(unsigned msr, u64 val, int failed), + TP_ARGS(msr, val, failed) +); + +#endif /* _TRACE_MSR_H */ + +/* This part must be outside protection */ +#include diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h index 77d8b284e4a7..fedd6e6d1e43 100644 --- a/arch/x86/include/asm/msr.h +++ b/arch/x86/include/asm/msr.h @@ -57,11 +57,34 @@ static inline unsigned long long native_read_tscp(unsigned int *aux) #define EAX_EDX_RET(val, low, high) "=A" (val) #endif +#ifdef CONFIG_TRACEPOINTS +/* + * Be very careful with includes. This header is prone to include loops. + */ +#include +#include + +extern struct tracepoint __tracepoint_read_msr; +extern struct tracepoint __tracepoint_write_msr; +extern struct tracepoint __tracepoint_rdpmc; +#define msr_tracepoint_active(t) static_key_false(&(t).key) +extern void do_trace_write_msr(unsigned msr, u64 val, int failed); +extern void do_trace_read_msr(unsigned msr, u64 val, int failed); +extern void do_trace_rdpmc(unsigned msr, u64 val, int failed); +#else +#define msr_tracepoint_active(t) false +static inline void do_trace_write_msr(unsigned msr, u64 val, int failed) {} +static inline void do_trace_read_msr(unsigned msr, u64 val, int failed) {} +static inline void do_trace_rdpmc(unsigned msr, u64 val, int failed) {} +#endif + static inline unsigned long long native_read_msr(unsigned int msr) { DECLARE_ARGS(val, low, high); asm volatile("rdmsr" : EAX_EDX_RET(val, low, high) : "c" (msr)); + if (msr_tracepoint_active(__tracepoint_read_msr)) + do_trace_read_msr(msr, EAX_EDX_VAL(val, low, high), 0); return EAX_EDX_VAL(val, low, high); } @@ -78,6 +101,8 @@ static inline unsigned long long native_read_msr_safe(unsigned int msr, _ASM_EXTABLE(2b, 3b) : [err] "=r" (*err), EAX_EDX_RET(val, low, high) : "c" (msr), [fault] "i" (-EIO)); + if (msr_tracepoint_active(__tracepoint_read_msr)) + do_trace_read_msr(msr, EAX_EDX_VAL(val, low, high), *err); return EAX_EDX_VAL(val, low, high); } @@ -85,6 +110,8 @@ static inline void native_write_msr(unsigned int msr, unsigned low, unsigned high) { asm volatile("wrmsr" : : "c" (msr), "a"(low), "d" (high) : "memory"); + if (msr_tracepoint_active(__tracepoint_read_msr)) + do_trace_write_msr(msr, ((u64)high << 32 | low), 0); } /* Can be uninlined because referenced by paravirt */ @@ -102,6 +129,8 @@ notrace static inline int native_write_msr_safe(unsigned int msr, : "c" (msr), "0" (low), "d" (high), [fault] "i" (-EIO) : "memory"); + if (msr_tracepoint_active(__tracepoint_read_msr)) + do_trace_write_msr(msr, ((u64)high << 32 | low), err); return err; } @@ -160,6 +189,8 @@ static inline unsigned long long native_read_pmc(int counter) DECLARE_ARGS(val, low, high); asm volatile("rdpmc" : EAX_EDX_RET(val, low, high) : "c" (counter)); + if (msr_tracepoint_active(__tracepoint_rdpmc)) + do_trace_rdpmc(counter, EAX_EDX_VAL(val, low, high), 0); return EAX_EDX_VAL(val, low, high); } diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c index 43623739c7cf..004c861b1648 100644 --- a/arch/x86/lib/msr.c +++ b/arch/x86/lib/msr.c @@ -1,6 +1,8 @@ #include #include #include +#define CREATE_TRACE_POINTS +#include struct msr *msrs_alloc(void) { @@ -108,3 +110,27 @@ int msr_clear_bit(u32 msr, u8 bit) { return __flip_bit(msr, bit, false); } + +#ifdef CONFIG_TRACEPOINTS +void do_trace_write_msr(unsigned msr, u64 val, int failed) +{ + trace_write_msr(msr, val, failed); +} +EXPORT_SYMBOL(do_trace_write_msr); +EXPORT_TRACEPOINT_SYMBOL(write_msr); + +void do_trace_read_msr(unsigned msr, u64 val, int failed) +{ + trace_read_msr(msr, val, failed); +} +EXPORT_SYMBOL(do_trace_read_msr); +EXPORT_TRACEPOINT_SYMBOL(read_msr); + +void do_trace_rdpmc(unsigned counter, u64 val, int failed) +{ + trace_rdpmc(counter, val, failed); +} +EXPORT_SYMBOL(do_trace_rdpmc); +EXPORT_TRACEPOINT_SYMBOL(rdpmc); + +#endif From f1ad44884a4c421ceaa9a4a8242aeeee6f686670 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 1 Dec 2015 17:01:00 -0800 Subject: [PATCH 083/271] perf/x86: Remove old MSR perf tracing code Now that we have generic MSR trace points we can remove the old hackish perf MSR read tracing code. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: rostedt@goodmis.org Link: http://lkml.kernel.org/r/1449018060-1742-4-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index a7ab350bce98..799e6bd58cc1 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -14,17 +14,7 @@ #include -#if 0 -#undef wrmsrl -#define wrmsrl(msr, val) \ -do { \ - unsigned int _msr = (msr); \ - u64 _val = (val); \ - trace_printk("wrmsrl(%x, %Lx)\n", (unsigned int)(_msr), \ - (unsigned long long)(_val)); \ - native_write_msr((_msr), (u32)(_val), (u32)(_val >> 32)); \ -} while (0) -#endif +/* To enable MSR tracing please use the generic trace points. */ /* * | NHM/WSM | SNB | From 5dcf16df3ce48b2e4f798b1a11b5de2fc3cfd73a Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 7 Dec 2015 02:36:25 +0000 Subject: [PATCH 084/271] 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(-) 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, From bdaba8aee5c3806d78ee4f130048b2238c636d47 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 09:34:12 +0100 Subject: [PATCH 085/271] perf test: Use machine__new_host in dwarf unwind test This is more straightforward than what we have now. It also fixes a segfault within machine__exit, that's caused by not creating kernel maps for machine.. We're calling machine__destroy_kernel_maps in machine__exit since commit: ebe9729c8c31 perf machine: Fix to destroy kernel maps when machine exits Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1449131658-1841-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/dwarf-unwind.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 3cce13b19cbb..1c5c0221cea2 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -160,14 +160,11 @@ static int krava_1(struct thread *thread) int test__dwarf_unwind(int subtest __maybe_unused) { - struct machines machines; struct machine *machine; struct thread *thread; int err = -1; - machines__init(&machines); - - machine = machines__find(&machines, HOST_KERNEL_ID); + machine = machine__new_host(); if (!machine) { pr_err("Could not get machine\n"); return -1; @@ -199,7 +196,6 @@ int test__dwarf_unwind(int subtest __maybe_unused) out: machine__delete_threads(machine); - machine__exit(machine); - machines__exit(&machines); + machine__delete(machine); return err; } From 046847935754f27c2e8334ff15abda0b733a1fd4 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 09:34:13 +0100 Subject: [PATCH 086/271] perf test: Use machine__new_host in mmap thread lookup test This is more straightforward than what we have now. It also fixes a segfault within machine__exit, that's caused by not creating kernel maps for machine.. We're calling machine__destroy_kernel_maps in machine__exit since commit: ebe9729c8c31 perf machine: Fix to destroy kernel maps when machine exits Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1449131658-1841-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/mmap-thread-lookup.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c index 6cdb97579c45..0c5ce44f723f 100644 --- a/tools/perf/tests/mmap-thread-lookup.c +++ b/tools/perf/tests/mmap-thread-lookup.c @@ -149,7 +149,6 @@ static int synth_process(struct machine *machine) static int mmap_events(synth_cb synth) { - struct machines machines; struct machine *machine; int err, i; @@ -162,8 +161,7 @@ static int mmap_events(synth_cb synth) */ TEST_ASSERT_VAL("failed to create threads", !threads_create()); - machines__init(&machines); - machine = &machines.host; + machine = machine__new_host(); dump_trace = verbose > 1 ? 1 : 0; @@ -203,7 +201,7 @@ static int mmap_events(synth_cb synth) } machine__delete_threads(machine); - machines__exit(&machines); + machine__delete(machine); return err; } From 0fd4008ed755c52d85117302a3c2c108b2958420 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 09:34:14 +0100 Subject: [PATCH 087/271] perf test: Use machine__new_host in mmap thread code reading test This is more straightforward than what we have now. It also fixes a segfault within machine__exit, that's caused by not creating kernel maps for machine.. We're calling machine__destroy_kernel_maps in machine__exit since commit: ebe9729c8c31 perf machine: Fix to destroy kernel maps when machine exits Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1449131658-1841-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/code-reading.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 4417b6a079f0..26182ffcea75 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -433,7 +433,6 @@ enum { static int do_test_code_reading(bool try_kcore) { - struct machines machines; struct machine *machine; struct thread *thread; struct record_opts opts = { @@ -459,8 +458,7 @@ static int do_test_code_reading(bool try_kcore) pid = getpid(); - machines__init(&machines); - machine = &machines.host; + machine = machine__new_host(); ret = machine__create_kernel_maps(machine); if (ret < 0) { @@ -594,9 +592,8 @@ out_err: cpu_map__put(cpus); thread_map__put(threads); } - machines__destroy_kernel_maps(&machines); machine__delete_threads(machine); - machines__exit(&machines); + machine__delete(machine); return err; } From 7320b1b3d9e6af30adcbead64568be3c40b50e59 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 09:34:15 +0100 Subject: [PATCH 088/271] perf test: Fix cpus and thread maps reference in error path In error path to try user space event, both cpus and threads map now owned by evlist and freed by perf_evlist__set_maps call. Getting reference to keep them alive. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1449131658-1841-5-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/code-reading.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 26182ffcea75..313a48c6b2bc 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -547,6 +547,13 @@ static int do_test_code_reading(bool try_kcore) if (ret < 0) { if (!excl_kernel) { excl_kernel = true; + /* + * Both cpus and threads are now owned by evlist + * and will be freed by following perf_evlist__set_maps + * call. Getting refference to keep them alive. + */ + cpu_map__get(cpus); + thread_map__get(threads); perf_evlist__set_maps(evlist, NULL, NULL); perf_evlist__delete(evlist); evlist = NULL; From c0651c41e45dee1d6abb83fd5b25e7097aeac141 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 09:34:16 +0100 Subject: [PATCH 089/271] perf test: Prevent using bpf-output event in round trip name test The bpf-output is added under software events, but is not parse-able within parse_events, which is what round trip test is expecting. Checking software events only until dummy event. Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1449131658-1841-6-git-send-email-jolsa@kernel.org [ Make it a one liner by keeping __perf_evsel__name_array_test() around ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/evsel-roundtrip-name.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 1da92e1159ee..2de4a4f2c3ed 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -103,7 +103,8 @@ int test__perf_evsel__roundtrip_name_test(int subtest __maybe_unused) if (err) ret = err; - err = perf_evsel__name_array_test(perf_evsel__sw_names); + err = __perf_evsel__name_array_test(perf_evsel__sw_names, + PERF_COUNT_SW_DUMMY + 1); if (err) ret = err; From d6e94fa6b6dab4668e46665bbe766142af32cc15 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 2 Dec 2015 22:08:29 +0100 Subject: [PATCH 090/271] perf test: Create kernel maps properly for hist entries test It fixes segfault within machine__exit, that's caused but not creating kernel maps for machine.. We're calling machine__destroy_kernel_maps in machine__exit since commit: ebe9729c8c31 perf machine: Fix to destroy kernel maps when machine exits Signed-off-by: Jiri Olsa Acked-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Masami Hiramatsu Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/tip-k4snzv5t4dvdckggzwdzyljo@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hists_common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index ce80b274b097..46f453b1de60 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c @@ -87,6 +87,11 @@ struct machine *setup_fake_machine(struct machines *machines) return NULL; } + if (machine__create_kernel_maps(machine)) { + pr_debug("Not enough memory for machine setup\n"); + goto out; + } + for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { struct thread *thread; From 5cd95fc3f8d84a8bb256838fa3b6b59e9095eaa2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:40 +0100 Subject: [PATCH 091/271] 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/builtin-stat.c | 5 +---- tools/perf/util/evsel.c | 5 ++++- tools/perf/util/evsel.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index df2fbf046ee2..813c52ad9303 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -256,12 +256,9 @@ static void handle_initial_delay(void) struct perf_evsel *counter; if (initial_delay) { - const int ncpus = cpu_map__nr(evsel_list->cpus), - nthreads = thread_map__nr(evsel_list->threads); - usleep(initial_delay * 1000); evlist__for_each(evsel_list, counter) - perf_evsel__enable(counter, ncpus, nthreads); + perf_evsel__enable(counter); } } 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); From e98a4cbb01e0ba1110eba5166a425b3eab9b2244 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:41 +0100 Subject: [PATCH 092/271] 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(+) 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); From 3e27c92081131738fa4d7dd71673aa6e8c24866d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:42 +0100 Subject: [PATCH 093/271] 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(-) 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; From ab46db0a3325a064bb24e826b12995d157565efb Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:43 +0100 Subject: [PATCH 094/271] perf stat: Use perf_evlist__enable in handle_initial_delay No need to mimic the behaviour of perf_evlist__enable, we can use it directly. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-5-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 813c52ad9303..8ca40deaa728 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -253,12 +253,9 @@ static void process_interval(void) static void handle_initial_delay(void) { - struct perf_evsel *counter; - if (initial_delay) { usleep(initial_delay * 1000); - evlist__for_each(evsel_list, counter) - perf_evsel__enable(counter); + perf_evlist__enable(evsel_list); } } From 67ccdecd09cac818146b1e153ff901cb67570012 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:44 +0100 Subject: [PATCH 095/271] perf stat: Create events as disabled Currently we have 2 kinds of stat counters based on when the event is enabled: 1) tracee command events, which are enable once the tracee executes exec syscall (enable_on_exec bit) 2) all other events which get alive within the perf_event_open syscall And 2) case could raise a problem in case we want additional filter to be attached for event. In this case we want the event to be enabled after it's configured with filter. Changing the behaviour of 2) events, so they all are created as disabled (disabled bit). Adding extra enable call to make them alive once they finish setup. 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-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 8ca40deaa728..2e70610649a1 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -168,11 +168,18 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) attr->sample_period = 0; attr->sample_type = 0; + /* + * Disabling all counters initially, they will be enabled + * either manually by us or by kernel via enable_on_exec + * set later. + */ + if (perf_evsel__is_group_leader(evsel)) + attr->disabled = 1; + if (target__has_cpu(&target)) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) { - attr->disabled = 1; if (!initial_delay) attr->enable_on_exec = 1; } @@ -251,12 +258,18 @@ static void process_interval(void) print_counters(&rs, 0, NULL); } -static void handle_initial_delay(void) +static void enable_counters(void) { - if (initial_delay) { + if (initial_delay) usleep(initial_delay * 1000); + + /* + * We need to enable counters only if: + * - we don't have tracee (attaching to task or cpu) + * - we have initial delay configured + */ + if (!target__none(&target) || initial_delay) perf_evlist__enable(evsel_list); - } } static volatile int workload_exec_errno; @@ -353,7 +366,7 @@ static int __run_perf_stat(int argc, const char **argv) if (forks) { perf_evlist__start_workload(evsel_list); - handle_initial_delay(); + enable_counters(); if (interval) { while (!waitpid(child_pid, &status, WNOHANG)) { @@ -372,7 +385,7 @@ static int __run_perf_stat(int argc, const char **argv) if (WIFSIGNALED(status)) psignal(WTERMSIG(status), argv[0]); } else { - handle_initial_delay(); + enable_counters(); while (!done) { nanosleep(&ts, NULL); if (interval) From c8280cec2a196f2ffea83dd755b17eb020ca1b83 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:45 +0100 Subject: [PATCH 096/271] perf stat: Move enable_on_exec setup under earlier code It's more readable this way and we can save one perf_evsel__is_group_leader condition in current code. 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-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2e70610649a1..e74712dee242 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -173,17 +173,20 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * either manually by us or by kernel via enable_on_exec * set later. */ - if (perf_evsel__is_group_leader(evsel)) + if (perf_evsel__is_group_leader(evsel)) { attr->disabled = 1; + /* + * In case of initial_delay we enable tracee + * events manually. + */ + if (target__none(&target) && !initial_delay) + attr->enable_on_exec = 1; + } + if (target__has_cpu(&target)) return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); - if (!target__has_task(&target) && perf_evsel__is_group_leader(evsel)) { - if (!initial_delay) - attr->enable_on_exec = 1; - } - return perf_evsel__open_per_thread(evsel, evsel_list->threads); } From cfef25b8daf7e4b49c84e174a904af9d89dc7c46 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 6 Dec 2015 23:07:13 +0000 Subject: [PATCH 097/271] 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(+) 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, }, From 8d7d377c2bea16afa6b600a4517615a9eebb259b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Thu, 5 Mar 2015 11:32:13 +0100 Subject: [PATCH 098/271] perf tui: Change default selection background color to yellow Boris reported that 'perf top' is unusable on his default 'black on white' terminal, which uses (eye friendly) light-grey as a background color. The reason is that the TUI cursor for the current selection line uses HE_COLORSET_SELECTED, and that has a default background color of 'lightgrey' - which is a common terminal background choice and thus the colors conflict. Use yellow as the background color instead: that should be an uncommon terminal background, yet it's still ergonomic on both black and white/grey terminals. [ It would be a better solution to straight out detect color collisions and resolve them reasonably by converting them to RGB and calculating color space distances, but I was unable to find proper documentation for SLtt_get_color_object() to recover the current color scheme so I gave up ... Yellow works well enough. ] Reported-and-Tested-by: Borislav Petkov Signed-off-by: Ingo Molnar Tested-by: Arnaldo Carvalho de Melo Cc: David Binderman Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20150305103213.GA23046@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index e9703c0829f1..d37202121689 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -528,7 +528,7 @@ static struct ui_browser_colorset { .colorset = HE_COLORSET_SELECTED, .name = "selected", .fg = "black", - .bg = "lightgray", + .bg = "yellow", }, { .colorset = HE_COLORSET_CODE, From bae32b50ea96ca0f8702ea55e62095e8cc4745e2 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:20 +0900 Subject: [PATCH 099/271] 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(+) 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; From 544c2ae7b1a794ad0bc5ec24d832ab5658d5aef6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:27 +0900 Subject: [PATCH 100/271] perf stat: Fix cmd_stat to release cpu_map Fix cmd_stat() to release cpu_map objects (aggr_map and cpus_aggr_map) afterwards. refcnt debugger shows that the cmd_stat initializes cpu_map but not puts it. ---- # ./perf stat -v ls .... REFCNT: BUG: Unreclaimed objects found. ==== [0] ==== Unreclaimed cpu_map@0x29339c0 Refcount +1 => 1 at ./perf(cpu_map__empty_new+0x6d) [0x4e64bd] ./perf(cmd_stat+0x5fe) [0x43594e] ./perf() [0x47b785] ./perf(main+0x617) [0x422587] /lib64/libc.so.6(__libc_start_main+0xf5) [0x7f2dff420af5] ./perf() [0x4226fd] REFCNT: Total 1 objects are not reclaimed. "cpu_map" leaks 1 objects ---- Signed-off-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151209021127.10245.93697.stgit@localhost.localdomain [ Remove NULL checks before calling the put operation, it checks it already ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e74712dee242..25a95f49c36e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1094,6 +1094,14 @@ static int perf_stat_init_aggr_mode(void) return cpus_aggr_map ? 0 : -ENOMEM; } +static void perf_stat__exit_aggr_mode(void) +{ + cpu_map__put(aggr_map); + cpu_map__put(cpus_aggr_map); + aggr_map = NULL; + cpus_aggr_map = NULL; +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1442,6 +1450,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: perf_evlist__delete(evsel_list); From 17577decb2ddae28f5a449ddb79cf0ed3e2312c5 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:29 +0900 Subject: [PATCH 101/271] 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(-) 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); From d91130e90a005876b488b6d52b743149d95b4a59 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:31 +0900 Subject: [PATCH 102/271] 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(+) 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); From cc1121ab9687d660cc02f50b1a4974112f87a8e6 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:33 +0900 Subject: [PATCH 103/271] 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(+) 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; From 5191d887681dd34ba3993a438d5746378952885a Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:35 +0900 Subject: [PATCH 104/271] 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(-) 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; } From de7cf7cadca3a3c32c1f1dbf4593a54f236e2dcf Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:43 -0600 Subject: [PATCH 105/271] 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 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; From 1fe143c5f928e3d117355ce2655bac0eb80c1aa3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:42 -0600 Subject: [PATCH 106/271] 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 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; From 2bdb2c2729d2ba2f2f90b729d04254308096c5a0 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:46 -0600 Subject: [PATCH 107/271] 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/perf.c | 1 + tools/perf/util/env.c | 9 --------- tools/perf/util/parse-options.c | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 4bee53c3f796..59ea48c7e26c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -384,6 +384,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) use_pager = 1; commit_pager_choice(); + perf_env__set_cmdline(&perf_env, argc, argv); status = p->fn(argc, argv, prefix); exit_browser(status); perf_env__exit(&perf_env); 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; From 0a4bb5da957b83ece8b4723c5bac7a5d29fbfb33 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 7 Dec 2015 22:21:48 -0600 Subject: [PATCH 108/271] 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/Build | 1 + tools/perf/builtin-version.c | 10 ++++++++++ tools/perf/util/help.c | 7 ------- 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 tools/perf/builtin-version.c diff --git a/tools/perf/Build b/tools/perf/Build index 2c7aaf2ba119..2a41217e9d88 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -20,6 +20,7 @@ perf-y += builtin-kvm.o perf-y += builtin-inject.o perf-y += builtin-mem.o perf-y += builtin-data.o +perf-y += builtin-version.o perf-$(CONFIG_AUDIT) += builtin-trace.o perf-$(CONFIG_LIBELF) += builtin-probe.o diff --git a/tools/perf/builtin-version.c b/tools/perf/builtin-version.c new file mode 100644 index 000000000000..9b10cda6b6dc --- /dev/null +++ b/tools/perf/builtin-version.c @@ -0,0 +1,10 @@ +#include "util/util.h" +#include "builtin.h" +#include "perf.h" + +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; +} 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; -} From 50e19ef978158a3d1f790568eccd8e4a802190c2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:53 +0900 Subject: [PATCH 109/271] perf annotate: Check argument before calling setup_browser() This is necessary to get rid of the browser dependency from usage_with_options() and its friends. Because there's no code changing the argc and argv, it'd be ok to check it early. 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-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2bf9b3fd9e61..55f6f8dab5d4 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -343,6 +343,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) return ret; argc = parse_options(argc, argv, options, annotate_usage, 0); + if (argc) { + /* + * Special case: if there's an argument left then assume that + * it's a symbol filter: + */ + if (argc > 1) + usage_with_options(annotate_usage, options); + + annotate.sym_hist_filter = argv[0]; + } if (annotate.use_stdio) use_browser = 0; @@ -369,17 +379,6 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) if (setup_sorting() < 0) usage_with_options(annotate_usage, options); - if (argc) { - /* - * Special case: if there's an argument left then assume that - * it's a symbol filter: - */ - if (argc > 1) - usage_with_options(annotate_usage, options); - - annotate.sym_hist_filter = argv[0]; - } - ret = __cmd_annotate(&annotate); out_delete: From 3df668e74a5bc60d74c2ce0b3498af2d77b4b556 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:54 +0900 Subject: [PATCH 110/271] perf annotate: Delay UI browser setup after initialization is done Move setup_browser after all necessary initialization is done. This is to remove the browser dependency from usage_with_options and friends. 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-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 55f6f8dab5d4..1f00dc7cecba 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -354,17 +354,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) annotate.sym_hist_filter = argv[0]; } - if (annotate.use_stdio) - use_browser = 0; - else if (annotate.use_tui) - use_browser = 1; - else if (annotate.use_gtk) - use_browser = 2; - file.path = input_name; - setup_browser(true); - annotate.session = perf_session__new(&file, false, &annotate.tool); if (annotate.session == NULL) return -1; @@ -379,6 +370,15 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) if (setup_sorting() < 0) usage_with_options(annotate_usage, options); + if (annotate.use_stdio) + use_browser = 0; + else if (annotate.use_tui) + use_browser = 1; + else if (annotate.use_gtk) + use_browser = 2; + + setup_browser(true); + ret = __cmd_annotate(&annotate); out_delete: From 1b0344e64d7b4e512a8e5d2bc88b022fbb7a9ee6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:55 +0900 Subject: [PATCH 111/271] perf kvm: Remove invocation of setup/exit_browser() Calling setup_browser(false) with use_browser = 0 is meaningless. Just get rid of it. This is necessary to remove the browser dependency from usage_with_options() and friends. 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-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kvm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index dd94b4ca2213..031f9f55c281 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -1351,7 +1351,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, disable_buildid_cache(); use_browser = 0; - setup_browser(false); if (argc) { argc = parse_options(argc, argv, live_options, @@ -1409,8 +1408,6 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, err = kvm_events_live_report(kvm); out: - exit_browser(0); - if (kvm->session) perf_session__delete(kvm->session); kvm->session = NULL; From b3f38fc2422ace049110d1588a67b35bd15b81ce Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:56 +0900 Subject: [PATCH 112/271] perf report: Check argument before calling setup_browser() This is necessary to get rid of the browser dependency from usage_with_options() and its friends. Because there's no code changing the argc and argv, it'd be ok to check it early. 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-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index af5db885ea9c..5a454669d075 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -801,6 +801,16 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) perf_config(report__config, &report); argc = parse_options(argc, argv, options, report_usage, 0); + if (argc) { + /* + * Special case: if there's an argument left then assume that + * it's a symbol filter: + */ + if (argc > 1) + usage_with_options(report_usage, options); + + report.symbol_filter_str = argv[0]; + } if (symbol_conf.vmlinux_name && access(symbol_conf.vmlinux_name, R_OK)) { @@ -946,17 +956,6 @@ repeat: if (symbol__init(&session->header.env) < 0) goto error; - if (argc) { - /* - * Special case: if there's an argument left then assume that - * it's a symbol filter: - */ - if (argc > 1) - usage_with_options(report_usage, options); - - report.symbol_filter_str = argv[0]; - } - sort__setup_elide(stdout); ret = __cmd_report(&report); From f8a5c0b24b8b1e77a0812b0c8251db0afc0524b7 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 10 Dec 2015 14:48:45 -0300 Subject: [PATCH 113/271] perf top: Do show usage message when failing to create cpu/thread maps This is necessary to get rid of the browser dependency from usage_with_options() and its friends. Because we validate the targets which are used to create the cpu/thread maps and inform the user about any override performed via the chosen UI, we don't need to call the usage routine for that. Cc: David Ahern Cc: Jiri Olsa Cc: Josh Poimboeuf Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/n/tip-slu7lj7buzpwgop1vo9la8ma@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7e2e72e6d9d1..84fd6368ed6d 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1279,8 +1279,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (target__none(target)) target->system_wide = true; - if (perf_evlist__create_maps(top.evlist, target) < 0) - usage_with_options(top_usage, options); + if (perf_evlist__create_maps(top.evlist, target) < 0) { + ui__error("Couldn't create thread/CPU maps: %s\n", + errno == ENOENT ? "No such process" : strerror_r(errno, errbuf, sizeof(errbuf))); + goto out_delete_evlist; + } if (!top.evlist->nr_entries && perf_evlist__add_default(top.evlist) < 0) { From 7ecb48fde39e1d61ab8aff95581dcdfb572bcc28 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:58 +0900 Subject: [PATCH 114/271] 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(+) 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; } From 3f86eb6b0771d785099c91354838d3f8d8126630 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 12:00:59 +0900 Subject: [PATCH 115/271] 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(-) 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); From 61fa0e94ca6ab62db5e095a5528150bf9962196d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 10 Dec 2015 16:53:20 +0900 Subject: [PATCH 116/271] 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(-) 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); } /* From 8488335c039ff4917754332763e21c01a81435b4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 8 Dec 2015 16:51:24 -0300 Subject: [PATCH 117/271] Revert "perf tools: Improve setting of gcc debug option" This reverts commit e8b7ea4356fdd3c4de5478f3418eb84f8dce2b61. Martin created a gcc PR: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68836 Reported-by: Jiri Olsa Acked-by: Ingo Molnar Cc: David Ahern Cc: Martin Liska Cc: Namhyung Kim , Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20151202164827.GA21124@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/config/Makefile | 2 -- tools/perf/config/utilities.mak | 19 ------------------- 2 files changed, 21 deletions(-) diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 6eb9a956a408..a5524179d26e 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -135,8 +135,6 @@ endif ifeq ($(DEBUG),0) CFLAGS += -O6 -else - CFLAGS += $(call cc-option,-Og,-O0) endif ifdef PARSER_DEBUG diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 0ebef09c0842..c16ce833079c 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak @@ -177,22 +177,3 @@ $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) endef _ge_attempt = $(if $(get-executable),$(get-executable),$(call _gea_err,$(2))) _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) - -# try-run -# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) -# Exit code chooses option. "$$TMP" is can be used as temporary file and -# is automatically cleaned up. -try-run = $(shell set -e; \ - TMP="$(TMPOUT).$$$$.tmp"; \ - TMPO="$(TMPOUT).$$$$.o"; \ - if ($(1)) >/dev/null 2>&1; \ - then echo "$(2)"; \ - else echo "$(3)"; \ - fi; \ - rm -f "$$TMP" "$$TMPO") - -# cc-option -# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586) - -cc-option = $(call try-run,\ - $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2)) From 9d8b172f29ac0e5d1923d348e395e9643625ef7f Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:23 +0900 Subject: [PATCH 118/271] 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/builtin-top.c | 2 +- tools/perf/util/session.c | 11 +++++++---- tools/perf/util/session.h | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 84fd6368ed6d..785aa2dd8f0b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -964,7 +964,7 @@ static int __cmd_top(struct perf_top *top) if (ret) goto out_delete; - if (perf_session__register_idle_thread(top->session) == NULL) + if (perf_session__register_idle_thread(top->session) < 0) goto out_delete; machine__synthesize_threads(&top->session->machines.host, &opts->target, 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); From e7a7865cc0da306542db0b9205cb0a467f59e33d Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Wed, 9 Dec 2015 11:11:18 +0900 Subject: [PATCH 119/271] 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(-) 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; From 64226bcf64629996948dc03c38594f00511bfc2b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Dec 2015 11:56:53 +0900 Subject: [PATCH 120/271] perf top: Do not convert address for perf_top__record_precise_ip() We call map->unmap_ip() before the function and call map->map_ip() inside the function. This is meaningless and look strange since only one of the two checks 'map'. Let's use al->addr directly. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449802616-16170-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 785aa2dd8f0b..3b0978e5578a 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -189,8 +189,6 @@ static void perf_top__record_precise_ip(struct perf_top *top, if (pthread_mutex_trylock(¬es->lock)) return; - ip = he->ms.map->map_ip(he->ms.map, ip); - if (ui__has_annotation()) err = hist_entry__inc_addr_samples(he, counter, ip); @@ -687,14 +685,8 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter, struct hist_entry *he = iter->he; struct perf_evsel *evsel = iter->evsel; - if (sort__has_sym && single) { - u64 ip = al->addr; - - if (al->map) - ip = al->map->unmap_ip(al->map, ip); - - perf_top__record_precise_ip(top, he, evsel->idx, ip); - } + if (sort__has_sym && single) + perf_top__record_precise_ip(top, he, evsel->idx, al->addr); hist__account_cycles(iter->sample->branch_stack, al, iter->sample, !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY)); From 151ee834cc946fa159ee406c62b4d5ce1ebd7115 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Dec 2015 11:56:54 +0900 Subject: [PATCH 121/271] perf top: Access hists->lock only if needed The perf_top__record_precise_ip() releases and regrabs the he->hists->lock because it can sleep if there's an error. But it should be done conditionally as it slows down the fast path. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449802616-16170-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 3b0978e5578a..586798acf7db 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -194,21 +194,23 @@ static void perf_top__record_precise_ip(struct perf_top *top, pthread_mutex_unlock(¬es->lock); - /* - * This function is now called with he->hists->lock held. - * Release it before going to sleep. - */ - pthread_mutex_unlock(&he->hists->lock); + if (unlikely(err)) { + /* + * This function is now called with he->hists->lock held. + * Release it before going to sleep. + */ + pthread_mutex_unlock(&he->hists->lock); - if (err == -ERANGE && !he->ms.map->erange_warned) - ui__warn_map_erange(he->ms.map, sym, ip); - else if (err == -ENOMEM) { - pr_err("Not enough memory for annotating '%s' symbol!\n", - sym->name); - sleep(1); + if (err == -ERANGE && !he->ms.map->erange_warned) + ui__warn_map_erange(he->ms.map, sym, ip); + else if (err == -ENOMEM) { + pr_err("Not enough memory for annotating '%s' symbol!\n", + sym->name); + sleep(1); + } + + pthread_mutex_lock(&he->hists->lock); } - - pthread_mutex_lock(&he->hists->lock); } static void perf_top__show_details(struct perf_top *top) From 448f13b2d18fdc8dbaada97442e8954dcb4ef8fa Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Dec 2015 11:56:55 +0900 Subject: [PATCH 122/271] perf top: Fix annotation on --stdio The ui__has_annotation() inside perf_top__record_precise_ip() should be removed since it returns true only for TUI (and when sort key has symbol). However the 'perf top --stdio' also supports annotation for a symbol which was specified by 's' key action. Actually it already does the necessary checks before calling the function. So it's ok to get rid of the check here. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449802616-16170-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 586798acf7db..f447e5531f8b 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -189,8 +189,7 @@ static void perf_top__record_precise_ip(struct perf_top *top, if (pthread_mutex_trylock(¬es->lock)) return; - if (ui__has_annotation()) - err = hist_entry__inc_addr_samples(he, counter, ip); + err = hist_entry__inc_addr_samples(he, counter, ip); pthread_mutex_unlock(¬es->lock); From beefb8d0e556aaf3cb69168c5953e023ace6aa78 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 11 Dec 2015 11:56:56 +0900 Subject: [PATCH 123/271] perf top: Cleanup condition in perf_top__record_precise_ip() The 'he' cannot be NULL since it's caller hist_iter__top_callback() is called only if iter->he is not NULL (see hist_entry_iter__add). So setting 'sym' before the condition to simplify the code. Also make it clearer that the top->symbol_filter_entry check is only meaningful on stdio mode (i.e. when use_browser is 0). Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Jiri Olsa Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449802616-16170-4-git-send-email-namhyung@kernel.org [ Complete the simplification replacing one more he->ms.sym with sym ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f447e5531f8b..92fe963e43c4 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -175,15 +175,14 @@ static void perf_top__record_precise_ip(struct perf_top *top, int counter, u64 ip) { struct annotation *notes; - struct symbol *sym; + struct symbol *sym = he->ms.sym; int err = 0; - if (he == NULL || he->ms.sym == NULL || - ((top->sym_filter_entry == NULL || - top->sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1)) + if (sym == NULL || (use_browser == 0 && + (top->sym_filter_entry == NULL || + top->sym_filter_entry->ms.sym != sym))) return; - sym = he->ms.sym; notes = symbol__annotation(sym); if (pthread_mutex_trylock(¬es->lock)) From 973170e66726672518eb935eb0dc0e63876d133d Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:29 +0000 Subject: [PATCH 124/271] tools lib bpf: Check return value of strdup when reading map names Commit 561bbccac72d08babafaa33fd7fa9100ec4c9fb6 ("tools lib bpf: Extract and collect map names from BPF object file") forgets checking return value of strdup(). This patch fixes it. It also checks names pointer before strcmp() for safety. Signed-off-by: Wang Nan Acked-by: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Fixes: 561bbccac72d ("tools lib bpf: Extract and collect map names from BPF object file") Link: http://lkml.kernel.org/r/1449541544-67621-2-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index a298614ad091..16485ab05fc1 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -527,14 +527,14 @@ bpf_object__init_maps(struct bpf_object *obj, void *data, return 0; } -static void +static int bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx) { int i; Elf_Data *symbols = obj->efile.symbols; if (!symbols || maps_shndx < 0) - return; + return -EINVAL; for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { GElf_Sym sym; @@ -556,9 +556,14 @@ bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx) continue; } obj->maps[map_idx].name = strdup(map_name); + if (!obj->maps[map_idx].name) { + pr_warning("failed to alloc map name\n"); + return -ENOMEM; + } pr_debug("map %zu is \"%s\"\n", map_idx, obj->maps[map_idx].name); } + return 0; } static int bpf_object__elf_collect(struct bpf_object *obj) @@ -663,7 +668,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) } if (maps_shndx >= 0) - bpf_object__init_maps_name(obj, maps_shndx); + err = bpf_object__init_maps_name(obj, maps_shndx); out: return err; } @@ -1372,7 +1377,7 @@ bpf_object__get_map_by_name(struct bpf_object *obj, const char *name) struct bpf_map *pos; bpf_map__for_each(pos, obj) { - if (strcmp(pos->name, name) == 0) + if (pos->name && !strcmp(pos->name, name)) return pos; } return NULL; From 77ba9a5b48a7c742f9a46d26596852e9cfec7900 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:30 +0000 Subject: [PATCH 125/271] tools lib bpf: Fetch map names from correct strtab Namhyung Kim pointed out a potential problem in original code that it fetches names of maps from section header string table, which is used to store section names. Original code doesn't cause error because of a LLVM behavior that, it combines shstrtab into strtab. For example: $ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -o temp.o -c - $ readelf -h ./temp.o ELF Header: Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 ... Section header string table index: 1 $ readelf -S ./temp.o There are 10 section headers, starting at offset 0x288: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .strtab STRTAB 0000000000000000 00000230 0000000000000051 0000000000000000 0 0 1 ... $ readelf -p .strtab ./temp.o String dump of section '.strtab': [ 1] .text [ 7] .comment [ 10] .bss [ 15] .note.GNU-stack [ 25] .rela.eh_frame [ 34] func [ 39] .strtab [ 41] .symtab [ 49] .data [ 4f] - $ readelf -p .shstrtab ./temp.o readelf: Warning: Section '.shstrtab' was not dumped because it does not exist! Where, 'section header string table index' points to '.strtab', and symbol names are also stored there. However, in case of gcc: $ echo 'int func() {return 0;}' | gcc -x c -o temp.o -c - $ readelf -p .shstrtab ./temp.o String dump of section '.shstrtab': [ 1] .symtab [ 9] .strtab [ 11] .shstrtab [ 1b] .text [ 21] .data [ 27] .bss [ 2c] .comment [ 35] .note.GNU-stack [ 45] .rela.eh_frame $ readelf -p .strtab ./temp.o String dump of section '.strtab': [ 1] func They are separated sections. Although original code doesn't cause error, we'd better use canonical method for fetching symbol names to avoid potential behavior changing. This patch learns from readelf's code, fetches string from sh_link of .symbol section. Signed-off-by: Wang Nan Reported-and-Acked-by: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1449541544-67621-3-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/libbpf.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 16485ab05fc1..8334a5a9d5d7 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -195,6 +195,7 @@ struct bpf_object { Elf *elf; GElf_Ehdr ehdr; Elf_Data *symbols; + size_t strtabidx; struct { GElf_Shdr shdr; Elf_Data *data; @@ -547,7 +548,7 @@ bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx) continue; map_name = elf_strptr(obj->efile.elf, - obj->efile.ehdr.e_shstrndx, + obj->efile.strtabidx, sym.st_name); map_idx = sym.st_value / sizeof(struct bpf_map_def); if (map_idx >= obj->nr_maps) { @@ -630,8 +631,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj) pr_warning("bpf: multiple SYMTAB in %s\n", obj->path); err = -LIBBPF_ERRNO__FORMAT; - } else + } else { obj->efile.symbols = data; + obj->efile.strtabidx = sh.sh_link; + } } else if ((sh.sh_type == SHT_PROGBITS) && (sh.sh_flags & SHF_EXECINSTR) && (data->d_size > 0)) { @@ -667,6 +670,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj) goto out; } + if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) { + pr_warning("Corrupted ELF file: index of strtab invalid\n"); + return LIBBPF_ERRNO__FORMAT; + } if (maps_shndx >= 0) err = bpf_object__init_maps_name(obj, maps_shndx); out: From 26812d466b2633d0c772fe3aca954129f150d3cb Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:39 +0000 Subject: [PATCH 126/271] 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(+) 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(); From 27cfef009ae8a1019d174153987ce22a0e6677fc Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:43 +0000 Subject: [PATCH 127/271] perf script: Add support for PERF_TYPE_BREAKPOINT Useful for getting stack traces for hardware breakpoint events. Test result: Before this patch: # ~/perf record -g -e mem:0x600980 ./sample [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.011 MB perf.data (12 samples) ] # ~/perf script # ~/perf script -F comm,tid,pid,time,event,ip,sym,dso sample 22520/22520 97457.836294: mem:0x600980: 5a4ad8 __clear_user (/lib/modules/4.3.0-rc4+/build/vmlinux) ... 3f41ba sys_execve (/lib/modules/4.3.0-rc4+/build/vmlinux) 979395 return_from_execve (/lib/modules/4.3.0-rc4+/build/vmlinux) 7f1b59719cf7 [unknown] ([unknown]) sample 22520/22520 97457.836648: mem:0x600980: 532 main (/home/w00229757/DataBreakpoints/sample) 21bd5 __libc_start_main (/tmp/oxygen_root-root/lib64/libc-2.18.so) ... After this patch: # ~/perf script sample 22520 97457.836294: mem:0x600980: 5a4ad8 __clear_user (/lib/modules/4.3.0-rc4+/build/vmlinux) ... 3f41ba sys_execve (/lib/modules/4.3.0-rc4+/build/vmlinux) 979395 return_from_execve (/lib/modules/4.3.0-rc4+/build/vmlinux) 7f1b59719cf7 [unknown] ([unknown]) sample 22520 97457.836648: mem:0x600980: 532 main (/home/w00229757/DataBreakpoints/sample) 21bd5 __libc_start_main (/tmp/oxygen_root-root/lib64/libc-2.18.so) Committer note: So, further testing, lets do it for a kernel global variable, tcp_hashinfo: # grep -w tcp_hashinfo /proc/kallsyms ffffffff8202fc00 B tcp_hashinfo # Note: allow specifying mem:tcp_hashinfo: # perf record -g -e mem:0xffffffff81c65ac0 -a ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.790 MB perf.data ] # # perf evlist mem:0xffffffff8202fc00 # perf evlist -v mem:0xffffffff8202fc00: type: 5, size: 112, { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|CPU, disabled: 1, inherit: 1, mmap: 1, comm: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, bp_type: 3, { bp_addr, config1 }: 0xffffffff8202fc00, { bp_len, config2 }: 0x4 # Then, after this patch: # perf script swapper 0 [000] 171036.986988: mem:0xffffffff8202fc00: 8a0fb5 __inet_lookup_established (/lib/modules/4.3.0+/build/vmlinux) 8bc09d tcp_v4_early_demux (/lib/modules/4.3.0+/build/vmlinux) 896def ip_rcv_finish (/lib/modules/4.3.0+/build/vmlinux) 8976c2 ip_rcv (/lib/modules/4.3.0+/build/vmlinux) 855eba __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux) 8572a8 process_backlog (/lib/modules/4.3.0+/build/vmlinux) 856b11 net_rx_action (/lib/modules/4.3.0+/build/vmlinux) 2a284b __do_softirq (/lib/modules/4.3.0+/build/vmlinux) 2a2ba3 irq_exit (/lib/modules/4.3.0+/build/vmlinux) 96b7a4 do_IRQ (/lib/modules/4.3.0+/build/vmlinux) 969807 ret_from_intr (/lib/modules/4.3.0+/build/vmlinux) 804c27 cpuidle_enter (/lib/modules/4.3.0+/build/vmlinux) 2ded22 call_cpuidle (/lib/modules/4.3.0+/build/vmlinux) 2defb6 cpu_startup_entry (/lib/modules/4.3.0+/build/vmlinux) 95d5bc rest_init (/lib/modules/4.3.0+/build/vmlinux) 1163ffa start_kernel ([kernel.vmlinux].init.text) 11634d7 x86_64_start_reservations ([kernel.vmlinux].init.text) 1163623 x86_64_start_kernel ([kernel.vmlinux].init.text) Signed-off-by: Wang Nan Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: David S. Miller Cc: Jiri Olsa Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1449541544-67621-16-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 3c3f8d0e3064..d259e9aa3a71 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -130,6 +130,18 @@ static struct { .invalid_fields = PERF_OUTPUT_TRACE, }, + + [PERF_TYPE_BREAKPOINT] = { + .user_set = false, + + .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | + PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | + PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | + PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | + PERF_OUTPUT_PERIOD, + + .invalid_fields = PERF_OUTPUT_TRACE, + }, }; static bool output_set_by_user(void) @@ -1129,6 +1141,8 @@ static int parse_output_fields(const struct option *opt __maybe_unused, type = PERF_TYPE_TRACEPOINT; else if (!strcmp(str, "raw")) type = PERF_TYPE_RAW; + else if (!strcmp(str, "break")) + type = PERF_TYPE_BREAKPOINT; else { fprintf(stderr, "Invalid event type in field string.\n"); rc = -EINVAL; From 93b0ba3c60da89043ce2b9f601cd2b3da408903b Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 8 Dec 2015 02:25:44 +0000 Subject: [PATCH 128/271] 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(+) 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); From 9daddf66a37708ec7182a7058f159166d12c9812 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Dec 2015 16:43:57 -0300 Subject: [PATCH 129/271] 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(-) 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) From b6847d2c2a50e96680e233ce4b2784981b6f309e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Dec 2015 19:06:53 -0300 Subject: [PATCH 130/271] perf test: Dump the stack when test segfaults when in verbose mode E.g.: # perf test 26 26: Test mmap thread lookup : FAILED! # perf test -v 26 26: Test mmap thread lookup : --- start --- test child forked, pid 9269 tid = 9269, map = 0x7ff99ff0c000 tid = 9270, map = 0x7ff99ff0b000 tid = 9271, map = 0x7ff99ff0a000 tid = 9272, map = 0x7ff99ff09000 perf: Segmentation fault Obtained 13 stack frames. perf(sighandler_dump_stack+0x41) [0x4e3541] /lib64/libc.so.6(+0x34960) [0x7ff99d5f6960] perf(thread__put+0x5b) [0x4c6f6b] perf(machine__process_event+0x14e) [0x4bd37e] perf(perf_event__synthesize_threads+0x3aa) [0x48678a] perf(test__mmap_thread_lookup+0x20a) [0x474e0a] perf() [0x460d56] perf(cmd_test+0x589) [0x461319] perf() [0x47c641] perf(main+0x617) [0x422317] /lib64/libc.so.6(__libc_start_main+0xf0) [0x7ff99d5e1fe0] perf() [0x422429] [(nil)] test child interrupted ---- end ---- Test mmap thread lookup: FAILED! # Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-sypazzsl4ptctrmlyi2zcmaj@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 2b1ade1aafc3..fa98406c92e2 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -236,6 +236,9 @@ static int run_test(struct test *test, int subtest) dup2(STDOUT_FILENO, STDERR_FILENO); close(nullfd); } + } else { + signal(SIGSEGV, sighandler_dump_stack); + signal(SIGFPE, sighandler_dump_stack); } err = test->func(subtest); From abd828688407eb86044f1bc9e5133c55d7597596 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 11 Dec 2015 19:11:23 -0300 Subject: [PATCH 131/271] 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(-) 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); } From bd0f889536f80630c1c4a414f2de90744d2c87d0 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 11 Dec 2015 16:12:24 -0800 Subject: [PATCH 132/271] 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(-) 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: From 71d6de64feddd4b455555326fba2111b3006d9e0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 14 Dec 2015 12:11:13 +0900 Subject: [PATCH 133/271] perf test: Fix hist testcases when kptr_restrict is on Currently if kptr_restrict is enabled, all hist tests failed with segfaults. This is because machine__create_kernel_maps() in setup_fake_machine() failed in that situation, and it called machine__delete() on the error path. But outer callers again called machines__exit() causing double free for the host machine. Signed-off-by: Namhyung Kim Cc: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1450062673-22312-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/hists_common.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c index 46f453b1de60..bcfd081ee1d2 100644 --- a/tools/perf/tests/hists_common.c +++ b/tools/perf/tests/hists_common.c @@ -88,8 +88,8 @@ struct machine *setup_fake_machine(struct machines *machines) } if (machine__create_kernel_maps(machine)) { - pr_debug("Not enough memory for machine setup\n"); - goto out; + pr_debug("Cannot create kernel maps\n"); + return NULL; } for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { @@ -155,7 +155,6 @@ struct machine *setup_fake_machine(struct machines *machines) out: pr_debug("Not enough memory for machine setup\n"); machine__delete_threads(machine); - machine__delete(machine); return NULL; } From 26bc9b2df1f38536cdfd58df94bf8b5601eb894a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:01 -0600 Subject: [PATCH 134/271] perf build: Remove unnecessary line in Makefile.feature This line always silently fails because it doesn't add the 'test-' prefix to the .bin file. And it seems to be unnecessary anyway: the line immediately after it does all the individual feature checks. Signed-off-by: Josh Poimboeuf Acked-by: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/554a05c18af564ba015c9e68f25730126e0f4acb.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 37ff4c9f92f1..b8c31ece2d96 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -101,7 +101,6 @@ ifeq ($(feature-all), 1) # $(foreach feat,$(FEATURE_TESTS),$(call feature_set,$(feat))) else - $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS)" LDFLAGS=$(LDFLAGS) -i -j -C $(feature_dir) $(addsuffix .bin,$(FEATURE_TESTS)) >/dev/null 2>&1) $(foreach feat,$(FEATURE_TESTS),$(call feature_check,$(feat))) endif From 8bda6a63a049caee4f8ddf2dd99055794df96e4f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:03 -0600 Subject: [PATCH 135/271] perf test: Add Build file to dependencies for llvm-src-*.c Because the Build file writes source code to the generated llvm-src-*.c files, it should be listed as one of the dependencies, so that any future changes to the code being echoed won't require a 'make clean'. Signed-off-by: Josh Poimboeuf Acked-by: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/9b9886c295750dc83cbbb29a665d280f9c5e8b3e.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/Build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 0ff8a973b81c..f23fb7ed4400 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -35,21 +35,21 @@ perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o -$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c +$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) $(Q)echo '#include ' > $@ $(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ $(Q)echo ';' >> $@ -$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c +$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c tests/Build $(call rule_mkdir) $(Q)echo '#include ' > $@ $(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ $(Q)echo ';' >> $@ -$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c +$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c tests/Build $(call rule_mkdir) $(Q)echo '#include ' > $@ $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@ From 004bd89da8c8e7df87e951bf88e34af67348e4e9 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:04 -0600 Subject: [PATCH 136/271] perf test: Remove tarpkg at end of test Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/5e7e97a23e3ce11b59d1009b39ebb6d2813a0560.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/make | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 8ea3dffc5065..c1fbb8e884c0 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -259,7 +259,8 @@ $(run_O): tarpkg: @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ echo "- $@: $$cmd" && echo $$cmd > $@ && \ - ( eval $$cmd ) >> $@ 2>&1 + ( eval $$cmd ) >> $@ 2>&1 && \ + rm -f $@ make_kernelsrc: @echo "- make -C tools/perf" From 8f46dfd73e5378909834bd32a4e7710cd5522506 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:05 -0600 Subject: [PATCH 137/271] perf build: Fix 'make clean' Add some missing files to the 'make clean' target. Reported-and-Acked-by: Jiri Olsa Signed-off-by: Josh Poimboeuf Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/8b1f5a5bd66a652be071d423e64aaa994254be31.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 929a32ba15f5..906c72364d3a 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -582,15 +582,16 @@ $(INSTALL_DOC_TARGETS): # config-clean: $(call QUIET_CLEAN, config) - $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null + $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) - $(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete + $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT).config-detected $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ - $(OUTPUT)util/intel-pt-decoder/inat-tables.c + $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ + $(OUTPUT)tests/llvm-src-{base,kbuild,prologue}.c $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(python-clean) From 212e984a07d19c2e6b83da4ebac4e965dd92efd3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:06 -0600 Subject: [PATCH 138/271] perf build: Rename LIB_PATH -> API_PATH 'LIB_PATH' is a misnomer because there are multiple library paths. Signed-off-by: Josh Poimboeuf Acked-by: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/c10df0b749a27f05cc531fe06b8dd71a329341fa.1449965119.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 906c72364d3a..388ec64fa39b 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -185,13 +185,13 @@ ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) BPF_PATH=$(OUTPUT) ifneq ($(subdir),) - LIB_PATH=$(OUTPUT)/../lib/api/ + API_PATH=$(OUTPUT)/../lib/api/ else - LIB_PATH=$(OUTPUT) + API_PATH=$(OUTPUT) endif else TE_PATH=$(TRACE_EVENT_DIR) - LIB_PATH=$(LIB_DIR) + API_PATH=$(LIB_DIR) BPF_PATH=$(BPF_DIR) endif @@ -201,7 +201,7 @@ export LIBTRACEEVENT LIBTRACEEVENT_DYNAMIC_LIST = $(TE_PATH)libtraceevent-dynamic-list LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYNAMIC_LIST) -LIBAPI = $(LIB_PATH)libapi.a +LIBAPI = $(API_PATH)libapi.a export LIBAPI LIBBPF = $(BPF_PATH)libbpf.a From 32a56bd438ab3023d08874e2770aa0675364b8ab Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:07 -0600 Subject: [PATCH 139/271] 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 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 */ From a871a775172ac586b76199fd158e2843971bd052 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:08 -0600 Subject: [PATCH 140/271] 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(-) 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; } From 5feaac248a46dd5f9876c4ae45c4bbbde5472e90 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:09 -0600 Subject: [PATCH 141/271] 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 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); From 408cf34c176e1832bc2f9f68033a55a765484f93 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Sun, 13 Dec 2015 22:18:12 -0600 Subject: [PATCH 142/271] 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(-) 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); From 48e1cab1ba4db84fbc26379b887ba94a180347fe Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Mon, 14 Dec 2015 10:39:22 +0000 Subject: [PATCH 143/271] 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/builtin-probe.c | 15 +++- tools/perf/builtin-record.c | 9 ++- tools/perf/util/parse-options.c | 118 +++++++++++++++++++++++++++++--- tools/perf/util/parse-options.h | 5 ++ 4 files changed, 134 insertions(+), 13 deletions(-) diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 132afc97676c..dbe2ea5a2932 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -249,6 +249,9 @@ static int opt_show_vars(const struct option *opt, return ret; } +#else +# define opt_show_lines NULL +# define opt_show_vars NULL #endif static int opt_add_probe_event(const struct option *opt, const char *str, int unset __maybe_unused) @@ -473,7 +476,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) opt_add_probe_event), OPT_BOOLEAN('f', "force", &probe_conf.force_add, "forcibly add events" " with existing name"), -#ifdef HAVE_DWARF_SUPPORT OPT_CALLBACK('L', "line", NULL, "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]", "Show source code lines.", opt_show_lines), @@ -490,7 +492,6 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) "directory", "path to kernel source"), OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines, "Don't search inlined functions"), -#endif OPT__DRY_RUN(&probe_event_dry_run), OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes, "Set how many probe points can be found for a probe."), @@ -521,6 +522,16 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) #ifdef HAVE_DWARF_SUPPORT set_option_flag(options, 'L', "line", PARSE_OPT_EXCLUSIVE); set_option_flag(options, 'V', "vars", PARSE_OPT_EXCLUSIVE); +#else +# define set_nobuild(s, l, c) set_option_nobuild(options, s, l, "NO_DWARF=1", c) + set_nobuild('L', "line", false); + set_nobuild('V', "vars", false); + set_nobuild('\0', "externs", false); + set_nobuild('\0', "range", false); + set_nobuild('k', "vmlinux", true); + set_nobuild('s', "source", true); + set_nobuild('\0', "no-inlines", true); +# undef set_nobuild #endif set_option_flag(options, 'F', "funcs", PARSE_OPT_EXCLUSIVE); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 199fc31e3919..c2ba377e8d3a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1113,12 +1113,10 @@ struct option __record_options[] = { "per thread proc mmap processing timeout in ms"), OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events, "Record context switch events"), -#ifdef HAVE_LIBBPF_SUPPORT OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path", "clang binary to use for compiling BPF scriptlets"), OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options", "options passed to clang when compiling BPF scriptlets"), -#endif OPT_END() }; @@ -1130,6 +1128,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) struct record *rec = &record; char errbuf[BUFSIZ]; +#ifndef HAVE_LIBBPF_SUPPORT +# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, "NO_LIBBPF=1", c) + set_nobuild('\0', "clang-path", true); + set_nobuild('\0', "clang-opt", true); +# undef set_nobuild +#endif + rec->evlist = perf_evlist__new(); if (rec->evlist == NULL) return -ENOMEM; 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 */ From 7efe0e034c713716060bc7794c7e332589980c70 Mon Sep 17 00:00:00 2001 From: He Kuang Date: Mon, 14 Dec 2015 10:39:23 +0000 Subject: [PATCH 144/271] perf record: Support custom vmlinux path Make perf-record command support --vmlinux option if BPF_PROLOGUE is on. 'perf record' needs vmlinux as the source of DWARF info to generate prologue for BPF programs, so path of vmlinux should be specified. Short name 'k' has been taken by 'clockid'. This patch skips the short option name and uses '--vmlinux' for vmlinux path. Documentation is also updated. Test result: In a production (or broken) environment: (by: # rm -rf ~/.debug/ # mv /lib/modules/`uname -r`/build/vmlinux /tmp/ ) # ./perf record -e ./test_bpf_base.c ls Failed to find the path for kernel: No such file or directory event syntax error: './test_bpf_base.c' \___ You need to check probing points in BPF file ... # ./perf record --vmlinux /tmp/vmlinux -e ./test_bpf_base.c ls ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.011 MB perf.data ] Help messages when build with NO_LIBBPF: # ./perf record -h --transaction sample transaction flags (special events only) --vmlinux vmlinux pathname (not built-in because NO_LIBBPF=1) # ./perf record --vmlinux /tmp/vmlinux ls / Warning: option `vmlinux' is being ignored because NO_LIBBPF=1 ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.011 MB perf.data (11 samples) ] Help messages when build with NO_DWARF: # ./perf record -h --transaction sample transaction flags (special events only) --vmlinux vmlinux pathname (not built-in because NO_DWARF=1) Signed-off-by: He Kuang Cc: Alexei Starovoitov Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Wang Nan Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1450089563-122430-15-git-send-email-wangnan0@huawei.com Signed-off-by: Wang Nan Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 10 ++++++++-- tools/perf/builtin-record.c | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index e630a7d2c348..8d032f4e50bf 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -314,11 +314,17 @@ This option sets the time out limit. The default value is 500 ms. Record context switch events i.e. events of type PERF_RECORD_SWITCH or PERF_RECORD_SWITCH_CPU_WIDE. ---clang-path:: +--clang-path=PATH:: Path to clang binary to use for compiling BPF scriptlets. +(enabled when BPF support is on) ---clang-opt:: +--clang-opt=OPTIONS:: Options passed to clang when compiling BPF scriptlets. +(enabled when BPF support is on) + +--vmlinux=PATH:: +Specify vmlinux path which has debuginfo. +(enabled when BPF prologue is on) SEE ALSO -------- diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index c2ba377e8d3a..3ef3c79e7534 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1117,6 +1117,8 @@ struct option __record_options[] = { "clang binary to use for compiling BPF scriptlets"), OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options", "options passed to clang when compiling BPF scriptlets"), + OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name, + "file", "vmlinux pathname"), OPT_END() }; @@ -1133,6 +1135,20 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) set_nobuild('\0', "clang-path", true); set_nobuild('\0', "clang-opt", true); # undef set_nobuild +#endif + +#ifndef HAVE_BPF_PROLOGUE +# if !defined (HAVE_DWARF_SUPPORT) +# define REASON "NO_DWARF=1" +# elif !defined (HAVE_LIBBPF_SUPPORT) +# define REASON "NO_LIBBPF=1" +# else +# define REASON "this architecture doesn't support BPF prologue" +# endif +# define set_nobuild(s, l, c) set_option_nobuild(record_options, s, l, REASON, c) + set_nobuild('\0', "vmlinux", true); +# undef set_nobuild +# undef REASON #endif rec->evlist = perf_evlist__new(); From 7a29c087ff80f5d534bd6729c852099fc572c8d0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 15 Dec 2015 10:49:56 +0900 Subject: [PATCH 145/271] perf record: Add record.build-id config option Post processing at 'perf record' takes a long time on big machines. What it does is to find the build-id of binaries found in the event stream, so that it can make sure, at 'report' time, that the symtabs (be it ELF, kallsyms, etc) being used to resolve symbols are the ones matching the binaries found at 'record' time. Sometimes we just want to skip this processing of events at the end of the session to get quicker results, making sure the binaries haven't changed from 'record' to 'report' time. Add a new config option to control this behavior. The record.build-id config variable can have one of the following values: - cache: post-process data and save/update the binaries into the build-id cache (in ~/.debug). This is the default. - no-cache: post-process the data but not update the build-id cache. Same effect as using the -N option. - skip: skip post-processing and do not update the cache. Same effect as using the -B option. Reported-and-Acked-by: Peter Zijlstra (Intel) Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Cc: David Ahern Cc: Taeung Song Link: http://lkml.kernel.org/r/1450144196-22957-1-git-send-email-namhyung@kernel.org [ Added some more text to the documentation ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 14 +++++++++++++- tools/perf/builtin-record.c | 13 +++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 8d032f4e50bf..3a1a32f5479f 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -207,11 +207,23 @@ comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0- In per-thread mode with inheritance mode on (default), samples are captured only when the thread executes on the designated CPUs. Default is to monitor all CPUs. +-B:: +--no-buildid:: +Do not save the build ids of binaries in the perf.data files. This skips +post processing after recording, which sometimes makes the final step in +the recording process to take a long time, as it needs to process all +events looking for mmap records. The downside is that it can misresolve +symbols if the workload binaries used when recording get locally rebuilt +or upgraded, because the only key available in this case is the +pathname. You can also set the "record.build-id" config variable to +'skip to have this behaviour permanently. + -N:: --no-buildid-cache:: Do not update the buildid cache. This saves some overhead in situations where the information in the perf.data file (which includes buildids) -is sufficient. +is sufficient. You can also set the "record.build-id" config variable to +'no-cache' to have the same effect. -G name,...:: --cgroup name,...:: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 3ef3c79e7534..a3b4930737c6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -837,6 +837,19 @@ int record_callchain_opt(const struct option *opt, static int perf_record_config(const char *var, const char *value, void *cb) { + struct record *rec = cb; + + if (!strcmp(var, "record.build-id")) { + if (!strcmp(value, "cache")) + rec->no_buildid_cache = false; + else if (!strcmp(value, "no-cache")) + rec->no_buildid_cache = true; + else if (!strcmp(value, "skip")) + rec->no_buildid = true; + else + return -1; + return 0; + } if (!strcmp(var, "record.call-graph")) var = "call-graph.record-mode"; /* fall-through */ From 1925459b4d92d92e62d67ddc763cda650d2aa79c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:32 -0600 Subject: [PATCH 146/271] tools build: Fix feature Makefile issues with 'O=' When building perf binaries outside the source tree with 'make O=', the auto-detected features get re-tested for every build, which is unnecessary and inconsistent with the behavior seen when building directly in the source tree. Another issue is that 'make O= clean' doesn't remove the feature files from the object tree. Fix these problems by looking for the binaries in the $(OUTPUT) directory. Signed-off-by: Josh Poimboeuf Acked-by: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/113bd01530e9761778c60a75a96c65fc59860f68.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 2 +- tools/build/feature/Makefile | 93 ++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index b8c31ece2d96..6c0519de765d 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -7,7 +7,7 @@ endif feature_check = $(eval $(feature_check_code)) define feature_check_code - feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) + feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) endef feature_set = $(eval $(feature_set_code)) diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index cea04ce9f35c..bf8f0352264d 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -1,4 +1,3 @@ - FILES= \ test-all.bin \ test-backtrace.bin \ @@ -38,38 +37,40 @@ FILES= \ test-bpf.bin \ test-get_cpuid.bin +FILES := $(addprefix $(OUTPUT),$(FILES)) + CC := $(CROSS_COMPILE)gcc -MD PKG_CONFIG := $(CROSS_COMPILE)pkg-config all: $(FILES) -__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) - BUILD = $(__BUILD) > $(OUTPUT)$(@:.bin=.make.output) 2>&1 +__BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) + BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 ############################### -test-all.bin: +$(OUTPUT)test-all.bin: $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma -test-hello.bin: +$(OUTPUT)test-hello.bin: $(BUILD) -test-pthread-attr-setaffinity-np.bin: +$(OUTPUT)test-pthread-attr-setaffinity-np.bin: $(BUILD) -D_GNU_SOURCE -lpthread -test-stackprotector-all.bin: +$(OUTPUT)test-stackprotector-all.bin: $(BUILD) -fstack-protector-all -test-fortify-source.bin: +$(OUTPUT)test-fortify-source.bin: $(BUILD) -O2 -D_FORTIFY_SOURCE=2 -test-bionic.bin: +$(OUTPUT)test-bionic.bin: $(BUILD) -test-libelf.bin: +$(OUTPUT)test-libelf.bin: $(BUILD) -lelf -test-glibc.bin: +$(OUTPUT)test-glibc.bin: $(BUILD) DWARFLIBS := -ldw @@ -77,37 +78,37 @@ ifeq ($(findstring -static,${LDFLAGS}),-static) DWARFLIBS += -lelf -lebl -lz -llzma -lbz2 endif -test-dwarf.bin: +$(OUTPUT)test-dwarf.bin: $(BUILD) $(DWARFLIBS) -test-libelf-mmap.bin: +$(OUTPUT)test-libelf-mmap.bin: $(BUILD) -lelf -test-libelf-getphdrnum.bin: +$(OUTPUT)test-libelf-getphdrnum.bin: $(BUILD) -lelf -test-libnuma.bin: +$(OUTPUT)test-libnuma.bin: $(BUILD) -lnuma -test-numa_num_possible_cpus.bin: +$(OUTPUT)test-numa_num_possible_cpus.bin: $(BUILD) -lnuma -test-libunwind.bin: +$(OUTPUT)test-libunwind.bin: $(BUILD) -lelf -test-libunwind-debug-frame.bin: +$(OUTPUT)test-libunwind-debug-frame.bin: $(BUILD) -lelf -test-libaudit.bin: +$(OUTPUT)test-libaudit.bin: $(BUILD) -laudit -test-libslang.bin: +$(OUTPUT)test-libslang.bin: $(BUILD) -I/usr/include/slang -lslang -test-gtk2.bin: +$(OUTPUT)test-gtk2.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -test-gtk2-infobar.bin: +$(OUTPUT)test-gtk2-infobar.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) grep-libs = $(filter -l%,$(1)) @@ -119,63 +120,63 @@ PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) -test-libperl.bin: +$(OUTPUT)test-libperl.bin: $(BUILD) $(FLAGS_PERL_EMBED) -test-libpython.bin: +$(OUTPUT)test-libpython.bin: $(BUILD) -test-libpython-version.bin: +$(OUTPUT)test-libpython-version.bin: $(BUILD) -test-libbfd.bin: +$(OUTPUT)test-libbfd.bin: $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl -test-liberty.bin: - $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty +$(OUTPUT)test-liberty.bin: + $(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -test-liberty-z.bin: - $(CC) $(CFLAGS) -Wall -Werror -o $(OUTPUT)$@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -lz +$(OUTPUT)test-liberty-z.bin: + $(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty -lz -test-cplus-demangle.bin: +$(OUTPUT)test-cplus-demangle.bin: $(BUILD) -liberty -test-backtrace.bin: +$(OUTPUT)test-backtrace.bin: $(BUILD) -test-timerfd.bin: +$(OUTPUT)test-timerfd.bin: $(BUILD) -test-libdw-dwarf-unwind.bin: +$(OUTPUT)test-libdw-dwarf-unwind.bin: $(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind) -test-libbabeltrace.bin: +$(OUTPUT)test-libbabeltrace.bin: $(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace) -test-sync-compare-and-swap.bin: +$(OUTPUT)test-sync-compare-and-swap.bin: $(BUILD) -test-compile-32.bin: - $(CC) -m32 -o $(OUTPUT)$@ test-compile.c +$(OUTPUT)test-compile-32.bin: + $(CC) -m32 -o $@ test-compile.c -test-compile-x32.bin: - $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c +$(OUTPUT)test-compile-x32.bin: + $(CC) -mx32 -o $@ test-compile.c -test-zlib.bin: +$(OUTPUT)test-zlib.bin: $(BUILD) -lz -test-lzma.bin: +$(OUTPUT)test-lzma.bin: $(BUILD) -llzma -test-get_cpuid.bin: +$(OUTPUT)test-get_cpuid.bin: $(BUILD) -test-bpf.bin: +$(OUTPUT)test-bpf.bin: $(BUILD) --include *.d +-include $(OUTPUT)*.d ############################### clean: - rm -f $(FILES) *.d $(FILES:.bin=.make.output) + rm -f $(FILES) $(OUTPUT)*.d $(FILES:.bin=.make.output) From ce99091730c92bf560712baa0696ea5a461b1fe8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:33 -0600 Subject: [PATCH 147/271] 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/include/linux/string.h | 4 ++++ tools/lib/string.c | 27 +++++++++++++++++++++++++++ tools/perf/util/cache.h | 7 ++----- tools/perf/util/path.c | 18 ------------------ 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index 2e2f736c039c..e26223f1f287 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -8,4 +8,8 @@ void *memdup(const void *src, size_t len); int strtobool(const char *s, bool *res); +#ifndef __UCLIBC__ +extern size_t strlcpy(char *dest, const char *src, size_t size); +#endif + #endif /* _LINUX_STRING_H_ */ diff --git a/tools/lib/string.c b/tools/lib/string.c index 065e54f42d8f..bd239bc1d557 100644 --- a/tools/lib/string.c +++ b/tools/lib/string.c @@ -16,6 +16,7 @@ #include #include #include +#include /** * memdup - duplicate region of memory @@ -60,3 +61,29 @@ int strtobool(const char *s, bool *res) } return 0; } + +/** + * strlcpy - Copy a C-string into a sized buffer + * @dest: Where to copy the string to + * @src: Where to copy the string from + * @size: size of destination buffer + * + * Compatible with *BSD: the result is always a valid + * NUL-terminated string that fits in the buffer (unless, + * of course, the buffer size is zero). It does not pad + * out the result like strncpy() does. + * + * 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; +} 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]; From 24a88bdd05d5de32f3a56a4dcc5070c97d4a514f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:34 -0600 Subject: [PATCH 148/271] 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(+) 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, From 096d35585b4fce7d3ee9b8b34314f39f49491ab1 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:35 -0600 Subject: [PATCH 149/271] 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/Build | 5 ++++- tools/perf/perf.c | 6 +++++- 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 ++++++++++++++ 11 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 tools/perf/util/subcmd-config.c create mode 100644 tools/perf/util/subcmd-config.h diff --git a/tools/perf/Build b/tools/perf/Build index 2a41217e9d88..00c4b8c3d8ca 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -36,7 +36,10 @@ paths += -DPERF_MAN_PATH="BUILD_STR($(mandir_SQ))" CFLAGS_builtin-help.o += $(paths) CFLAGS_builtin-timechart.o += $(paths) -CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" -include $(OUTPUT)PERF-VERSION-FILE +CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \ + -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" \ + -DPREFIX="BUILD_STR($(prefix_SQ))" \ + -include $(OUTPUT)PERF-VERSION-FILE CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))" libperf-y += util/ diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 59ea48c7e26c..783a3310a9d8 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -119,7 +119,7 @@ static void commit_pager_choice(void) { switch (use_pager) { case 0: - setenv("PERF_PAGER", "cat", 1); + setenv(PERF_PAGER_ENVIRONMENT, "cat", 1); break; case 1: /* setup_pager(); */ @@ -530,6 +530,10 @@ int main(int argc, const char **argv) const char *cmd; char sbuf[STRERR_BUFSIZE]; + /* libsubcmd init */ + exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT); + pager_init(PERF_PAGER_ENVIRONMENT); + /* The page_size is placed in util object. */ page_size = sysconf(_SC_PAGE_SIZE); cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); 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 */ From 901421a5bdf605d24c278825cdd032cd6038bcb8 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:36 -0600 Subject: [PATCH 150/271] 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 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 */ From 46113a54be53aea50a4f5926b87e86e2e66c4266 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:37 -0600 Subject: [PATCH 151/271] 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/builtin-help.c | 2 +- tools/perf/builtin-script.c | 8 ++++---- tools/perf/perf.c | 6 +++--- tools/perf/tests/attr.c | 2 +- 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 ++-- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index a7d588bf3cdd..275aa641c31c 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -407,7 +407,7 @@ static int get_html_page_path(struct strbuf *page_path, const char *page) #ifndef open_html static void open_html(const char *path) { - execl_perf_cmd("web--browse", "-c", "help.browser", path, NULL); + execl_cmd("web--browse", "-c", "help.browser", path, NULL); } #endif diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index d259e9aa3a71..571016f16c5a 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1408,7 +1408,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused, char first_half[BUFSIZ]; char *script_root; - snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); + snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path()); scripts_dir = opendir(scripts_path); if (!scripts_dir) @@ -1529,7 +1529,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array) if (!session) return -1; - snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); + snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path()); scripts_dir = opendir(scripts_path); if (!scripts_dir) { @@ -1587,7 +1587,7 @@ static char *get_script_path(const char *script_root, const char *suffix) char lang_path[MAXPATHLEN]; char *__script_root; - snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); + snprintf(scripts_path, MAXPATHLEN, "%s/scripts", get_argv_exec_path()); scripts_dir = opendir(scripts_path); if (!scripts_dir) @@ -1823,7 +1823,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) scripting_max_stack = itrace_synth_opts.callchain_sz; /* make sure PERF_EXEC_PATH is set for scripts */ - perf_set_argv_exec_path(perf_exec_path()); + set_argv_exec_path(get_argv_exec_path()); if (argc && !script_name && !rec_script_path && !rep_script_path) { int live_pipe[2]; diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 783a3310a9d8..6894325fe921 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -183,9 +183,9 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) if (!prefixcmp(cmd, CMD_EXEC_PATH)) { cmd += strlen(CMD_EXEC_PATH); if (*cmd == '=') - perf_set_argv_exec_path(cmd + 1); + set_argv_exec_path(cmd + 1); else { - puts(perf_exec_path()); + puts(get_argv_exec_path()); exit(0); } } else if (!strcmp(cmd, "--html-path")) { @@ -538,7 +538,7 @@ int main(int argc, const char **argv) page_size = sysconf(_SC_PAGE_SIZE); cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); - cmd = perf_extract_argv0_path(argv[0]); + cmd = extract_argv0_path(argv[0]); if (!cmd) cmd = "perf-help"; diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index b66730eb94e3..6337f1c07f02 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -164,7 +164,7 @@ int test__attr(int subtest __maybe_unused) return run_dir("./tests", "./perf"); /* Then installed path. */ - snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path()); + snprintf(path_dir, PATH_MAX, "%s/tests", get_argv_exec_path()); snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR); if (!lstat(path_dir, &st) && 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); From 2f4ce5ec1d447beb42143a9653716a2ab025161e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:38 -0600 Subject: [PATCH 152/271] 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(-) 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 +#include +#include +#include +#include +#include +#include +#include +#include "subcmd-util.h" #include "exec_cmd.h" -#include "quote.h" #include "subcmd-config.h" -#include -#include "subcmd-util.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 From 4b6ab94eabe4f55371cff4569750bb3996c55db6 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:39 -0600 Subject: [PATCH 153/271] 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/lib/subcmd/Build | 7 +++ tools/lib/subcmd/Makefile | 48 +++++++++++++++++++ .../util/exec_cmd.c => lib/subcmd/exec-cmd.c} | 2 +- .../util/exec_cmd.h => lib/subcmd/exec-cmd.h} | 0 tools/{perf/util => lib/subcmd}/help.c | 2 +- tools/{perf/util => lib/subcmd}/help.h | 0 tools/{perf/util => lib/subcmd}/pager.c | 0 tools/{perf/util => lib/subcmd}/pager.h | 0 .../{perf/util => lib/subcmd}/parse-options.c | 0 .../{perf/util => lib/subcmd}/parse-options.h | 0 tools/{perf/util => lib/subcmd}/run-command.c | 2 +- tools/{perf/util => lib/subcmd}/run-command.h | 0 tools/{perf/util => lib/subcmd}/sigchain.c | 0 tools/{perf/util => lib/subcmd}/sigchain.h | 0 .../{perf/util => lib/subcmd}/subcmd-config.c | 0 .../{perf/util => lib/subcmd}/subcmd-config.h | 0 tools/{perf/util => lib/subcmd}/subcmd-util.h | 0 tools/perf/MANIFEST | 1 + tools/perf/Makefile.perf | 20 ++++++-- tools/perf/arch/x86/util/intel-pt.c | 2 +- tools/perf/bench/futex-hash.c | 2 +- tools/perf/bench/futex-lock-pi.c | 2 +- tools/perf/bench/futex-requeue.c | 2 +- tools/perf/bench/futex-wake-parallel.c | 2 +- tools/perf/bench/futex-wake.c | 2 +- tools/perf/bench/mem-functions.c | 2 +- tools/perf/bench/numa.c | 2 +- tools/perf/bench/sched-messaging.c | 2 +- tools/perf/bench/sched-pipe.c | 2 +- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-bench.c | 2 +- tools/perf/builtin-buildid-cache.c | 2 +- tools/perf/builtin-buildid-list.c | 2 +- tools/perf/builtin-config.c | 2 +- tools/perf/builtin-data.c | 2 +- tools/perf/builtin-evlist.c | 2 +- tools/perf/builtin-help.c | 8 ++-- tools/perf/builtin-inject.c | 2 +- tools/perf/builtin-kmem.c | 2 +- tools/perf/builtin-kvm.c | 2 +- tools/perf/builtin-list.c | 2 +- tools/perf/builtin-lock.c | 2 +- tools/perf/builtin-mem.c | 2 +- tools/perf/builtin-probe.c | 2 +- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-sched.c | 2 +- tools/perf/builtin-script.c | 4 +- tools/perf/builtin-stat.c | 2 +- tools/perf/builtin-timechart.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 4 +- tools/perf/perf.c | 6 +-- tools/perf/tests/attr.c | 2 +- tools/perf/tests/builtin-test.c | 2 +- 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/help-unknown-cmd.c | 2 +- tools/perf/util/parse-branch-options.c | 2 +- tools/perf/util/parse-events.c | 4 +- tools/perf/util/parse-regs-options.c | 2 +- tools/perf/util/sort.h | 2 +- 66 files changed, 129 insertions(+), 68 deletions(-) create mode 100644 tools/lib/subcmd/Build create mode 100644 tools/lib/subcmd/Makefile rename tools/{perf/util/exec_cmd.c => lib/subcmd/exec-cmd.c} (99%) rename tools/{perf/util/exec_cmd.h => lib/subcmd/exec-cmd.h} (100%) rename tools/{perf/util => lib/subcmd}/help.c (99%) rename tools/{perf/util => lib/subcmd}/help.h (100%) rename tools/{perf/util => lib/subcmd}/pager.c (100%) rename tools/{perf/util => lib/subcmd}/pager.h (100%) rename tools/{perf/util => lib/subcmd}/parse-options.c (100%) rename tools/{perf/util => lib/subcmd}/parse-options.h (100%) rename tools/{perf/util => lib/subcmd}/run-command.c (99%) rename tools/{perf/util => lib/subcmd}/run-command.h (100%) rename tools/{perf/util => lib/subcmd}/sigchain.c (100%) rename tools/{perf/util => lib/subcmd}/sigchain.h (100%) rename tools/{perf/util => lib/subcmd}/subcmd-config.c (100%) rename tools/{perf/util => lib/subcmd}/subcmd-config.h (100%) rename tools/{perf/util => lib/subcmd}/subcmd-util.h (100%) diff --git a/tools/lib/subcmd/Build b/tools/lib/subcmd/Build new file mode 100644 index 000000000000..ee31288788c1 --- /dev/null +++ b/tools/lib/subcmd/Build @@ -0,0 +1,7 @@ +libsubcmd-y += exec-cmd.o +libsubcmd-y += help.o +libsubcmd-y += pager.o +libsubcmd-y += parse-options.o +libsubcmd-y += run-command.o +libsubcmd-y += sigchain.o +libsubcmd-y += subcmd-config.o diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile new file mode 100644 index 000000000000..629cf8c14e68 --- /dev/null +++ b/tools/lib/subcmd/Makefile @@ -0,0 +1,48 @@ +include ../../scripts/Makefile.include +include ../../perf/config/utilities.mak # QUIET_CLEAN + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(shell pwd))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar +RM = rm -f + +MAKEFLAGS += --no-print-directory + +LIBFILE = $(OUTPUT)libsubcmd.a + +CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) +CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC +CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE + +CFLAGS += -I$(srctree)/tools/include/ +CFLAGS += -I$(srctree)/include/uapi +CFLAGS += -I$(srctree)/include + +SUBCMD_IN := $(OUTPUT)libsubcmd-in.o + +all: + +export srctree OUTPUT CC LD CFLAGS V +include $(srctree)/tools/build/Makefile.include + +all: fixdep $(LIBFILE) + +$(SUBCMD_IN): FORCE + @$(MAKE) $(build)=libsubcmd + +$(LIBFILE): $(SUBCMD_IN) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(SUBCMD_IN) + +clean: + $(call QUIET_CLEAN, libsubcmd) $(RM) $(LIBFILE); \ + find $(if $(OUTPUT),$(OUTPUT),.) -name \*.o -or -name \*.o.cmd -or -name \*.o.d | xargs $(RM) + +FORCE: + +.PHONY: clean FORCE diff --git a/tools/perf/util/exec_cmd.c b/tools/lib/subcmd/exec-cmd.c similarity index 99% rename from tools/perf/util/exec_cmd.c rename to tools/lib/subcmd/exec-cmd.c index e7f9ed7943e3..1ae833af1a4a 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/lib/subcmd/exec-cmd.c @@ -7,7 +7,7 @@ #include #include #include "subcmd-util.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #include "subcmd-config.h" #define MAX_ARGS 32 diff --git a/tools/perf/util/exec_cmd.h b/tools/lib/subcmd/exec-cmd.h similarity index 100% rename from tools/perf/util/exec_cmd.h rename to tools/lib/subcmd/exec-cmd.h diff --git a/tools/perf/util/help.c b/tools/lib/subcmd/help.c similarity index 99% rename from tools/perf/util/help.c rename to tools/lib/subcmd/help.c index 8169480066c6..e228c3cb3716 100644 --- a/tools/perf/util/help.c +++ b/tools/lib/subcmd/help.c @@ -9,7 +9,7 @@ #include #include "subcmd-util.h" #include "help.h" -#include "exec_cmd.h" +#include "exec-cmd.h" void add_cmdname(struct cmdnames *cmds, const char *name, size_t len) { diff --git a/tools/perf/util/help.h b/tools/lib/subcmd/help.h similarity index 100% rename from tools/perf/util/help.h rename to tools/lib/subcmd/help.h diff --git a/tools/perf/util/pager.c b/tools/lib/subcmd/pager.c similarity index 100% rename from tools/perf/util/pager.c rename to tools/lib/subcmd/pager.c diff --git a/tools/perf/util/pager.h b/tools/lib/subcmd/pager.h similarity index 100% rename from tools/perf/util/pager.h rename to tools/lib/subcmd/pager.h diff --git a/tools/perf/util/parse-options.c b/tools/lib/subcmd/parse-options.c similarity index 100% rename from tools/perf/util/parse-options.c rename to tools/lib/subcmd/parse-options.c diff --git a/tools/perf/util/parse-options.h b/tools/lib/subcmd/parse-options.h similarity index 100% rename from tools/perf/util/parse-options.h rename to tools/lib/subcmd/parse-options.h diff --git a/tools/perf/util/run-command.c b/tools/lib/subcmd/run-command.c similarity index 99% rename from tools/perf/util/run-command.c rename to tools/lib/subcmd/run-command.c index fed37d6ae070..f4f6c9eb8e59 100644 --- a/tools/perf/util/run-command.c +++ b/tools/lib/subcmd/run-command.c @@ -7,7 +7,7 @@ #include #include "subcmd-util.h" #include "run-command.h" -#include "exec_cmd.h" +#include "exec-cmd.h" #define STRERR_BUFSIZE 128 diff --git a/tools/perf/util/run-command.h b/tools/lib/subcmd/run-command.h similarity index 100% rename from tools/perf/util/run-command.h rename to tools/lib/subcmd/run-command.h diff --git a/tools/perf/util/sigchain.c b/tools/lib/subcmd/sigchain.c similarity index 100% rename from tools/perf/util/sigchain.c rename to tools/lib/subcmd/sigchain.c diff --git a/tools/perf/util/sigchain.h b/tools/lib/subcmd/sigchain.h similarity index 100% rename from tools/perf/util/sigchain.h rename to tools/lib/subcmd/sigchain.h diff --git a/tools/perf/util/subcmd-config.c b/tools/lib/subcmd/subcmd-config.c similarity index 100% rename from tools/perf/util/subcmd-config.c rename to tools/lib/subcmd/subcmd-config.c diff --git a/tools/perf/util/subcmd-config.h b/tools/lib/subcmd/subcmd-config.h similarity index 100% rename from tools/perf/util/subcmd-config.h rename to tools/lib/subcmd/subcmd-config.h diff --git a/tools/perf/util/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h similarity index 100% rename from tools/perf/util/subcmd-util.h rename to tools/lib/subcmd/subcmd-util.h diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 2562eac6451d..ce3932ee4893 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -20,6 +20,7 @@ tools/lib/traceevent tools/lib/bpf tools/lib/api tools/lib/bpf +tools/lib/subcmd tools/lib/hweight.c tools/lib/rbtree.c tools/lib/string.c diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 388ec64fa39b..569fcf022531 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -145,9 +145,10 @@ BISON = bison STRIP = strip AWK = awk -LIB_DIR = $(srctree)/tools/lib/api/ +LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ -BPF_DIR = $(srctree)/tools/lib/bpf/ +BPF_DIR = $(srctree)/tools/lib/bpf/ +SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ # include config/Makefile by default and rule out # non-config cases @@ -184,6 +185,7 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) BPF_PATH=$(OUTPUT) + SUBCMD_PATH=$(OUTPUT) ifneq ($(subdir),) API_PATH=$(OUTPUT)/../lib/api/ else @@ -193,6 +195,7 @@ else TE_PATH=$(TRACE_EVENT_DIR) API_PATH=$(LIB_DIR) BPF_PATH=$(BPF_DIR) + SUBCMD_PATH=$(SUBCMD_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a @@ -206,6 +209,8 @@ export LIBAPI LIBBPF = $(BPF_PATH)libbpf.a +LIBSUBCMD = $(SUBCMD_PATH)libsubcmd.a + # python extension build directories PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ @@ -257,7 +262,7 @@ export PERL_PATH LIB_FILE=$(OUTPUT)libperf.a -PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) +PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT) $(LIBSUBCMD) ifndef NO_LIBBPF PERFLIBS += $(LIBBPF) endif @@ -437,6 +442,13 @@ $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null +$(LIBSUBCMD): fixdep FORCE + $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) $(OUTPUT)libsubcmd.a + +$(LIBSUBCMD)-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(MAKE) -C $(SUBCMD_DIR) O=$(OUTPUT) clean + help: @echo 'Perf make targets:' @echo ' doc - make *all* documentation (see below)' @@ -584,7 +596,7 @@ config-clean: $(call QUIET_CLEAN, config) $(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null -clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean +clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete $(Q)$(RM) $(OUTPUT).config-detected diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index b02af064f0f9..b64d46285ebb 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -26,7 +26,7 @@ #include "../../util/evlist.h" #include "../../util/evsel.h" #include "../../util/cpumap.h" -#include "../../util/parse-options.h" +#include #include "../../util/parse-events.h" #include "../../util/pmu.h" #include "../../util/debug.h" diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index fc9bebd2cca0..0999ac536d86 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-lock-pi.c b/tools/perf/bench/futex-lock-pi.c index bc6a16adbca8..6a18ce21f865 100644 --- a/tools/perf/bench/futex-lock-pi.c +++ b/tools/perf/bench/futex-lock-pi.c @@ -5,7 +5,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index ad0d9b5342fb..718238683013 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-wake-parallel.c b/tools/perf/bench/futex-wake-parallel.c index 6d8c9fa2a16c..91aaf2a1fa90 100644 --- a/tools/perf/bench/futex-wake-parallel.c +++ b/tools/perf/bench/futex-wake-parallel.c @@ -10,7 +10,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index e5e41d3bdce7..f416bd705f66 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" #include "../util/stat.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "bench.h" #include "futex.h" diff --git a/tools/perf/bench/mem-functions.c b/tools/perf/bench/mem-functions.c index 9419b944220f..a91aa85d80ff 100644 --- a/tools/perf/bench/mem-functions.c +++ b/tools/perf/bench/mem-functions.c @@ -8,7 +8,7 @@ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../util/header.h" #include "../util/cloexec.h" #include "bench.h" diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 492df2752a2d..5049d6357a46 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c @@ -7,7 +7,7 @@ #include "../perf.h" #include "../builtin.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../util/cloexec.h" #include "bench.h" diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index d4ff1b539cfd..bfaf9503de8e 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c @@ -11,7 +11,7 @@ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../builtin.h" #include "bench.h" diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c index 005cc283790c..1dc2d13cc272 100644 --- a/tools/perf/bench/sched-pipe.c +++ b/tools/perf/bench/sched-pipe.c @@ -10,7 +10,7 @@ */ #include "../perf.h" #include "../util/util.h" -#include "../util/parse-options.h" +#include #include "../builtin.h" #include "bench.h" diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1f00dc7cecba..e18f1b995ffd 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -21,7 +21,7 @@ #include "util/evsel.h" #include "util/annotate.h" #include "util/event.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/thread.h" #include "util/sort.h" diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index b17aed36ca16..a1cddc6bbf0f 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -16,7 +16,7 @@ */ #include "perf.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "builtin.h" #include "bench/bench.h" diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 7b8450cd33c2..d93bff7fc0e4 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -16,7 +16,7 @@ #include "util/cache.h" #include "util/debug.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/strlist.h" #include "util/build-id.h" #include "util/session.h" diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 6419f57b0850..5e914ee79eb3 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -12,7 +12,7 @@ #include "util/build-id.h" #include "util/cache.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/session.h" #include "util/symbol.h" #include "util/data.h" diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c index 427ea7a705b8..f04e804a9fad 100644 --- a/tools/perf/builtin-config.c +++ b/tools/perf/builtin-config.c @@ -9,7 +9,7 @@ #include "perf.h" #include "util/cache.h" -#include "util/parse-options.h" +#include #include "util/util.h" #include "util/debug.h" diff --git a/tools/perf/builtin-data.c b/tools/perf/builtin-data.c index d6525bc54d13..b97bc1518b44 100644 --- a/tools/perf/builtin-data.c +++ b/tools/perf/builtin-data.c @@ -2,7 +2,7 @@ #include "builtin.h" #include "perf.h" #include "debug.h" -#include "parse-options.h" +#include #include "data-convert-bt.h" typedef int (*data_cmd_fn_t)(int argc, const char **argv, const char *prefix); diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index f4d62510acbb..08a7d36a2cf8 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -12,7 +12,7 @@ #include "util/evlist.h" #include "util/evsel.h" #include "util/parse-events.h" -#include "util/parse-options.h" +#include #include "util/session.h" #include "util/data.h" #include "util/debug.h" diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 275aa641c31c..96c1a4cfbbbf 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -6,11 +6,11 @@ #include "perf.h" #include "util/cache.h" #include "builtin.h" -#include "util/exec_cmd.h" +#include #include "common-cmds.h" -#include "util/parse-options.h" -#include "util/run-command.h" -#include "util/help.h" +#include +#include +#include #include "util/debug.h" static struct man_viewer_list { diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 99d127fe9c35..0022e02ed31a 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -18,7 +18,7 @@ #include "util/data.h" #include "util/auxtrace.h" -#include "util/parse-options.h" +#include #include diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 93ce665f976f..118010553d0c 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -12,7 +12,7 @@ #include "util/tool.h" #include "util/callchain.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/data.h" #include "util/cpumap.h" diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 031f9f55c281..4418d9214872 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -10,7 +10,7 @@ #include "util/header.h" #include "util/session.h" #include "util/intlist.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" #include "util/tool.h" diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index bf679e2c978b..5e22db4684b8 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -14,7 +14,7 @@ #include "util/parse-events.h" #include "util/cache.h" #include "util/pmu.h" -#include "util/parse-options.h" +#include int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) { diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index de16aaed516e..ce3bfb48b26f 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -9,7 +9,7 @@ #include "util/thread.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 80170aace5d4..390170041696 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -1,7 +1,7 @@ #include "builtin.h" #include "perf.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/tool.h" #include "util/session.h" diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index dbe2ea5a2932..9af859b28b15 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@ #include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" -#include "util/parse-options.h" +#include #include "util/probe-finder.h" #include "util/probe-event.h" #include "util/probe-file.h" diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a3b4930737c6..1435ef6265b6 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -11,7 +11,7 @@ #include "util/build-id.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/callchain.h" diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5a454669d075..2a7330b99b82 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -27,7 +27,7 @@ #include "util/session.h" #include "util/tool.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/thread.h" diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index e3d3e32c0a93..871b55ae22a4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -12,7 +12,7 @@ #include "util/tool.h" #include "util/cloexec.h" -#include "util/parse-options.h" +#include #include "util/trace-event.h" #include "util/debug.h" diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 571016f16c5a..bcc3542d9df5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3,9 +3,9 @@ #include "perf.h" #include "util/cache.h" #include "util/debug.h" -#include "util/exec_cmd.h" +#include #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/perf_regs.h" #include "util/session.h" #include "util/tool.h" diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 25a95f49c36e..bbf42eefd5e5 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -45,7 +45,7 @@ #include "builtin.h" #include "util/cgroup.h" #include "util/util.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/pmu.h" #include "util/event.h" diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 30e59620179d..bd7a7757176f 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -30,7 +30,7 @@ #include "perf.h" #include "util/header.h" -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/event.h" #include "util/session.h" diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 92fe963e43c4..9ebd67a42ede 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -34,7 +34,7 @@ #include "util/top.h" #include "util/util.h" #include -#include "util/parse-options.h" +#include #include "util/parse-events.h" #include "util/cpumap.h" #include "util/xyarray.h" diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c783d8fd3a80..20916dd77aac 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -22,11 +22,11 @@ #include "util/color.h" #include "util/debug.h" #include "util/evlist.h" -#include "util/exec_cmd.h" +#include #include "util/machine.h" #include "util/session.h" #include "util/thread.h" -#include "util/parse-options.h" +#include #include "util/strlist.h" #include "util/intlist.h" #include "util/thread_map.h" diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6894325fe921..cb1d2499c45c 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -9,12 +9,12 @@ #include "builtin.h" #include "util/env.h" -#include "util/exec_cmd.h" +#include #include "util/cache.h" #include "util/quote.h" -#include "util/run-command.h" +#include #include "util/parse-events.h" -#include "util/parse-options.h" +#include #include "util/bpf-loader.h" #include "util/debug.h" #include diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 6337f1c07f02..28d1605b0338 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c @@ -24,7 +24,7 @@ #include #include "../perf.h" #include "util.h" -#include "exec_cmd.h" +#include #include "tests.h" #define ENV "PERF_TEST_ATTR" diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index fa98406c92e2..0372d5945910 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -11,7 +11,7 @@ #include "tests.h" #include "debug.h" #include "color.h" -#include "parse-options.h" +#include #include "symbol.h" struct test __weak arch_tests[] = { 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/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/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-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/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" From 1843b4e057b7717db21a3ad96fa16d6b4ee8f6c4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Tue, 15 Dec 2015 09:39:40 -0600 Subject: [PATCH 154/271] tools subcmd: Rename subcmd header include guards Signed-off-by: Josh Poimboeuf Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/d8081e7528b25ad91f4154b6a3fd063e93c108ec.1450193761.git.jpoimboe@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/subcmd/exec-cmd.h | 6 +++--- tools/lib/subcmd/help.h | 6 +++--- tools/lib/subcmd/pager.h | 6 +++--- tools/lib/subcmd/parse-options.h | 7 ++++--- tools/lib/subcmd/run-command.h | 6 +++--- tools/lib/subcmd/sigchain.h | 6 +++--- tools/lib/subcmd/subcmd-util.h | 6 +++--- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/tools/lib/subcmd/exec-cmd.h b/tools/lib/subcmd/exec-cmd.h index f1bd3436ad5f..5d08bda31d90 100644 --- a/tools/lib/subcmd/exec-cmd.h +++ b/tools/lib/subcmd/exec-cmd.h @@ -1,5 +1,5 @@ -#ifndef __PERF_EXEC_CMD_H -#define __PERF_EXEC_CMD_H +#ifndef __SUBCMD_EXEC_CMD_H +#define __SUBCMD_EXEC_CMD_H extern void exec_cmd_init(const char *exec_name, const char *prefix, const char *exec_path, const char *exec_path_env); @@ -13,4 +13,4 @@ extern int execl_cmd(const char *cmd, ...); extern char *get_argv_exec_path(void); extern char *system_path(const char *path); -#endif /* __PERF_EXEC_CMD_H */ +#endif /* __SUBCMD_EXEC_CMD_H */ diff --git a/tools/lib/subcmd/help.h b/tools/lib/subcmd/help.h index 096c8bc45cd7..e145a020780c 100644 --- a/tools/lib/subcmd/help.h +++ b/tools/lib/subcmd/help.h @@ -1,5 +1,5 @@ -#ifndef __PERF_HELP_H -#define __PERF_HELP_H +#ifndef __SUBCMD_HELP_H +#define __SUBCMD_HELP_H #include @@ -31,4 +31,4 @@ 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 */ +#endif /* __SUBCMD_HELP_H */ diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h index d6a591a4c017..8b83714ecf73 100644 --- a/tools/lib/subcmd/pager.h +++ b/tools/lib/subcmd/pager.h @@ -1,9 +1,9 @@ -#ifndef __PERF_PAGER_H -#define __PERF_PAGER_H +#ifndef __SUBCMD_PAGER_H +#define __SUBCMD_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 */ +#endif /* __SUBCMD_PAGER_H */ diff --git a/tools/lib/subcmd/parse-options.h b/tools/lib/subcmd/parse-options.h index dec893f10477..13a2cc1d6140 100644 --- a/tools/lib/subcmd/parse-options.h +++ b/tools/lib/subcmd/parse-options.h @@ -1,5 +1,5 @@ -#ifndef __PERF_PARSE_OPTIONS_H -#define __PERF_PARSE_OPTIONS_H +#ifndef __SUBCMD_PARSE_OPTIONS_H +#define __SUBCMD_PARSE_OPTIONS_H #include #include @@ -225,4 +225,5 @@ extern const char *parse_options_fix_filename(const char *prefix, const char *fi 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 */ + +#endif /* __SUBCMD_PARSE_OPTIONS_H */ diff --git a/tools/lib/subcmd/run-command.h b/tools/lib/subcmd/run-command.h index 4a55393a6547..fe2befea1e73 100644 --- a/tools/lib/subcmd/run-command.h +++ b/tools/lib/subcmd/run-command.h @@ -1,5 +1,5 @@ -#ifndef __PERF_RUN_COMMAND_H -#define __PERF_RUN_COMMAND_H +#ifndef __SUBCMD_RUN_COMMAND_H +#define __SUBCMD_RUN_COMMAND_H #include @@ -57,4 +57,4 @@ int run_command(struct child_process *); #define RUN_COMMAND_STDOUT_TO_STDERR 4 int run_command_v_opt(const char **argv, int opt); -#endif /* __PERF_RUN_COMMAND_H */ +#endif /* __SUBCMD_RUN_COMMAND_H */ diff --git a/tools/lib/subcmd/sigchain.h b/tools/lib/subcmd/sigchain.h index 959d64eb5557..0c919f2874ca 100644 --- a/tools/lib/subcmd/sigchain.h +++ b/tools/lib/subcmd/sigchain.h @@ -1,5 +1,5 @@ -#ifndef __PERF_SIGCHAIN_H -#define __PERF_SIGCHAIN_H +#ifndef __SUBCMD_SIGCHAIN_H +#define __SUBCMD_SIGCHAIN_H typedef void (*sigchain_fun)(int); @@ -7,4 +7,4 @@ int sigchain_pop(int sig); void sigchain_push_common(sigchain_fun f); -#endif /* __PERF_SIGCHAIN_H */ +#endif /* __SUBCMD_SIGCHAIN_H */ diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h index 321aeb11a381..fc2e45d8aaf1 100644 --- a/tools/lib/subcmd/subcmd-util.h +++ b/tools/lib/subcmd/subcmd-util.h @@ -1,5 +1,5 @@ -#ifndef __PERF_SUBCMD_UTIL_H -#define __PERF_SUBCMD_UTIL_H +#ifndef __SUBCMD_UTIL_H +#define __SUBCMD_UTIL_H #include #include @@ -88,4 +88,4 @@ static inline int prefixcmp(const char *str, const char *prefix) return (unsigned char)*prefix - (unsigned char)*str; } -#endif /* __PERF_SUBCMD_UTIL_H */ +#endif /* __SUBCMD_UTIL_H */ From 5f3339d2e83ca587c2e13c3e37e1b5fb7c68ebe5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:19 +0100 Subject: [PATCH 155/271] 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(-) 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; From 99471c967a00c875bb5d61f377d4267904545499 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:20 +0100 Subject: [PATCH 156/271] 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/tests/builtin-test.c | 4 ++++ tools/perf/tests/tests.h | 1 + tools/perf/tests/thread-map.c | 29 ++++++++++++++++++++++++++ tools/perf/util/event.c | 36 +++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 4 ++++ 5 files changed, 74 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0372d5945910..745bdb02d22b 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -179,6 +179,10 @@ static struct test generic_tests[] = { .get_desc = test__bpf_subtest_get_desc, }, }, + { + .desc = "Test thread map synthesize", + .func = test__thread_map_synthesize, + }, { .func = NULL, }, diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a0733aaad081..3fe52ccc4d05 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -79,6 +79,7 @@ int test__bpf(int subtest); const char *test__bpf_subtest_get_desc(int subtest); int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); +int test__thread_map_synthesize(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 2be02d303e82..ac5be2578367 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -40,3 +40,32 @@ int test__thread_map(int subtest __maybe_unused) thread_map__put(map); return 0; } + +static int process_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct thread_map_event *map = &event->thread_map; + + TEST_ASSERT_VAL("wrong nr", map->nr == 1); + TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid()); + TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf")); + return 0; +} + +int test__thread_map_synthesize(int subtest __maybe_unused) +{ + struct thread_map *threads; + + /* test map on current pid */ + threads = thread_map__new_by_pid(getpid()); + TEST_ASSERT_VAL("failed to alloc map", threads); + + thread_map__read_comms(threads); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_thread_map2(NULL, threads, process_event, NULL)); + + return 0; +} 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, From 59660942397b57b37eccba014544623cf4beb12b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:21 +0100 Subject: [PATCH 157/271] 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/tests/thread-map.c | 14 ++++++++++++++ tools/perf/util/thread_map.c | 27 +++++++++++++++++++++++++++ tools/perf/util/thread_map.h | 3 +++ 3 files changed, 44 insertions(+) diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index ac5be2578367..fccde848fe9c 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -47,10 +47,24 @@ static int process_event(struct perf_tool *tool __maybe_unused, struct machine *machine __maybe_unused) { struct thread_map_event *map = &event->thread_map; + struct thread_map *threads; TEST_ASSERT_VAL("wrong nr", map->nr == 1); TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid()); TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf")); + + threads = thread_map__new_event(&event->thread_map); + TEST_ASSERT_VAL("failed to alloc map", threads); + + TEST_ASSERT_VAL("wrong nr", threads->nr == 1); + TEST_ASSERT_VAL("wrong pid", + thread_map__pid(threads, 0) == getpid()); + TEST_ASSERT_VAL("wrong comm", + thread_map__comm(threads, 0) && + !strcmp(thread_map__comm(threads, 0), "perf")); + TEST_ASSERT_VAL("wrong refcnt", + atomic_read(&threads->refcnt) == 1); + thread_map__put(threads); return 0; } 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); From ec7fa596f514b76a5f1003ffe9e6dfb50cb9e811 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:22 +0100 Subject: [PATCH 158/271] 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(+) 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, From 6640b6c227fc85fd8bdcc4a31239a04450487f6a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:23 +0100 Subject: [PATCH 159/271] 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(-) 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; From 6c872901af07c41745f1abf5ceac9b3b4d9cdbb6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:24 +0100 Subject: [PATCH 160/271] 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/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 + tools/perf/tests/cpumap.c | 71 +++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 131 ++++++++++++++++++++++++++++++++ tools/perf/util/event.h | 8 ++ 6 files changed, 216 insertions(+) create mode 100644 tools/perf/tests/cpumap.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index f23fb7ed4400..7abad28fe17e 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -34,6 +34,7 @@ perf-y += thread-map.o perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o +perf-y += cpumap.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 745bdb02d22b..0c3fe2846de8 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -183,6 +183,10 @@ static struct test generic_tests[] = { .desc = "Test thread map synthesize", .func = test__thread_map_synthesize, }, + { + .desc = "Test cpu map synthesize", + .func = test__cpu_map_synthesize, + }, { .func = NULL, }, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c new file mode 100644 index 000000000000..715480558088 --- /dev/null +++ b/tools/perf/tests/cpumap.c @@ -0,0 +1,71 @@ +#include "tests.h" +#include "cpumap.h" + +static int process_event_mask(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_mask *mask; + struct cpu_map_data *data; + int i; + + data = &map->data; + + TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK); + + mask = (struct cpu_map_mask *)data->data; + + TEST_ASSERT_VAL("wrong nr", mask->nr == 1); + + for (i = 0; i < 20; i++) { + TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask)); + } + + return 0; +} + +static int process_event_cpus(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_entries *cpus; + struct cpu_map_data *data; + + data = &map->data; + + TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS); + + cpus = (struct cpu_map_entries *)data->data; + + TEST_ASSERT_VAL("wrong nr", cpus->nr == 2); + TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1); + TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256); + return 0; +} + + +int test__cpu_map_synthesize(int subtest __maybe_unused) +{ + struct cpu_map *cpus; + + /* This one is better stores in mask. */ + cpus = cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19"); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL)); + + cpu_map__put(cpus); + + /* This one is better stores in cpu values. */ + cpus = cpu_map__new("1,256"); + + TEST_ASSERT_VAL("failed to synthesize map", + !perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL)); + + cpu_map__put(cpus); + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 3fe52ccc4d05..f85160f6ebb8 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -80,6 +80,7 @@ const char *test__bpf_subtest_get_desc(int subtest); int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); +int test__cpu_map_synthesize(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT 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 */ From f77b57ad4fc42a074eae564bbb6660f0a3ff5503 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:25 +0100 Subject: [PATCH 161/271] 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/tests/cpumap.c | 25 +++++++++++++++++++---- tools/perf/util/cpumap.c | 42 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 1 + 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index 715480558088..4cb6418a8ffc 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -6,12 +6,13 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_event *map_event = &event->cpu_map; struct cpu_map_mask *mask; struct cpu_map_data *data; + struct cpu_map *map; int i; - data = &map->data; + data = &map_event->data; TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK); @@ -23,6 +24,14 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask)); } + map = cpu_map__new_data(data); + TEST_ASSERT_VAL("wrong nr", map->nr == 20); + + for (i = 0; i < 20; i++) { + TEST_ASSERT_VAL("wrong cpu", map->map[i] == i); + } + + cpu_map__put(map); return 0; } @@ -31,11 +40,12 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct cpu_map_event *map = &event->cpu_map; + struct cpu_map_event *map_event = &event->cpu_map; struct cpu_map_entries *cpus; struct cpu_map_data *data; + struct cpu_map *map; - data = &map->data; + data = &map_event->data; TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS); @@ -44,6 +54,13 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, TEST_ASSERT_VAL("wrong nr", cpus->nr == 2); TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1); TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256); + + map = cpu_map__new_data(data); + TEST_ASSERT_VAL("wrong nr", map->nr == 2); + TEST_ASSERT_VAL("wrong cpu", map->map[0] == 1); + TEST_ASSERT_VAL("wrong cpu", map->map[1] == 256); + TEST_ASSERT_VAL("wrong refcnt", atomic_read(&map->refcnt) == 1); + cpu_map__put(map); return 0; } 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); From eb12a1afdc02e59fc09934743490549c77327b1a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:26 +0100 Subject: [PATCH 162/271] 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(+) 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, From 374fb9e362f64e730388abc1de9bb93829670a54 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:27 +0100 Subject: [PATCH 163/271] 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(-) 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; From 6742434261158ad9678bf15b165304e0200cc324 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:28 +0100 Subject: [PATCH 164/271] 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/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/stat.c | 53 +++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 40 +++++++++++++++++++++++++ tools/perf/util/event.h | 5 ++++ 6 files changed, 104 insertions(+) create mode 100644 tools/perf/tests/stat.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 7abad28fe17e..fc0293150f93 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -35,6 +35,7 @@ perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o perf-y += cpumap.o +perf-y += stat.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 0c3fe2846de8..ed8402f339fa 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -187,6 +187,10 @@ static struct test generic_tests[] = { .desc = "Test cpu map synthesize", .func = test__cpu_map_synthesize, }, + { + .desc = "Test stat config synthesize", + .func = test__synthesize_stat_config, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c new file mode 100644 index 000000000000..c7a2bdb97708 --- /dev/null +++ b/tools/perf/tests/stat.c @@ -0,0 +1,53 @@ +#include +#include "event.h" +#include "tests.h" +#include "stat.h" +#include "debug.h" + +static bool has_term(struct stat_config_event *config, + u64 tag, u64 val) +{ + unsigned i; + + for (i = 0; i < config->nr; i++) { + if ((config->data[i].tag == tag) && + (config->data[i].val == val)) + return true; + } + + return false; +} + +static int process_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_config_event *config = &event->stat_config; + +#define HAS(term, val) \ + has_term(config, PERF_STAT_CONFIG_TERM__##term, val) + + TEST_ASSERT_VAL("wrong nr", config->nr == PERF_STAT_CONFIG_TERM__MAX); + TEST_ASSERT_VAL("wrong aggr_mode", HAS(AGGR_MODE, AGGR_CORE)); + TEST_ASSERT_VAL("wrong scale", HAS(SCALE, 1)); + TEST_ASSERT_VAL("wrong interval", HAS(INTERVAL, 1)); + +#undef HAS + + return 0; +} + +int test__synthesize_stat_config(int subtest __maybe_unused) +{ + struct perf_stat_config stat_config = { + .aggr_mode = AGGR_CORE, + .scale = 1, + .interval = 1, + }; + + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat_config(NULL, &stat_config, process_event, NULL)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index f85160f6ebb8..319757a3ca69 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -81,6 +81,7 @@ int test__bpf_subtest_get_nr(void); int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); +int test__synthesize_stat_config(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT 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, From 8e381596b67af53564a69f16440d3e5d5a73d034 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:29 +0100 Subject: [PATCH 165/271] 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/tests/stat.c | 6 ++++++ tools/perf/util/event.c | 24 ++++++++++++++++++++++++ tools/perf/util/event.h | 2 ++ 3 files changed, 32 insertions(+) diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index c7a2bdb97708..aa35d28294a0 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -24,6 +24,7 @@ static int process_event(struct perf_tool *tool __maybe_unused, struct machine *machine __maybe_unused) { struct stat_config_event *config = &event->stat_config; + struct perf_stat_config stat_config; #define HAS(term, val) \ has_term(config, PERF_STAT_CONFIG_TERM__##term, val) @@ -35,6 +36,11 @@ static int process_event(struct perf_tool *tool __maybe_unused, #undef HAS + perf_event__read_stat_config(&stat_config, config); + + TEST_ASSERT_VAL("wrong aggr_mode", stat_config.aggr_mode == AGGR_CORE); + TEST_ASSERT_VAL("wrong scale", stat_config.scale == 1); + TEST_ASSERT_VAL("wrong interval", stat_config.interval == 1); return 0; } 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, From d80518c90bb2b4af9755d79af5dfe9d44e04cdb9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:30 +0100 Subject: [PATCH 166/271] 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(-) 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; From 5796f8f073fe50171376f058376dde93ec5f3785 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:31 +0100 Subject: [PATCH 167/271] 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/tests/builtin-test.c | 4 ++++ tools/perf/tests/stat.c | 41 +++++++++++++++++++++++++++++---- tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 22 ++++++++++++++++++ tools/perf/util/event.h | 7 +++++- 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index ed8402f339fa..4a7d9989e1c3 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -191,6 +191,10 @@ static struct test generic_tests[] = { .desc = "Test stat config synthesize", .func = test__synthesize_stat_config, }, + { + .desc = "Test stat synthesize", + .func = test__synthesize_stat, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index aa35d28294a0..d319875a5e7c 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -2,6 +2,7 @@ #include "event.h" #include "tests.h" #include "stat.h" +#include "counts.h" #include "debug.h" static bool has_term(struct stat_config_event *config, @@ -18,10 +19,10 @@ static bool has_term(struct stat_config_event *config, return false; } -static int process_event(struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) +static int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) { struct stat_config_event *config = &event->stat_config; struct perf_stat_config stat_config; @@ -53,7 +54,37 @@ int test__synthesize_stat_config(int subtest __maybe_unused) }; TEST_ASSERT_VAL("failed to synthesize stat_config", - !perf_event__synthesize_stat_config(NULL, &stat_config, process_event, NULL)); + !perf_event__synthesize_stat_config(NULL, &stat_config, process_stat_config_event, NULL)); + + return 0; +} + +static int process_stat_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_event *st = &event->stat; + + TEST_ASSERT_VAL("wrong cpu", st->cpu == 1); + TEST_ASSERT_VAL("wrong thread", st->thread == 2); + TEST_ASSERT_VAL("wrong id", st->id == 3); + TEST_ASSERT_VAL("wrong val", st->val == 100); + TEST_ASSERT_VAL("wrong run", st->ena == 200); + TEST_ASSERT_VAL("wrong ena", st->run == 300); + return 0; +} + +int test__synthesize_stat(int subtest __maybe_unused) +{ + struct perf_counts_values count; + + count.val = 100; + count.ena = 200; + count.run = 300; + + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat(NULL, 1, 2, 3, &count, process_stat_event, NULL)); return 0; } diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 319757a3ca69..d36eda17a5f5 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -82,6 +82,7 @@ int test_session_topology(int subtest); int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); +int test__synthesize_stat(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT 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); From 0ea0e3558607626196eb09ace796aac585e61f5c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:32 +0100 Subject: [PATCH 168/271] 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(+) 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 From 2d8f0f18a5c37cf0322cb385b99adb1167b7cf78 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:33 +0100 Subject: [PATCH 169/271] 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(-) 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; From d4c2259195f538505d2570e78555532372fb4ad2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:34 +0100 Subject: [PATCH 170/271] 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/tests/builtin-test.c | 4 ++++ tools/perf/tests/stat.c | 21 +++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.c | 17 +++++++++++++++++ tools/perf/util/event.h | 4 ++++ 5 files changed, 47 insertions(+) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 4a7d9989e1c3..6a3519814492 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -195,6 +195,10 @@ static struct test generic_tests[] = { .desc = "Test stat synthesize", .func = test__synthesize_stat, }, + { + .desc = "Test stat round synthesize", + .func = test__synthesize_stat_round, + }, { .func = NULL, }, diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index d319875a5e7c..6a20ff2326bb 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -88,3 +88,24 @@ int test__synthesize_stat(int subtest __maybe_unused) return 0; } + +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct stat_round_event *stat_round = &event->stat_round; + + TEST_ASSERT_VAL("wrong time", stat_round->time == 0xdeadbeef); + TEST_ASSERT_VAL("wrong type", stat_round->type == PERF_STAT_ROUND_TYPE__INTERVAL); + return 0; +} + +int test__synthesize_stat_round(int subtest __maybe_unused) +{ + TEST_ASSERT_VAL("failed to synthesize stat_config", + !perf_event__synthesize_stat_round(NULL, 0xdeadbeef, PERF_STAT_ROUND_TYPE__INTERVAL, + process_stat_round_event, NULL)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index d36eda17a5f5..a82ab9c4c0ca 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -83,6 +83,7 @@ int test__thread_map_synthesize(int subtest); int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); int test__synthesize_stat(int subtest); +int test__synthesize_stat_round(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT 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); From e08a4564e23f8f89a055d717887674f54a9da515 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:35 +0100 Subject: [PATCH 171/271] 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(+) 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 From ffe777254cce24fb5fde3f0aa91fc755cfb1b812 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:36 +0100 Subject: [PATCH 172/271] 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(+) 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, From a6e5281780d1da65c15ce529707f43eb4a6df856 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:37 +0100 Subject: [PATCH 173/271] 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/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 +++ tools/perf/tests/event_update.c | 42 +++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/event.h | 4 +++ tools/perf/util/header.c | 44 +++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 7 files changed, 99 insertions(+) create mode 100644 tools/perf/tests/event_update.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index fc0293150f93..614899b88b37 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -36,6 +36,7 @@ perf-y += bpf.o perf-y += topology.o perf-y += cpumap.o perf-y += stat.o +perf-y += event_update.o $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(call rule_mkdir) diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 6a3519814492..f2b1dcac45d3 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -199,6 +199,10 @@ static struct test generic_tests[] = { .desc = "Test stat round synthesize", .func = test__synthesize_stat_round, }, + { + .desc = "Test attr update synthesize", + .func = test__event_update, + }, { .func = NULL, }, diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c new file mode 100644 index 000000000000..9cdf4c934977 --- /dev/null +++ b/tools/perf/tests/event_update.c @@ -0,0 +1,42 @@ +#include +#include "evlist.h" +#include "evsel.h" +#include "machine.h" +#include "tests.h" +#include "debug.h" + +static int process_event_unit(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event *) event; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__UNIT); + TEST_ASSERT_VAL("wrong unit", !strcmp(ev->data, "KRAVA")); + return 0; +} + +int test__event_update(int subtest __maybe_unused) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + + evlist = perf_evlist__new_default(); + TEST_ASSERT_VAL("failed to get evlist", evlist); + + evsel = perf_evlist__first(evlist); + + TEST_ASSERT_VAL("failed to allos ids", + !perf_evsel__alloc_id(evsel, 1, 1)); + + perf_evlist__id_add(evlist, evsel, 0, 0, 123); + + evsel->unit = strdup("KRAVA"); + + TEST_ASSERT_VAL("failed to synthesize attr update unit", + !perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit)); + + return 0; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a82ab9c4c0ca..82b2b5e6ba7c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -84,6 +84,7 @@ int test__cpu_map_synthesize(int subtest); int test__synthesize_stat_config(int subtest); int test__synthesize_stat(int subtest); int test__synthesize_stat_round(int subtest); +int test__event_update(int subtest); #if defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT 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, From daeecbc0c431f15f492fb8d704080a02de6e2918 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:38 +0100 Subject: [PATCH 174/271] 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/tests/event_update.c | 21 +++++++++++++++++++++ tools/perf/util/event.h | 5 +++++ tools/perf/util/header.c | 26 ++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 55 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index 9cdf4c934977..a91fcefc9f67 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -18,6 +18,22 @@ static int process_event_unit(struct perf_tool *tool __maybe_unused, return 0; } +static int process_event_scale(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event *) event; + struct event_update_event_scale *ev_data; + + ev_data = (struct event_update_event_scale *) ev->data; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE); + TEST_ASSERT_VAL("wrong scale", ev_data->scale = 0.123); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; @@ -38,5 +54,10 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update unit", !perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit)); + evsel->scale = 0.123; + + TEST_ASSERT_VAL("failed to synthesize attr update scale", + !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + return 0; } 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, From 802c9048b824eaa3c75d875e2d107460ad586439 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:39 +0100 Subject: [PATCH 175/271] 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/tests/event_update.c | 25 +++++++++++++++++++++++++ tools/perf/util/event.h | 1 + tools/perf/util/header.c | 21 +++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 50 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index a91fcefc9f67..482b89650bbe 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -34,10 +34,30 @@ static int process_event_scale(struct perf_tool *tool __maybe_unused, return 0; } +struct event_name { + struct perf_tool tool; + const char *name; +}; + +static int process_event_name(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_name *tmp = container_of(tool, struct event_name, tool); + struct event_update_event *ev = (struct event_update_event*) event; + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__NAME); + TEST_ASSERT_VAL("wrong name", !strcmp(ev->data, tmp->name)); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; struct perf_evsel *evsel; + struct event_name tmp; evlist = perf_evlist__new_default(); TEST_ASSERT_VAL("failed to get evlist", evlist); @@ -59,5 +79,10 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update scale", !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + tmp.name = perf_evsel__name(evsel); + + TEST_ASSERT_VAL("failed to synthesize attr update name", + !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name)); + return 0; } 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, From 86ebb09f96fe6886e1e5d53b648df5537ba859ca Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:40 +0100 Subject: [PATCH 176/271] 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/tests/event_update.c | 29 +++++++++++++++++++++++ tools/perf/util/event.h | 5 ++++ tools/perf/util/header.c | 42 +++++++++++++++++++++++++++++++++ tools/perf/util/header.h | 3 +++ 4 files changed, 79 insertions(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index 482b89650bbe..012eab5d1df1 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -53,6 +53,29 @@ static int process_event_name(struct perf_tool *tool, return 0; } +static int process_event_cpus(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct event_update_event *ev = (struct event_update_event*) event; + struct event_update_event_cpus *ev_data; + struct cpu_map *map; + + ev_data = (struct event_update_event_cpus*) ev->data; + + map = cpu_map__new_data(&ev_data->cpus); + + TEST_ASSERT_VAL("wrong id", ev->id == 123); + TEST_ASSERT_VAL("wrong type", ev->type == PERF_EVENT_UPDATE__CPUS); + TEST_ASSERT_VAL("wrong cpus", map->nr == 3); + TEST_ASSERT_VAL("wrong cpus", map->map[0] == 1); + TEST_ASSERT_VAL("wrong cpus", map->map[1] == 2); + TEST_ASSERT_VAL("wrong cpus", map->map[2] == 3); + cpu_map__put(map); + return 0; +} + int test__event_update(int subtest __maybe_unused) { struct perf_evlist *evlist; @@ -84,5 +107,11 @@ int test__event_update(int subtest __maybe_unused) TEST_ASSERT_VAL("failed to synthesize attr update name", !perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name)); + evsel->own_cpus = cpu_map__new("1,2,3"); + + TEST_ASSERT_VAL("failed to synthesize attr update cpus", + !perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus)); + + cpu_map__put(evsel->own_cpus); return 0; } 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, From c853f9394b7bc189632673cac802bdbf6537463b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:41 +0100 Subject: [PATCH 177/271] 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(+) 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, From 2d2aea6ae736503d3896c4997b494760ed8febc1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:42 +0100 Subject: [PATCH 178/271] 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(+) 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; } From ffa517adf625fa6a6c168285534e1ff7344fa2f1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 25 Oct 2015 15:51:43 +0100 Subject: [PATCH 179/271] 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/builtin-record.c | 2 ++ tools/perf/util/header.c | 14 ++++++++++++++ tools/perf/util/header.h | 1 + 3 files changed, 17 insertions(+) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 1435ef6265b6..9c5cdc2c4471 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -452,6 +452,8 @@ static void record__init_features(struct record *rec) if (!rec->opts.full_auxtrace) perf_header__clear_feat(&session->header, HEADER_AUXTRACE); + + perf_header__clear_feat(&session->header, HEADER_STAT); } static volatile int workload_exec_errno; 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, }; From 4979d0c7d0c73a3e799d4dcfbacd3cd11cc55638 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:46 +0100 Subject: [PATCH 180/271] perf stat record: Add record command Add 'perf stat record' command support. It creates simple (header only) perf.data file ATM. The record command could be specified anywhere among stat options. All stat command options are valid for stat record command with '-o' option exception. If specified for record command it denotes the perf data file name. Committer note: Set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless while avoiding that older tools show confusing messages, for instance, with sample_type = 0, we get: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.630237 task-clock (msec) # 0.528 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.083 M/sec 978,312 cycles # 1.552 GHz 671,931 stalled-cycles-frontend # 68.68% frontend cycles idle stalled-cycles-backend 646,379 instructions # 0.66 insns per cycle # 1.04 stalled cycles per insn 131,046 branches # 207.931 M/sec 7,073 branch-misses # 5.40% of all branches 0.001193240 seconds time elapsed $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? non matching sample_type $ While with sample_type set to PERF_SAMPLE_IDENTIFIER, after we re-run 'perf stat record usleep' we get: $ oldperf evlist WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ Which at least shows the names of the events in the perf.data file. Additionally, such files, when passed to 'perf report' will produce: $ oldperf report --stdio WARNING: The perf.data file's data size field is 0 which is unexpected. Was the 'perf record' command properly terminated? Warning: Kernel address maps (/proc/{kallsyms,modules}) were restricted. Check /proc/sys/kernel/kptr_restrict before running 'perf record'. As no suitable kallsyms nor vmlinux was found, kernel samples can't be resolved. Samples in kernel modules can't be resolved as well. Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ Which is confusing and can be solved by just adding the kernel mmap record, which will also remove that warning about the data size field being equal to zero, after generating the mmap record: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.600796 task-clock (msec) # 0.478 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 54 page-faults # 0.090 M/sec 886,844 cycles # 1.476 GHz 582,169 stalled-cycles-frontend # 65.65% frontend cycles idle stalled-cycles-backend 638,344 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 130,204 branches # 216.719 M/sec 7,500 branch-misses # 5.76% of all branches 0.001255897 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # [acme@zoo linux]$ No warnings, sensible output about what are the events in the perf.data file and also a "file has no samples" message, which indeed it doesn't. 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: htp://lkml.kernel.org/r/1446734469-11352-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 +++ tools/perf/builtin-stat.c | 120 ++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 4e074a660826..70eee1c2c444 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] +'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] DESCRIPTION ----------- @@ -22,6 +23,8 @@ OPTIONS ...:: Any command you can specify in a shell. +record:: + See STAT RECORD. -e:: --event=:: @@ -159,6 +162,15 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +STAT RECORD +----------- +Stores stat data into perf data file. + +-o file:: +--output file:: +Output file name. + + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index bbf42eefd5e5..af2a3bf659f7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -59,6 +59,7 @@ #include "util/thread.h" #include "util/thread_map.h" #include "util/counts.h" +#include "util/session.h" #include #include @@ -126,6 +127,16 @@ static bool append_file; static const char *output_name; static int output_fd; +struct perf_stat { + bool record; + struct perf_data_file file; + struct perf_session *session; + u64 bytes_written; +}; + +static struct perf_stat perf_stat; +#define STAT_RECORD perf_stat.record + static volatile int done = 0; static struct perf_stat_config stat_config = { @@ -166,7 +177,11 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; - attr->sample_type = 0; + /* + * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless + * while avoiding that older tools show confusing messages. + */ + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled @@ -202,6 +217,26 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } +static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) +{ + if (perf_data_file__write(stat->session->file, bf, size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + stat->bytes_written += size; + return 0; +} + +static int process_synthesized_event(struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + struct perf_stat *stat = (void *)tool; + return perf_stat__write(stat, event, event->header.size); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -361,6 +396,15 @@ static int __run_perf_stat(int argc, const char **argv) return -1; } + if (STAT_RECORD) { + int err, fd = perf_data_file__fd(&perf_stat.file); + + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + if (err < 0) + return err; + } + /* * Enable counters and exec the command: */ @@ -1261,6 +1305,38 @@ static int add_default_attributes(void) return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs); } +static const char * const recort_usage[] = { + "perf stat record []", + NULL, +}; + +static int __cmd_record(int argc, const char **argv) +{ + struct perf_session *session; + struct perf_data_file *file = &perf_stat.file; + + argc = parse_options(argc, argv, stat_options, record_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (output_name) + file->path = output_name; + + session = perf_session__new(file, false, NULL); + if (session == NULL) { + pr_err("Perf session creation failed.\n"); + return -1; + } + + /* No pipe support ATM */ + if (perf_stat.file.is_pipe) + return -EINVAL; + + session->evlist = evsel_list; + perf_stat.session = session; + perf_stat.record = true; + return argc; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1271,6 +1347,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; + const char * const stat_subcommands[] = { "record" }; setlocale(LC_ALL, ""); @@ -1278,12 +1355,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (evsel_list == NULL) return -ENOMEM; - argc = parse_options(argc, argv, stat_options, stat_usage, - PARSE_OPT_STOP_AT_NON_OPTION); + argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, + (const char **) stat_usage, + PARSE_OPT_STOP_AT_NON_OPTION); + + if (argc && !strncmp(argv[0], "rec", 3)) { + argc = __cmd_record(argc, argv); + if (argc < 0) + return -1; + } interval = stat_config.interval; - if (output_name && strcmp(output_name, "-")) + /* + * For record command the -o is already taken care of. + */ + if (!STAT_RECORD && output_name && strcmp(output_name, "-")) output = NULL; if (output_name && output_fd) { @@ -1450,6 +1537,31 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) if (!forever && status != -1 && !interval) print_counters(NULL, argc, argv); + if (STAT_RECORD) { + /* + * We synthesize the kernel mmap record just so that older tools + * don't emit warnings about not being able to resolve symbols + * due to /proc/sys/kernel/kptr_restrict settings and instear provide + * a saner message about no samples being in the perf.data file. + * + * This also serves to suppress a warning about f_header.data.size == 0 + * in header.c. -acme + */ + int fd = perf_data_file__fd(&perf_stat.file); + int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, + process_synthesized_event, + &perf_stat.session->machines.host); + if (err) { + pr_warning("Couldn't synthesize the kernel mmap record, harmless, " + "older tools may produce warnings about this file\n."); + } + + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + + perf_session__delete(perf_stat.session); + } + perf_stat__exit_aggr_mode(); perf_evlist__free_stats(evsel_list); out: From 3ba78bd00e508bf46a6aa2b8e296dc8287ea4c29 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:47 +0100 Subject: [PATCH 181/271] 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/builtin-stat.c | 15 +++++++++++++++ tools/perf/util/session.c | 3 +++ 2 files changed, 18 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index af2a3bf659f7..c9c896a94fee 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1310,6 +1310,19 @@ static const char * const recort_usage[] = { NULL, }; +static void init_features(struct perf_session *session) +{ + int feat; + + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); + perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); + perf_header__clear_feat(&session->header, HEADER_AUXTRACE); +} + static int __cmd_record(int argc, const char **argv) { struct perf_session *session; @@ -1331,6 +1344,8 @@ static int __cmd_record(int argc, const char **argv) if (perf_stat.file.is_pipe) return -EINVAL; + init_features(session); + session->evlist = evsel_list; perf_stat.session = session; perf_stat.record = true; 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; From 8b99b1a4e0b082ea6a277766982dac84483d4d3c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:48 +0100 Subject: [PATCH 182/271] perf stat record: Synthesize stat record data Synthesizing needed stat record data for report/script: - cpu/thread maps - stat config Committer note: New records generated on a perf.data file with this patch: $ perf report -D | grep PERF_RECORD_ 0x568 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 29097 0x590 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5a2 [0x40]: PERF_RECORD_STAT_CONFIG $ 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-5-git-send-email-jolsa@kernel.org [ Adjusted wrt kernel PERF_RECORD_MMAP added when introducing 'perf stat record' ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 60 +++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c9c896a94fee..45bf4d2caebe 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -217,24 +217,18 @@ static inline int nsec_counter(struct perf_evsel *evsel) return 0; } -static int perf_stat__write(struct perf_stat *stat, void *bf, size_t size) -{ - if (perf_data_file__write(stat->session->file, bf, size) < 0) { - pr_err("failed to write perf data, error: %m\n"); - return -1; - } - - stat->bytes_written += size; - return 0; -} - -static int process_synthesized_event(struct perf_tool *tool, +static int process_synthesized_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { - struct perf_stat *stat = (void *)tool; - return perf_stat__write(stat, event, event->header.size); + if (perf_data_file__write(&perf_stat.file, event, event->header.size) < 0) { + pr_err("failed to write perf data, error: %m\n"); + return -1; + } + + perf_stat.bytes_written += event->header.size; + return 0; } /* @@ -323,6 +317,35 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static int perf_stat_synthesize_config(void) +{ + int err; + + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, + process_synthesized_event, + NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_cpu_map(NULL, evsel_list->cpus, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize thread map.\n"); + return err; + } + + err = perf_event__synthesize_stat_config(NULL, &stat_config, + process_synthesized_event, NULL); + if (err < 0) { + pr_err("Couldn't synthesize config.\n"); + return err; + } + + return 0; +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -403,6 +426,10 @@ static int __run_perf_stat(int argc, const char **argv) fd, false); if (err < 0) return err; + + err = perf_stat_synthesize_config(); + if (err < 0) + return err; } /* @@ -1560,7 +1587,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) * a saner message about no samples being in the perf.data file. * * This also serves to suppress a warning about f_header.data.size == 0 - * in header.c. -acme + * in header.c at the moment 'perf stat record' gets introduced, which + * is not really needed once we start adding the stat specific PERF_RECORD_ + * records, but the need to suppress the kptr_restrict messages in older + * tools remain -acme */ int fd = perf_data_file__fd(&perf_stat.file); int err = perf_event__synthesize_kernel_mmap((void *)&perf_stat, From 1c59612de0264790698e32eb0368daf3fcba4c65 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: [PATCH 183/271] 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(-) 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); From 2af4646d1041ee590b0032d2b0103fa81aa43174 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:49 +0100 Subject: [PATCH 184/271] perf stat record: Store events IDs in perf data file Store 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 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 45bf4d2caebe..39d0c30f0f59 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -346,6 +346,38 @@ static int perf_stat_synthesize_config(void) return 0; } +#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) + +static int __store_counter_ids(struct perf_evsel *counter, + struct cpu_map *cpus, + struct thread_map *threads) +{ + int cpu, thread; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd = FD(counter, cpu, thread); + + if (perf_evlist__id_add_fd(evsel_list, counter, + cpu, thread, fd) < 0) + return -1; + } + } + + return 0; +} + +static int store_counter_ids(struct perf_evsel *counter) +{ + struct cpu_map *cpus = counter->cpus; + struct thread_map *threads = counter->threads; + + if (perf_evsel__alloc_id(counter, cpus->nr, threads->nr)) + return -ENOMEM; + + return __store_counter_ids(counter, cpus, threads); +} + static int __run_perf_stat(int argc, const char **argv) { int interval = stat_config.interval; @@ -410,6 +442,9 @@ static int __run_perf_stat(int argc, const char **argv) l = strlen(counter->unit); if (l > unit_width) unit_width = l; + + if (STAT_RECORD && store_counter_ids(counter)) + return -1; } if (perf_evlist__apply_filters(evsel_list, &counter)) { From 664c98d4e1c2ff60627d78d4c8ae81cd2df13783 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:50 +0100 Subject: [PATCH 185/271] perf stat record: Add pipe support for record command Allowing storing stat record data into pipe, so report tools (report/script) could read data directly from record. Committer note: Before this patch: $ perf stat record -o - usleep 1 | perf report -i - incompatible file format (rerun with -v to learn more) $ perf stat record -o - usleep 1 | perf script -i - incompatible file format (rerun with -v to learn more) $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ After: $ perf stat record -o - usleep 1 | perf report -i - # To display the perf.data header info, please use # --header/--header-only options. # Error: The - file has no samples! $ perf stat record -o - usleep 1 | perf script -i - Display of symbols requested but neither sample IP nor sample address is selected. Hence, no addresses to convert to symbols. 0 [0x80]: failed to process type: 64 $ ls -la perf.data ls: cannot access perf.data: No such file or directory $ 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-7-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 39d0c30f0f59..8a2f9ce677e7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -317,10 +317,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } -static int perf_stat_synthesize_config(void) +static int perf_stat_synthesize_config(bool is_pipe) { int err; + if (is_pipe) { + err = perf_event__synthesize_attrs(NULL, perf_stat.session, + process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize attrs.\n"); + return err; + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); @@ -388,6 +397,7 @@ static int __run_perf_stat(int argc, const char **argv) size_t l; int status = 0; const bool forks = (argc > 0); + bool is_pipe = STAT_RECORD ? perf_stat.file.is_pipe : false; if (interval) { ts.tv_sec = interval / 1000; @@ -398,7 +408,7 @@ static int __run_perf_stat(int argc, const char **argv) } if (forks) { - if (perf_evlist__prepare_workload(evsel_list, &target, argv, false, + if (perf_evlist__prepare_workload(evsel_list, &target, argv, is_pipe, workload_exec_failed_signal) < 0) { perror("failed to prepare workload"); return -1; @@ -457,12 +467,17 @@ static int __run_perf_stat(int argc, const char **argv) if (STAT_RECORD) { int err, fd = perf_data_file__fd(&perf_stat.file); - err = perf_session__write_header(perf_stat.session, evsel_list, - fd, false); + if (is_pipe) { + err = perf_header__write_pipe(perf_data_file__fd(&perf_stat.file)); + } else { + err = perf_session__write_header(perf_stat.session, evsel_list, + fd, false); + } + if (err < 0) return err; - err = perf_stat_synthesize_config(); + err = perf_stat_synthesize_config(is_pipe); if (err < 0) return err; } @@ -970,6 +985,10 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) struct perf_evsel *counter; char buf[64], *prefix = NULL; + /* Do not print anything if we record to the pipe. */ + if (STAT_RECORD && perf_stat.file.is_pipe) + return; + if (interval) print_interval(prefix = buf, ts); else @@ -1402,10 +1421,6 @@ static int __cmd_record(int argc, const char **argv) return -1; } - /* No pipe support ATM */ - if (perf_stat.file.is_pipe) - return -EINVAL; - init_features(session); session->evlist = evsel_list; @@ -1636,8 +1651,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } - perf_stat.session->header.data_size += perf_stat.bytes_written; - perf_session__write_header(perf_stat.session, evsel_list, fd, true); + if (!perf_stat.file.is_pipe) { + perf_stat.session->header.data_size += perf_stat.bytes_written; + perf_session__write_header(perf_stat.session, evsel_list, fd, true); + } perf_session__delete(perf_stat.session); } From 5a6ea81b8f9ce2736759d256ac4d63be65751199 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:51 +0100 Subject: [PATCH 186/271] perf stat record: Write stat events on record Writing stat events on 'perf stat record' at the time we read counter values from kernel. Committer note: After the patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.598006 task-clock (msec) # 0.484 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 52 page-faults # 0.087 M/sec 882,744 cycles # 1.476 GHz 581,416 stalled-cycles-frontend # 65.86% frontend cycles idle stalled-cycles-backend 636,479 instructions # 0.72 insns per cycle # 0.91 stalled cycles per insn 129,334 branches # 216.275 M/sec 7,512 branch-misses # 5.81% of all branches 0.001235157 seconds time elapsed $ oldperf evlist task-clock context-switches cpu-migrations page-faults cycles stalled-cycles-frontend stalled-cycles-backend instructions branches branch-misses $ oldperf report --stdio Error: The perf.data file has no samples! # To display the perf.data header info, please use --header/--header-only options. # $ perf report -D | grep PERF_RECORD 0x5b0 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 5504 0x5d8 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535 0x5ea [0x40]: PERF_RECORD_STAT_CONFIG 0x62a [0x30]: PERF_RECORD_STAT 0x65a [0x30]: PERF_RECORD_STAT 0x68a [0x30]: PERF_RECORD_STAT 0x6ba [0x30]: PERF_RECORD_STAT 0x6ea [0x30]: PERF_RECORD_STAT 0x71a [0x30]: PERF_RECORD_STAT 0x74a [0x30]: PERF_RECORD_STAT 0x77a [0x30]: PERF_RECORD_STAT 0x7aa [0x30]: PERF_RECORD_STAT -1 -1 0x7da [0x40]: PERF_RECORD_MMAP -1/0: [0xffffffff81000000(0x1f000000) @ 0xffffffff81000000]: x [kernel.kallsyms]_text $ 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-8-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 8a2f9ce677e7..32aa2ea1c553 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,18 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) + +static int +perf_evsel__write_stat_event(struct perf_evsel *counter, u32 cpu, u32 thread, + struct perf_counts_values *count) +{ + struct perf_sample_id *sid = SID(counter, cpu, thread); + + return perf_event__synthesize_stat(NULL, cpu, thread, sid->id, count, + process_synthesized_event, NULL); +} + /* * Read out the results of a single counter: * do not aggregate counts across CPUs in system-wide mode @@ -254,6 +266,13 @@ static int read_counter(struct perf_evsel *counter) count = perf_counts(counter->counts, cpu, thread); if (perf_evsel__read(counter, cpu, thread, count)) return -1; + + if (STAT_RECORD) { + if (perf_evsel__write_stat_event(counter, cpu, thread, count)) { + pr_err("failed to write stat event\n"); + return -1; + } + } } } From 7aad0c32bb6aaa39aab596264ddc49d44c8088f3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:52 +0100 Subject: [PATCH 187/271] perf stat record: Write stat round events on record Writing stat round events on 'perf stat record' for each interval round. In non interval mode we store round event after the last stat event. Committer note: After the patch: $ perf report -D | grep PERF_RECORD | grep ROUND 0x852 [0x18]: PERF_RECORD_STAT_ROUND $ Reported-by: Kan Liang 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/1446734469-11352-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 32aa2ea1c553..fcece42c2611 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -231,6 +231,16 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } +static int write_stat_round_event(u64 time, u64 type) +{ + return perf_event__synthesize_stat_round(NULL, time, type, + process_synthesized_event, + NULL); +} + +#define WRITE_STAT_ROUND_EVENT(time, interval) \ + write_stat_round_event(time, PERF_STAT_ROUND_TYPE__ ## interval) + #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) static int @@ -306,6 +316,11 @@ static void process_interval(void) clock_gettime(CLOCK_MONOTONIC, &ts); diff_timespec(&rs, &ts, &ref_time); + if (STAT_RECORD) { + if (WRITE_STAT_ROUND_EVENT(rs.tv_sec * NSECS_PER_SEC + rs.tv_nsec, INTERVAL)) + pr_err("failed to write stat round event\n"); + } + print_counters(&rs, 0, NULL); } @@ -1670,6 +1685,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) "older tools may produce warnings about this file\n."); } + if (!interval) { + if (WRITE_STAT_ROUND_EVENT(walltime_nsecs_stats.max, FINAL)) + pr_err("failed to write stat round event\n"); + } + if (!perf_stat.file.is_pipe) { perf_stat.session->header.data_size += perf_stat.bytes_written; perf_session__write_header(perf_stat.session, evsel_list, fd, true); From e9d6db8e8df42a38f79f264ab58c104e1678b12c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:53 +0100 Subject: [PATCH 188/271] perf stat record: Do not allow record with multiple runs mode We currently don't support storing multiple session in perf.data, so we can't allow -r option in stat record. $ perf stat -e cycles -r 2 record ls Cannot use -r option with perf stat record. Committer note: Before this patch we would a perf.data file such as: $ perf stat -e cycles -r 2 record ls Performance counter stats for 'ls' (2 runs): 3,935,236 cycles 0.002353261 seconds time elapsed ( +- 4.76% ) $ perf report -D | grep PERF_RECORD | grep ROUND 0xf0 [0]: failed to process type: 16 Error: failed to process sample $ Reported-by: Kan Liang 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/1446734469-11352-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index fcece42c2611..10f86a6d7b15 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1449,6 +1449,11 @@ static int __cmd_record(int argc, const char **argv) if (output_name) file->path = output_name; + if (run_count != 1 || forever) { + pr_err("Cannot use -r option with perf stat record.\n"); + return -1; + } + session = perf_session__new(file, false, NULL); if (session == NULL) { pr_err("Perf session creation failed.\n"); From 7b60a7e3a687481553d2b6ec7e6390a6e82f1849 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:54 +0100 Subject: [PATCH 189/271] perf stat record: Synthesize event update events Synthesize other events stuff not carried within attr event - unit, scale, name. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-11-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 10f86a6d7b15..575e2535ea03 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -351,8 +351,19 @@ static void workload_exec_failed_signal(int signo __maybe_unused, siginfo_t *inf workload_exec_errno = info->si_value.sival_int; } +static bool has_unit(struct perf_evsel *counter) +{ + return counter->unit && *counter->unit; +} + +static bool has_scale(struct perf_evsel *counter) +{ + return counter->scale != 1; +} + static int perf_stat_synthesize_config(bool is_pipe) { + struct perf_evsel *counter; int err; if (is_pipe) { @@ -364,6 +375,54 @@ static int perf_stat_synthesize_config(bool is_pipe) } } + /* + * Synthesize other events stuff not carried within + * attr event - unit, scale, name + */ + evlist__for_each(evsel_list, counter) { + if (!counter->supported) + continue; + + /* + * Synthesize unit and scale only if it's defined. + */ + if (has_unit(counter)) { + err = perf_event__synthesize_event_update_unit(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel unit.\n"); + return err; + } + } + + if (has_scale(counter)) { + err = perf_event__synthesize_event_update_scale(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + if (counter->own_cpus) { + err = perf_event__synthesize_event_update_cpus(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel scale.\n"); + return err; + } + } + + /* + * Name is needed only for pipe output, + * perf.data carries event names. + */ + if (is_pipe) { + err = perf_event__synthesize_event_update_name(NULL, counter, process_synthesized_event); + if (err < 0) { + pr_err("Couldn't synthesize evsel name.\n"); + return err; + } + } + } + err = perf_event__synthesize_thread_map2(NULL, evsel_list->threads, process_synthesized_event, NULL); From ba6039b6c8fcc24de7d6ab7b0bada4becaf84a2c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:55 +0100 Subject: [PATCH 190/271] perf stat report: Add report command Adding 'perf stat report' command support. ATM it only processes attr events and display nothing. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-12-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 12 +++++ tools/perf/builtin-stat.c | 61 ++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 70eee1c2c444..95f492828657 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -11,6 +11,7 @@ SYNOPSIS 'perf stat' [-e | --event=EVENT] [-a] 'perf stat' [-e | --event=EVENT] [-a] -- [] 'perf stat' [-e | --event=EVENT] [-a] record [-o file] -- [] +'perf stat' report [-i file] DESCRIPTION ----------- @@ -26,6 +27,9 @@ OPTIONS record:: See STAT RECORD. +report:: + See STAT REPORT. + -e:: --event=:: Select the PMU event. Selection can be: @@ -170,6 +174,14 @@ Stores stat data into perf data file. --output file:: Output file name. +STAT REPORT +----------- +Reads and reports stat data from perf data file. + +-i file:: +--input file:: +Input file name. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 575e2535ea03..abba49b847d2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -60,6 +60,8 @@ #include "util/thread_map.h" #include "util/counts.h" #include "util/session.h" +#include "util/tool.h" +#include "asm/bug.h" #include #include @@ -132,6 +134,7 @@ struct perf_stat { struct perf_data_file file; struct perf_session *session; u64 bytes_written; + struct perf_tool tool; }; static struct perf_stat perf_stat; @@ -1041,8 +1044,8 @@ static void print_header(int argc, const char **argv) else if (target.cpu_list) fprintf(output, "\'CPU(s) %s", target.cpu_list); else if (!target__has_task(&target)) { - fprintf(output, "\'%s", argv[0]); - for (i = 1; i < argc; i++) + fprintf(output, "\'%s", argv ? argv[0] : "pipe"); + for (i = 1; argv && (i < argc); i++) fprintf(output, " %s", argv[i]); } else if (target.pid) fprintf(output, "process id \'%s", target.pid); @@ -1527,6 +1530,55 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static const char * const report_usage[] = { + "perf stat report []", + NULL, +}; + +static struct perf_stat perf_stat = { + .tool = { + .attr = perf_event__process_attr, + }, +}; + +static int __cmd_report(int argc, const char **argv) +{ + struct perf_session *session; + const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_END() + }; + struct stat st; + int ret; + + argc = parse_options(argc, argv, options, report_usage, 0); + + if (!input_name || !strlen(input_name)) { + if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) + input_name = "-"; + else + input_name = "perf.data"; + } + + perf_stat.file.path = input_name; + perf_stat.file.mode = PERF_DATA_MODE_READ; + + session = perf_session__new(&perf_stat.file, false, &perf_stat.tool); + if (session == NULL) + return -1; + + perf_stat.session = session; + stat_config.output = stderr; + evsel_list = session->evlist; + + ret = perf_session__process_events(session); + if (ret) + return ret; + + perf_session__delete(session); + return 0; +} + int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { const char * const stat_usage[] = { @@ -1537,7 +1589,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) const char *mode; FILE *output = stderr; unsigned int interval; - const char * const stat_subcommands[] = { "record" }; + const char * const stat_subcommands[] = { "record", "report" }; setlocale(LC_ALL, ""); @@ -1553,7 +1605,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) argc = __cmd_record(argc, argv); if (argc < 0) return -1; - } + } else if (argc && !strncmp(argv[0], "rep", 3)) + return __cmd_report(argc, argv); interval = stat_config.interval; From 1975d36e14b3314d1d0c7a428946ec0c27fd6e95 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:56 +0100 Subject: [PATCH 191/271] perf stat report: Process cpu/threads maps Adding processing of cpu/threads maps. Configuring session's evlist with these maps. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-13-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/time/tm/g parameters to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 66 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index abba49b847d2..0a1cfdd70df0 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -135,6 +135,9 @@ struct perf_stat { struct perf_session *session; u64 bytes_written; struct perf_tool tool; + bool maps_allocated; + struct cpu_map *cpus; + struct thread_map *threads; }; static struct perf_stat perf_stat; @@ -234,9 +237,9 @@ static int process_synthesized_event(struct perf_tool *tool __maybe_unused, return 0; } -static int write_stat_round_event(u64 time, u64 type) +static int write_stat_round_event(u64 tm, u64 type) { - return perf_event__synthesize_stat_round(NULL, time, type, + return perf_event__synthesize_stat_round(NULL, tm, type, process_synthesized_event, NULL); } @@ -1530,6 +1533,63 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int set_maps(struct perf_stat *st) +{ + if (!st->cpus || !st->threads) + return 0; + + if (WARN_ONCE(st->maps_allocated, "stats double allocation\n")) + return -EINVAL; + + perf_evlist__set_maps(evsel_list, st->cpus, st->threads); + + if (perf_evlist__alloc_stats(evsel_list, true)) + return -ENOMEM; + + st->maps_allocated = true; + return 0; +} + +static +int process_thread_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + + if (st->threads) { + pr_warning("Extra thread map event, ignoring.\n"); + return 0; + } + + st->threads = thread_map__new_event(&event->thread_map); + if (!st->threads) + return -ENOMEM; + + return set_maps(st); +} + +static +int process_cpu_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + struct cpu_map *cpus; + + if (st->cpus) { + pr_warning("Extra cpu map event, ignoring.\n"); + return 0; + } + + cpus = cpu_map__new_data(&event->cpu_map.data); + if (!cpus) + return -ENOMEM; + + st->cpus = cpus; + return set_maps(st); +} + static const char * const report_usage[] = { "perf stat report []", NULL, @@ -1538,6 +1598,8 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .thread_map = process_thread_map_event, + .cpu_map = process_cpu_map_event, }, }; From 62ba18ba938a8740ab18e02342b282d7378986f7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:57 +0100 Subject: [PATCH 192/271] perf stat report: Process stat config event Adding processing of stat config event and initialize stat_config object. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-14-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/builtin-stat.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 0a1cfdd70df0..1e5db50dab9e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1533,6 +1533,15 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static +int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + perf_event__read_stat_config(&stat_config, &event->stat_config); + return 0; +} + static int set_maps(struct perf_stat *st) { if (!st->cpus || !st->threads) @@ -1600,6 +1609,7 @@ static struct perf_stat perf_stat = { .attr = perf_event__process_attr, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, + .stat_config = process_stat_config_event, }, }; From 68d702f7a1202dd39d9fa01b7bea92ba9e5785d9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:58 +0100 Subject: [PATCH 193/271] perf stat report: Add support to initialize aggr_map from file Using perf.data's perf_env data to initialize aggregate config. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-15-git-send-email-jolsa@kernel.org [ s/stat/st/g, s/socket/socket_id/g to fix 'already defined' build error with older distros (e.g. RHEL6.7) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1e5db50dab9e..c78052580023 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1326,6 +1326,101 @@ static void perf_stat__exit_aggr_mode(void) cpus_aggr_map = NULL; } +static inline int perf_env__get_cpu(struct perf_env *env, struct cpu_map *map, int idx) +{ + int cpu; + + if (idx > map->nr) + return -1; + + cpu = map->map[idx]; + + if (cpu >= env->nr_cpus_online) + return -1; + + return cpu; +} + +static int perf_env__get_socket(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int cpu = perf_env__get_cpu(env, map, idx); + + return cpu == -1 ? -1 : env->cpu[cpu].socket_id; +} + +static int perf_env__get_core(struct cpu_map *map, int idx, void *data) +{ + struct perf_env *env = data; + int core = -1, cpu = perf_env__get_cpu(env, map, idx); + + if (cpu != -1) { + int socket_id = env->cpu[cpu].socket_id; + + /* + * Encode socket in upper 16 bits + * core_id is relative to socket, and + * we need a global id. So we combine + * socket + core id. + */ + core = (socket_id << 16) | (env->cpu[cpu].core_id & 0xffff); + } + + return core; +} + +static int perf_env__build_socket_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **sockp) +{ + return cpu_map__build_map(cpus, sockp, perf_env__get_socket, env); +} + +static int perf_env__build_core_map(struct perf_env *env, struct cpu_map *cpus, + struct cpu_map **corep) +{ + return cpu_map__build_map(cpus, corep, perf_env__get_core, env); +} + +static int perf_stat__get_socket_file(struct cpu_map *map, int idx) +{ + return perf_env__get_socket(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat__get_core_file(struct cpu_map *map, int idx) +{ + return perf_env__get_core(map, idx, &perf_stat.session->header.env); +} + +static int perf_stat_init_aggr_mode_file(struct perf_stat *st) +{ + struct perf_env *env = &st->session->header.env; + + switch (stat_config.aggr_mode) { + case AGGR_SOCKET: + if (perf_env__build_socket_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build socket map"); + return -1; + } + aggr_get_id = perf_stat__get_socket_file; + break; + case AGGR_CORE: + if (perf_env__build_core_map(env, evsel_list->cpus, &aggr_map)) { + perror("cannot build core map"); + return -1; + } + aggr_get_id = perf_stat__get_core_file; + break; + case AGGR_NONE: + case AGGR_GLOBAL: + case AGGR_THREAD: + case AGGR_UNSET: + default: + break; + } + + return 0; +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: @@ -1538,7 +1633,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_session *session __maybe_unused) { + struct perf_stat *st = container_of(tool, struct perf_stat, tool); + perf_event__read_stat_config(&stat_config, &event->stat_config); + + if (perf_stat.file.is_pipe) + perf_stat_init_aggr_mode(); + else + perf_stat_init_aggr_mode_file(st); + return 0; } From 6edb78a2178fd85d07b1a7fbb3629be56b860224 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:01 +0100 Subject: [PATCH 194/271] perf stat report: Move csv_sep initialization before report command So we have csv_sep properly initialized before report command leg. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-18-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c78052580023..f9d4e0963ac7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1776,6 +1776,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) (const char **) stat_usage, PARSE_OPT_STOP_AT_NON_OPTION); + if (csv_sep) { + csv_output = true; + if (!strcmp(csv_sep, "\\t")) + csv_sep = "\t"; + } else + csv_sep = DEFAULT_SEPARATOR; + if (argc && !strncmp(argv[0], "rec", 3)) { argc = __cmd_record(argc, argv); if (argc < 0) @@ -1826,13 +1833,6 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) stat_config.output = output; - if (csv_sep) { - csv_output = true; - if (!strcmp(csv_sep, "\\t")) - csv_sep = "\t"; - } else - csv_sep = DEFAULT_SEPARATOR; - /* * let the spreadsheet do the pretty-printing */ From a56f9390aa9d9b1c782c3dbd5ca2c4245eb265fc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:40:59 +0100 Subject: [PATCH 195/271] perf stat report: Process stat and stat round events Adding processing of stat and stat round events. The stat data com in stat events, using generic function process_stat_round_event to store data under perf_evsel object. The stat-round events comes each interval or as last event in non interval mode. The function process_stat_round_event process stored data for each perf_evsel object and print it out. Committer note: After this patch: $ perf stat record usleep 1 Performance counter stats for 'usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ $ perf stat report Performance counter stats for '/home/acme/bin/perf stat record usleep 1': 0.498381 task-clock (msec) # 0.571 CPUs utilized 2 context-switches # 0.004 M/sec 0 cpu-migrations # 0.000 K/sec 149 page-faults # 0.299 M/sec 1,271,635 cycles # 2.552 GHz 928,712 stalled-cycles-frontend # 73.03% frontend cycles idle 663,286 stalled-cycles-backend # 52.16% backend cycles idle 792,614 instructions # 0.62 insns per cycle # 1.17 stalled cycles per insn 136,850 branches # 274.589 M/sec branch-misses (0.00%) 0.000873419 seconds time elapsed $ Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-16-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f9d4e0963ac7..d27d1b921efa 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1628,6 +1628,32 @@ static int __cmd_record(int argc, const char **argv) return argc; } +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct stat_round_event *round = &event->stat_round; + struct perf_evsel *counter; + struct timespec tsh, *ts = NULL; + const char **argv = session->header.env.cmdline_argv; + int argc = session->header.env.nr_cmdline; + + evlist__for_each(evsel_list, counter) + perf_stat_process_counter(&stat_config, counter); + + if (round->type == PERF_STAT_ROUND_TYPE__FINAL) + update_stats(&walltime_nsecs_stats, round->time); + + if (stat_config.interval && round->time) { + tsh.tv_sec = round->time / NSECS_PER_SEC; + tsh.tv_nsec = round->time % NSECS_PER_SEC; + ts = &tsh; + } + + print_counters(ts, argc, argv); + return 0; +} + static int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -1713,6 +1739,8 @@ static struct perf_stat perf_stat = { .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, + .stat = perf_event__process_stat_event, + .stat_round = process_stat_round_event, }, }; From fa6ea7817db3839b58d46649b7834320257e7702 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:00 +0100 Subject: [PATCH 196/271] perf stat report: Process event update events Adding processing of event update events, so perf stat report can store additional info for events - unit,scale,name. Committer note: Before: # perf stat record -e power/energy-cores/ -a ^C Performance counter stats for 'system wide': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 332,488,114,176 power/energy-cores/ 1.597176695 seconds time elapsed # After, using the same perf.data file generated in the "Before" case above: # perf stat report Performance counter stats for '/home/acme/bin/perf stat record -e power/energy-cores/ -a': 77.41 Joules power/energy-cores/ 1.597176695 seconds time elapsed # Reported-by: Kan Liang 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/1446734469-11352-17-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d27d1b921efa..3ccf5a9dab33 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1736,6 +1736,7 @@ static const char * const report_usage[] = { static struct perf_stat perf_stat = { .tool = { .attr = perf_event__process_attr, + .event_update = perf_event__process_event_update, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .stat_config = process_stat_config_event, From 89af4e05c21d68f22e07fe66940ea675615a49ed Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 5 Nov 2015 15:41:02 +0100 Subject: [PATCH 197/271] perf stat report: Allow to override aggr_mode Allowing to override record aggr_mode. It's possible to use perf stat like: $ perf stat report -A $ perf stat report --per-core $ perf stat report --per-socket To customize the recorded aggregate mode regardless what was used during the stat record command. Reported-by: Kan Liang Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1446734469-11352-19-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/Documentation/perf-stat.txt | 10 ++++++++++ tools/perf/builtin-stat.c | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 95f492828657..52ef7a9d50aa 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -182,6 +182,16 @@ Reads and reports stat data from perf data file. --input file:: Input file name. +--per-socket:: +Aggregate counts per processor socket for system-wide mode measurements. + +--per-core:: +Aggregate counts per physical processor for system-wide mode measurements. + +-A:: +--no-aggr:: +Do not aggregate counts across all monitored CPUs. + EXAMPLES -------- diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3ccf5a9dab33..9805e03ab163 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -138,6 +138,7 @@ struct perf_stat { bool maps_allocated; struct cpu_map *cpus; struct thread_map *threads; + enum aggr_mode aggr_mode; }; static struct perf_stat perf_stat; @@ -1663,6 +1664,15 @@ int process_stat_config_event(struct perf_tool *tool __maybe_unused, perf_event__read_stat_config(&stat_config, &event->stat_config); + if (cpu_map__empty(st->cpus)) { + if (st->aggr_mode != AGGR_UNSET) + pr_warning("warning: processing task data, aggregation mode not set\n"); + return 0; + } + + if (st->aggr_mode != AGGR_UNSET) + stat_config.aggr_mode = st->aggr_mode; + if (perf_stat.file.is_pipe) perf_stat_init_aggr_mode(); else @@ -1743,6 +1753,7 @@ static struct perf_stat perf_stat = { .stat = perf_event__process_stat_event, .stat_round = process_stat_round_event, }, + .aggr_mode = AGGR_UNSET, }; static int __cmd_report(int argc, const char **argv) @@ -1750,6 +1761,12 @@ static int __cmd_report(int argc, const char **argv) struct perf_session *session; const struct option options[] = { OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_SET_UINT(0, "per-socket", &perf_stat.aggr_mode, + "aggregate counts per processor socket", AGGR_SOCKET), + OPT_SET_UINT(0, "per-core", &perf_stat.aggr_mode, + "aggregate counts per physical processor core", AGGR_CORE), + OPT_SET_UINT('A', "no-aggr", &perf_stat.aggr_mode, + "disable CPU count aggregation", AGGR_NONE), OPT_END() }; struct stat st; From 7b648018f628eee73450b71dc68ebb3c3865465e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 3 Dec 2015 18:35:21 +0100 Subject: [PATCH 198/271] perf/core: Collapse more IPI loops This patch collapses the two 'hard' cases, which are perf_event_{dis,en}able(). I cannot seem to convince myself the current code is correct. So starting with perf_event_disable(); we don't strictly need to test for event->state == ACTIVE, ctx->is_active is enough. If the event is not scheduled while the ctx is, __perf_event_disable() still does the right thing. Its a little less efficient to IPI in that case, over-all simpler. For perf_event_enable(); the same goes, but I think that's actually broken in its current form. The current condition is: ctx->is_active && event->state == OFF, that means it doesn't do anything when !ctx->active && event->state == OFF. This is wrong, it should still mark the event INACTIVE in that case, otherwise we'll still not try and schedule the event once the context becomes active again. This patch implements the two function using the new event_function_call() and does away with the tricky event->state tests. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Signed-off-by: Ingo Molnar --- kernel/events/core.c | 108 ++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 74 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 2c7bb20afc43..bf8244190d0f 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1766,6 +1766,20 @@ int __perf_event_disable(void *info) return 0; } +void ___perf_event_disable(void *info) +{ + struct perf_event *event = info; + + /* + * Since we have the lock this context can't be scheduled + * in, so we can change the state safely. + */ + if (event->state == PERF_EVENT_STATE_INACTIVE) { + update_group_times(event); + event->state = PERF_EVENT_STATE_OFF; + } +} + /* * Disable a event. * @@ -1782,43 +1796,16 @@ int __perf_event_disable(void *info) static void _perf_event_disable(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Disable the event on the cpu that it's on - */ - cpu_function_call(event->cpu, __perf_event_disable, event); - return; - } - -retry: - if (!task_function_call(task, __perf_event_disable, event)) - return; raw_spin_lock_irq(&ctx->lock); - /* - * If the event is still active, we need to retry the cross-call. - */ - if (event->state == PERF_EVENT_STATE_ACTIVE) { + if (event->state <= PERF_EVENT_STATE_OFF) { raw_spin_unlock_irq(&ctx->lock); - /* - * Reload the task pointer, it might have been changed by - * a concurrent perf_event_context_sched_out(). - */ - task = ctx->task; - goto retry; - } - - /* - * Since we have the lock this context can't be scheduled - * in, so we can change the state safely. - */ - if (event->state == PERF_EVENT_STATE_INACTIVE) { - update_group_times(event); - event->state = PERF_EVENT_STATE_OFF; + return; } raw_spin_unlock_irq(&ctx->lock); + + event_function_call(event, __perf_event_disable, + ___perf_event_disable, event); } /* @@ -2269,6 +2256,11 @@ unlock: return 0; } +void ___perf_event_enable(void *info) +{ + __perf_event_mark_enabled((struct perf_event *)info); +} + /* * Enable a event. * @@ -2281,58 +2273,26 @@ unlock: static void _perf_event_enable(struct perf_event *event) { struct perf_event_context *ctx = event->ctx; - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Enable the event on the cpu that it's on - */ - cpu_function_call(event->cpu, __perf_event_enable, event); - return; - } raw_spin_lock_irq(&ctx->lock); - if (event->state >= PERF_EVENT_STATE_INACTIVE) - goto out; + if (event->state >= PERF_EVENT_STATE_INACTIVE) { + raw_spin_unlock_irq(&ctx->lock); + return; + } /* * If the event is in error state, clear that first. - * That way, if we see the event in error state below, we - * know that it has gone back into error state, as distinct - * from the task having been scheduled away before the - * cross-call arrived. + * + * That way, if we see the event in error state below, we know that it + * has gone back into error state, as distinct from the task having + * been scheduled away before the cross-call arrived. */ if (event->state == PERF_EVENT_STATE_ERROR) event->state = PERF_EVENT_STATE_OFF; - -retry: - if (!ctx->is_active) { - __perf_event_mark_enabled(event); - goto out; - } - raw_spin_unlock_irq(&ctx->lock); - if (!task_function_call(task, __perf_event_enable, event)) - return; - - raw_spin_lock_irq(&ctx->lock); - - /* - * If the context is active and the event is still off, - * we need to retry the cross-call. - */ - if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) { - /* - * task could have been flipped by a concurrent - * perf_event_context_sched_out() - */ - task = ctx->task; - goto retry; - } - -out: - raw_spin_unlock_irq(&ctx->lock); + event_function_call(event, __perf_event_enable, + ___perf_event_enable, event); } /* From 957ea1fdbcdb909e1540f06f06f1a9ce6e696efa Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 3 Dec 2015 13:22:19 -0800 Subject: [PATCH 199/271] perf/x86: Remove warning for zero PEBS status The recent commit: 75f80859b130 ("perf/x86/intel/pebs: Robustify PEBS buffer drain") causes lots of warnings on different CPUs before Skylake when running PEBS intensive workloads. They can have a zero status field in the PEBS record when PEBS is racing with clearing of GLOBAl_STATUS. This also can cause hangs (it seems there are still problems with printk in NMI). Disable the warning, but still ignore the record. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449177740-5422-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_ds.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 5db1c7755548..0e3a9c73187d 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -1232,10 +1232,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) bit = find_first_bit((unsigned long *)&pebs_status, x86_pmu.max_pebs_events); - if (WARN(bit >= x86_pmu.max_pebs_events, - "PEBS record without PEBS event! status=%Lx pebs_enabled=%Lx active_mask=%Lx", - (unsigned long long)p->status, (unsigned long long)cpuc->pebs_enabled, - *(unsigned long long *)cpuc->active_mask)) + if (bit >= x86_pmu.max_pebs_events) continue; /* From 01330d7288e0050c5aaabc558059ff91589e67cd Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 3 Dec 2015 13:22:20 -0800 Subject: [PATCH 200/271] perf/x86: Allow zero PEBS status with only single active event Normally we drop PEBS events with a zero status field. But when there is only a single PEBS event active we can assume the PEBS record is for that event. The PEBS buffer is always flushed when PEBS events are disabled, so there is no risk of mishandling state PEBS records this way. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449177740-5422-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_ds.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 0e3a9c73187d..cd1993e46d19 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -1230,6 +1230,18 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs) pebs_status = p->status & cpuc->pebs_enabled; pebs_status &= (1ULL << x86_pmu.max_pebs_events) - 1; + /* + * On some CPUs the PEBS status can be zero when PEBS is + * racing with clearing of GLOBAL_STATUS. + * + * Normally we would drop that record, but in the + * case when there is only a single active PEBS event + * we can assume it's for that event. + */ + if (!pebs_status && cpuc->pebs_enabled && + !(cpuc->pebs_enabled & (cpuc->pebs_enabled-1))) + pebs_status = cpuc->pebs_enabled; + bit = find_first_bit((unsigned long *)&pebs_status, x86_pmu.max_pebs_events); if (bit >= x86_pmu.max_pebs_events) From 442f5c74cbeaf54939980397ece59360c0a824e9 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 4 Dec 2015 03:50:32 -0800 Subject: [PATCH 201/271] perf/x86: Use INST_RETIRED.TOTAL_CYCLES_PS for cycles:pp for Skylake I added UOPS_RETIRED.ALL by mistake to the Skylake PEBS event list for cycles:pp. But the event is not documented for Skylake, and has some issues. The recommended replacement for cycles:pp is to use INST_RETIRED.ANY+pebs as a base, similar to what CPUs before Sandy Bridge did. This new event is called INST_RETIRED.TOTAL_CYCLES_PS. The event is not really new, but has been already used by perf before Sandy Bridge for the original cycles:p Note the SDM doesn't document that event either, but it's being documented in the latest version of the event list on: https://download.01.org/perfmon/SKL This patch does: - Remove UOPS_RETIRED.ALL from the Skylake PEBS event list - Add INST_RETIRED.ANY to the Skylake PEBS event list, and an table entry to allow cmask=16,inv=1 for cycles:pp - We don't need an extra entry for the base INST_RETIRED event, because it is already covered by the catch-all PEBS table entry. - Switch Skylake to use the Core2 PEBS alias (which is INST_RETIRED.TOTAL_CYCLES_PS) Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: hpa@zytor.com Link: http://lkml.kernel.org/r/1448929689-13771-1-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel.c | 2 +- arch/x86/kernel/cpu/perf_event_intel_ds.c | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 33b4b677bf6e..5ed6e0d8ab95 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -3521,7 +3521,7 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_skl_event_constraints; x86_pmu.pebs_constraints = intel_skl_pebs_event_constraints; x86_pmu.extra_regs = intel_skl_extra_regs; - x86_pmu.pebs_aliases = intel_pebs_aliases_snb; + x86_pmu.pebs_aliases = intel_pebs_aliases_core2; /* all extra regs are per-cpu when HT is on */ x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index cd1993e46d19..56b501532623 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -718,9 +718,8 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = { struct event_constraint intel_skl_pebs_event_constraints[] = { INTEL_FLAGS_UEVENT_CONSTRAINT(0x1c0, 0x2), /* INST_RETIRED.PREC_DIST */ - INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */ - /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ - INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), + /* INST_RETIRED.TOTAL_CYCLES_PS (inv=1, cmask=16) (cycles:p). */ + INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x0f), INTEL_PLD_CONSTRAINT(0x1cd, 0xf), /* MEM_TRANS_RETIRED.* */ INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_LD(0x11d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_LOADS */ INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_ST(0x12d0, 0xf), /* MEM_INST_RETIRED.STLB_MISS_STORES */ From 724697648eec540b2a7561089b1c87cb33e6a0eb Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 4 Dec 2015 03:50:52 -0800 Subject: [PATCH 202/271] perf/x86: Use INST_RETIRED.PREC_DIST for cycles: ppp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new 'three-p' precise level, that uses INST_RETIRED.PREC_DIST as base. The basic mechanism of abusing the inverse cmask to get all cycles works the same as before. PREC_DIST is available on Sandy Bridge or later. It had some problems on Sandy Bridge, so we only use it on IvyBridge and later. I tested it on Broadwell and Skylake. PREC_DIST has special support for avoiding shadow effects, which can give better results compare to UOPS_RETIRED. The drawback is that PREC_DIST can only schedule on counter 1, but that is ok for cycle sampling, as there is normally no need to do multiple cycle sampling runs in parallel. It is still possible to run perf top in parallel, as that doesn't use precise mode. Also of course the multiplexing can still allow parallel operation. :pp stays with the previous event. Example: Sample a loop with 10 sqrt with old cycles:pp 0.14 │10: sqrtps %xmm1,%xmm0 <-------------- 9.13 │ sqrtps %xmm1,%xmm0 11.58 │ sqrtps %xmm1,%xmm0 11.51 │ sqrtps %xmm1,%xmm0 6.27 │ sqrtps %xmm1,%xmm0 10.38 │ sqrtps %xmm1,%xmm0 12.20 │ sqrtps %xmm1,%xmm0 12.74 │ sqrtps %xmm1,%xmm0 5.40 │ sqrtps %xmm1,%xmm0 10.14 │ sqrtps %xmm1,%xmm0 10.51 │ ↑ jmp 10 We expect all 10 sqrt to get roughly the sample number of samples. But you can see that the instruction directly after the JMP is systematically underestimated in the result, due to sampling shadow effects. With the new PREC_DIST based sampling this problem is gone and all instructions show up roughly evenly: 9.51 │10: sqrtps %xmm1,%xmm0 11.74 │ sqrtps %xmm1,%xmm0 11.84 │ sqrtps %xmm1,%xmm0 6.05 │ sqrtps %xmm1,%xmm0 10.46 │ sqrtps %xmm1,%xmm0 12.25 │ sqrtps %xmm1,%xmm0 12.18 │ sqrtps %xmm1,%xmm0 5.26 │ sqrtps %xmm1,%xmm0 10.13 │ sqrtps %xmm1,%xmm0 10.43 │ sqrtps %xmm1,%xmm0 0.16 │ ↑ jmp 10 Even with PREC_DIST there is still sampling skid and the result is not completely even, but systematic shadow effects are significantly reduced. The improvements are mainly expected to make a difference in high IPC code. With low IPC it should be similar. Signed-off-by: Andi Kleen Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: hpa@zytor.com Link: http://lkml.kernel.org/r/1448929689-13771-2-git-send-email-andi@firstfloor.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 3 ++ arch/x86/kernel/cpu/perf_event.h | 3 +- arch/x86/kernel/cpu/perf_event_intel.c | 50 +++++++++++++++++++++-- arch/x86/kernel/cpu/perf_event_intel_ds.c | 6 +++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 9dfbba5ce6e8..e7e63a91ec05 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -482,6 +482,9 @@ int x86_pmu_hw_config(struct perf_event *event) /* Support for IP fixup */ if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2) precise++; + + if (x86_pmu.pebs_prec_dist) + precise++; } if (event->attr.precise_ip > precise) diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 799e6bd58cc1..ce8768f27714 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -583,7 +583,8 @@ struct x86_pmu { bts_active :1, pebs :1, pebs_active :1, - pebs_broken :1; + pebs_broken :1, + pebs_prec_dist :1; int pebs_record_size; void (*drain_pebs)(struct pt_regs *regs); struct event_constraint *pebs_constraints; diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 5ed6e0d8ab95..762c6023a97f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2475,6 +2475,44 @@ static void intel_pebs_aliases_snb(struct perf_event *event) } } +static void intel_pebs_aliases_precdist(struct perf_event *event) +{ + if ((event->hw.config & X86_RAW_EVENT_MASK) == 0x003c) { + /* + * Use an alternative encoding for CPU_CLK_UNHALTED.THREAD_P + * (0x003c) so that we can use it with PEBS. + * + * The regular CPU_CLK_UNHALTED.THREAD_P event (0x003c) isn't + * PEBS capable. However we can use INST_RETIRED.PREC_DIST + * (0x01c0), which is a PEBS capable event, to get the same + * count. + * + * The PREC_DIST event has special support to minimize sample + * shadowing effects. One drawback is that it can be + * only programmed on counter 1, but that seems like an + * acceptable trade off. + */ + u64 alt_config = X86_CONFIG(.event=0xc0, .umask=0x01, .inv=1, .cmask=16); + + alt_config |= (event->hw.config & ~X86_RAW_EVENT_MASK); + event->hw.config = alt_config; + } +} + +static void intel_pebs_aliases_ivb(struct perf_event *event) +{ + if (event->attr.precise_ip < 3) + return intel_pebs_aliases_snb(event); + return intel_pebs_aliases_precdist(event); +} + +static void intel_pebs_aliases_skl(struct perf_event *event) +{ + if (event->attr.precise_ip < 3) + return intel_pebs_aliases_core2(event); + return intel_pebs_aliases_precdist(event); +} + static unsigned long intel_pmu_free_running_flags(struct perf_event *event) { unsigned long flags = x86_pmu.free_running_flags; @@ -3431,7 +3469,8 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_ivb_event_constraints; x86_pmu.pebs_constraints = intel_ivb_pebs_event_constraints; - x86_pmu.pebs_aliases = intel_pebs_aliases_snb; + x86_pmu.pebs_aliases = intel_pebs_aliases_ivb; + x86_pmu.pebs_prec_dist = true; if (boot_cpu_data.x86_model == 62) x86_pmu.extra_regs = intel_snbep_extra_regs; else @@ -3464,7 +3503,8 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_hsw_event_constraints; x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints; x86_pmu.extra_regs = intel_snbep_extra_regs; - x86_pmu.pebs_aliases = intel_pebs_aliases_snb; + x86_pmu.pebs_aliases = intel_pebs_aliases_ivb; + x86_pmu.pebs_prec_dist = true; /* all extra regs are per-cpu when HT is on */ x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; @@ -3499,7 +3539,8 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_bdw_event_constraints; x86_pmu.pebs_constraints = intel_hsw_pebs_event_constraints; x86_pmu.extra_regs = intel_snbep_extra_regs; - x86_pmu.pebs_aliases = intel_pebs_aliases_snb; + x86_pmu.pebs_aliases = intel_pebs_aliases_ivb; + x86_pmu.pebs_prec_dist = true; /* all extra regs are per-cpu when HT is on */ x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; @@ -3521,7 +3562,8 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_skl_event_constraints; x86_pmu.pebs_constraints = intel_skl_pebs_event_constraints; x86_pmu.extra_regs = intel_skl_extra_regs; - x86_pmu.pebs_aliases = intel_pebs_aliases_core2; + x86_pmu.pebs_aliases = intel_pebs_aliases_skl; + x86_pmu.pebs_prec_dist = true; /* all extra regs are per-cpu when HT is on */ x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 56b501532623..9c0f8d464bc0 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -686,6 +686,8 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = { INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), + /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */ + INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2), INTEL_EXCLEVT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ INTEL_EXCLEVT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ INTEL_EXCLEVT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ @@ -700,6 +702,8 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = { INTEL_PLD_CONSTRAINT(0x01cd, 0xf), /* MEM_TRANS_RETIRED.* */ /* UOPS_RETIRED.ALL, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c2, 0xf), + /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */ + INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2), INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_NA(0x01c2, 0xf), /* UOPS_RETIRED.ALL */ INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x11d0, 0xf), /* MEM_UOPS_RETIRED.STLB_MISS_LOADS */ INTEL_FLAGS_UEVENT_CONSTRAINT_DATALA_XLD(0x21d0, 0xf), /* MEM_UOPS_RETIRED.LOCK_LOADS */ @@ -718,6 +722,8 @@ struct event_constraint intel_hsw_pebs_event_constraints[] = { struct event_constraint intel_skl_pebs_event_constraints[] = { INTEL_FLAGS_UEVENT_CONSTRAINT(0x1c0, 0x2), /* INST_RETIRED.PREC_DIST */ + /* INST_RETIRED.PREC_DIST, inv=1, cmask=16 (cycles:ppp). */ + INTEL_FLAGS_EVENT_CONSTRAINT(0x108001c0, 0x2), /* INST_RETIRED.TOTAL_CYCLES_PS (inv=1, cmask=16) (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x0f), INTEL_PLD_CONSTRAINT(0x1cd, 0xf), /* MEM_TRANS_RETIRED.* */ From 61b87cae6361ea6af161c1ffa549898892707b19 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Mon, 7 Dec 2015 20:33:25 +0100 Subject: [PATCH 203/271] perf/x86: Fix filter_events() bug with event mappings This patch fixes a bug in the filter_events() function. The patch fixes the bug whereby if some mappings did not exist, e.g., STALLED_CYCLES_FRONTEND, then any event after it in the attrs array would disappear from the published list of events in /sys/devices/cpu/events. This could be verified easily on any system post SNB (which do not publish STALLED_CYCLES_FRONTEND): $ ./perf stat -e cycles,ref-cycles true Performance counter stats for 'true': 1,217,348 cycles ref-cycles The problem is that in filter_events() there is an assumption that the argument (attrs) is organized in increasing continuous event indexes related to the event_map(). But if we remove the non-supported events by shifing the position in the array, then the lookup x86_pmu.event_map() needs to compensate for it, otherwise we are looking up the wrong index. This patch corrects this problem by compensating for the deleted events and with that ref-cycles reappears (here shown on Haswell): $ perf stat -e ref-cycles,cycles true Performance counter stats for 'true': 4,525,910 ref-cycles 1,064,920 cycles 0.002943888 seconds time elapsed Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vince Weaver Cc: jolsa@kernel.org Cc: kan.liang@intel.com Fixes: 8300daa26755 ("perf/x86: Filter out undefined events from sysfs events attribute") Link: http://lkml.kernel.org/r/1449516805-6637-1-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index e7e63a91ec05..1b443db2db50 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1534,6 +1534,7 @@ static void __init filter_events(struct attribute **attrs) { struct device_attribute *d; struct perf_pmu_events_attr *pmu_attr; + int offset = 0; int i, j; for (i = 0; attrs[i]; i++) { @@ -1542,7 +1543,7 @@ static void __init filter_events(struct attribute **attrs) /* str trumps id */ if (pmu_attr->event_str) continue; - if (x86_pmu.event_map(i)) + if (x86_pmu.event_map(i + offset)) continue; for (j = i; attrs[j]; j++) @@ -1550,6 +1551,14 @@ static void __init filter_events(struct attribute **attrs) /* Check the shifted attr. */ i--; + + /* + * event_map() is index based, the attrs array is organized + * by increasing event index. If we shift the events, then + * we need to compensate for the event_map(), otherwise + * we are looking up the wrong event in the map + */ + offset++; } } From 6fc2e83077b05a061afe9b24f2fdff7a0434eb67 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 3 Dec 2015 23:33:17 +0100 Subject: [PATCH 204/271] perf/x86: Fix LBR related crashes on Intel Atom This patches fixes the LBR kernel crashes on Intel Atom. The kernel was assuming that if the CPU supports 64-bit format LBR, then it has an LBR_SELECT MSR. Atom uses 64-bit LBR format but does not have LBR_SELECT. That was causing NULL pointer dereferences in a couple of places. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vince Weaver Cc: kan.liang@intel.com Fixes: 96f3eda67fcf ("perf/x86/intel: Fix static checker warning in lbr enable") Link: http://lkml.kernel.org/r/1449182000-31524-2-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_lbr.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index e2fad0cdca2f..1390148ee2e6 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -161,7 +161,7 @@ static void __intel_pmu_lbr_enable(bool pmi) */ if (cpuc->lbr_sel) lbr_select = cpuc->lbr_sel->config & x86_pmu.lbr_sel_mask; - if (!pmi) + if (!pmi && cpuc->lbr_sel) wrmsrl(MSR_LBR_SELECT, lbr_select); rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); @@ -430,7 +430,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) */ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) { - bool need_info = !(cpuc->lbr_sel->config & LBR_NO_INFO); + bool need_info = false; unsigned long mask = x86_pmu.lbr_nr - 1; int lbr_format = x86_pmu.intel_cap.lbr_format; u64 tos = intel_pmu_lbr_tos(); @@ -438,8 +438,11 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) int out = 0; int num = x86_pmu.lbr_nr; - if (cpuc->lbr_sel->config & LBR_CALL_STACK) - num = tos; + if (cpuc->lbr_sel) { + need_info = !(cpuc->lbr_sel->config & LBR_NO_INFO); + if (cpuc->lbr_sel->config & LBR_CALL_STACK) + num = tos; + } for (i = 0; i < num; i++) { unsigned long lbr_idx = (tos - i) & mask; From 1424a09a9e1839285e948d4ea9fdfca26c9a2086 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 3 Dec 2015 23:33:18 +0100 Subject: [PATCH 205/271] perf/x86: fix PEBS issues on Intel Atom/Core2 This patch fixes broken PEBS support on Intel Atom and Core2 due to wrong pointer arithmetic in intel_pmu_drain_pebs_core(). The get_next_pebs_record_by_bit() was called on PEBS format fmt0 which does not use the pebs_record_nhm layout. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vince Weaver Cc: kan.liang@intel.com Fixes: 21509084f999 ("perf/x86/intel: Handle multiple records in the PEBS buffer") Link: http://lkml.kernel.org/r/1449182000-31524-3-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_ds.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 9c0f8d464bc0..a7463ed0b40e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -1106,6 +1106,13 @@ get_next_pebs_record_by_bit(void *base, void *top, int bit) void *at; u64 pebs_status; + /* + * fmt0 does not have a status bitfield (does not use + * perf_record_nhm format) + */ + if (x86_pmu.intel_cap.pebs_format < 1) + return base; + if (base == NULL) return NULL; @@ -1191,7 +1198,7 @@ static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) if (!event->attr.precise_ip) return; - n = (top - at) / x86_pmu.pebs_record_size; + n = top - at; if (n <= 0) return; From 673d188ba5b1cef6f9a41a5a18b490b2831c3ea5 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Thu, 3 Dec 2015 21:03:10 +0100 Subject: [PATCH 206/271] perf/x86: Enable cycles:pp for Intel Atom This patch updates the PEBS support for Intel Atom to provide an alias for the cycles:pp event used by perf record/top by default nowadays. On Atom, only INST_RETIRED:ANY supports PEBS, so we use this event instead with a large cmask to count cycles. Given that Core2 has the same issue, we use the intel_pebs_aliases_core2() function for Atom as well. Signed-off-by: Stephane Eranian Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vince Weaver Cc: kan.liang@intel.com Link: http://lkml.kernel.org/r/1449172990-30183-3-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel.c | 1 + arch/x86/kernel/cpu/perf_event_intel_ds.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 762c6023a97f..95980c0b9561 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -3370,6 +3370,7 @@ __init int intel_pmu_init(void) x86_pmu.event_constraints = intel_gen_event_constraints; x86_pmu.pebs_constraints = intel_atom_pebs_event_constraints; + x86_pmu.pebs_aliases = intel_pebs_aliases_core2; pr_cont("Atom events, "); break; diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index a7463ed0b40e..10602f0a438f 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c @@ -620,6 +620,8 @@ struct event_constraint intel_atom_pebs_event_constraints[] = { INTEL_FLAGS_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED.* */ /* INST_RETIRED.ANY_P, inv=1, cmask=16 (cycles:p). */ INTEL_FLAGS_EVENT_CONSTRAINT(0x108000c0, 0x01), + /* Allow all events as PEBS with no flags */ + INTEL_ALL_EVENT_CONSTRAINT(0, 0x1), EVENT_CONSTRAINT_END }; From d3bcd64bbc35076a80c56918c905ddb167d097d8 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Fri, 4 Dec 2015 18:07:41 +0800 Subject: [PATCH 207/271] perf/x86/rapl: Use unified perf_event_sysfs_show instead of special interface Actually, rapl_sysfs_show is a duplicate of perf_event_sysfs_show. We prefer to use the unified interface. Signed-off-by: Huang Rui Signed-off-by: Peter Zijlstra (Intel) Cc: Andy Lutomirski Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Dasaratharaman Chandramouli Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Robert Richter Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449223661-2437-1-git-send-email-ray.huang@amd.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_rapl.c | 23 +++++---------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c index fb5843dc7b83..24a351ad628d 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c +++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c @@ -109,11 +109,11 @@ static struct kobj_attribute format_attr_##_var = \ #define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */ -#define RAPL_EVENT_ATTR_STR(_name, v, str) \ -static struct perf_pmu_events_attr event_attr_##v = { \ - .attr = __ATTR(_name, 0444, rapl_sysfs_show, NULL), \ - .id = 0, \ - .event_str = str, \ +#define RAPL_EVENT_ATTR_STR(_name, v, str) \ +static struct perf_pmu_events_attr event_attr_##v = { \ + .attr = __ATTR(_name, 0444, perf_event_sysfs_show, NULL), \ + .id = 0, \ + .event_str = str, \ }; struct rapl_pmu { @@ -405,19 +405,6 @@ static struct attribute_group rapl_pmu_attr_group = { .attrs = rapl_pmu_attrs, }; -static ssize_t rapl_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *page) -{ - struct perf_pmu_events_attr *pmu_attr = \ - container_of(attr, struct perf_pmu_events_attr, attr); - - if (pmu_attr->event_str) - return sprintf(page, "%s", pmu_attr->event_str); - - return 0; -} - RAPL_EVENT_ATTR_STR(energy-cores, rapl_cores, "event=0x01"); RAPL_EVENT_ATTR_STR(energy-pkg , rapl_pkg, "event=0x02"); RAPL_EVENT_ATTR_STR(energy-ram , rapl_ram, "event=0x03"); From d6980ef32570e2a26e05b1183788f4b70f1f27d0 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 3 Dec 2015 16:00:11 -0500 Subject: [PATCH 208/271] perf/x86/intel/uncore: Add Broadwell-EP uncore support The uncore subsystem for Broadwell-EP is similar to Haswell-EP. There are some differences in pci device IDs, box number and constraints. This patch extends the Broadwell-DE codes to support Broadwell-EP. Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/1449176411-9499-1-git-send-email-kan.liang@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_uncore.c | 2 + .../cpu/perf_event_intel_uncore_snbep.c | 152 +++++++++++++++++- 2 files changed, 149 insertions(+), 5 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index 61215a69b03d..b63271c09248 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -966,6 +966,7 @@ static int __init uncore_pci_init(void) case 63: /* Haswell-EP */ ret = hswep_uncore_pci_init(); break; + case 79: /* BDX-EP */ case 86: /* BDX-DE */ ret = bdx_uncore_pci_init(); break; @@ -1287,6 +1288,7 @@ static int __init uncore_cpu_init(void) case 63: /* Haswell-EP */ hswep_uncore_cpu_init(); break; + case 79: /* BDX-EP */ case 86: /* BDX-DE */ bdx_uncore_cpu_init(); break; diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index f0f4fcba252e..f2ddfccfaa36 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -2338,7 +2338,7 @@ int hswep_uncore_pci_init(void) } /* end of Haswell-EP uncore support */ -/* BDX-DE uncore support */ +/* BDX uncore support */ static struct intel_uncore_type bdx_uncore_ubox = { .name = "ubox", @@ -2360,13 +2360,14 @@ static struct event_constraint bdx_uncore_cbox_constraints[] = { UNCORE_EVENT_CONSTRAINT(0x09, 0x3), UNCORE_EVENT_CONSTRAINT(0x11, 0x1), UNCORE_EVENT_CONSTRAINT(0x36, 0x1), + UNCORE_EVENT_CONSTRAINT(0x3e, 0x1), EVENT_CONSTRAINT_END }; static struct intel_uncore_type bdx_uncore_cbox = { .name = "cbox", .num_counters = 4, - .num_boxes = 8, + .num_boxes = 24, .perf_ctr_bits = 48, .event_ctl = HSWEP_C0_MSR_PMON_CTL0, .perf_ctr = HSWEP_C0_MSR_PMON_CTR0, @@ -2379,9 +2380,24 @@ static struct intel_uncore_type bdx_uncore_cbox = { .format_group = &hswep_uncore_cbox_format_group, }; +static struct intel_uncore_type bdx_uncore_sbox = { + .name = "sbox", + .num_counters = 4, + .num_boxes = 4, + .perf_ctr_bits = 48, + .event_ctl = HSWEP_S0_MSR_PMON_CTL0, + .perf_ctr = HSWEP_S0_MSR_PMON_CTR0, + .event_mask = HSWEP_S_MSR_PMON_RAW_EVENT_MASK, + .box_ctl = HSWEP_S0_MSR_PMON_BOX_CTL, + .msr_offset = HSWEP_SBOX_MSR_OFFSET, + .ops = &hswep_uncore_sbox_msr_ops, + .format_group = &hswep_uncore_sbox_format_group, +}; + static struct intel_uncore_type *bdx_msr_uncores[] = { &bdx_uncore_ubox, &bdx_uncore_cbox, + &bdx_uncore_sbox, &hswep_uncore_pcu, NULL, }; @@ -2396,7 +2412,7 @@ void bdx_uncore_cpu_init(void) static struct intel_uncore_type bdx_uncore_ha = { .name = "ha", .num_counters = 4, - .num_boxes = 1, + .num_boxes = 2, .perf_ctr_bits = 48, SNBEP_UNCORE_PCI_COMMON_INIT(), }; @@ -2404,7 +2420,7 @@ static struct intel_uncore_type bdx_uncore_ha = { static struct intel_uncore_type bdx_uncore_imc = { .name = "imc", .num_counters = 5, - .num_boxes = 2, + .num_boxes = 8, .perf_ctr_bits = 48, .fixed_ctr_bits = 48, .fixed_ctr = SNBEP_MC_CHy_PCI_PMON_FIXED_CTR, @@ -2424,6 +2440,19 @@ static struct intel_uncore_type bdx_uncore_irp = { .format_group = &snbep_uncore_format_group, }; +static struct intel_uncore_type bdx_uncore_qpi = { + .name = "qpi", + .num_counters = 4, + .num_boxes = 3, + .perf_ctr_bits = 48, + .perf_ctr = SNBEP_PCI_PMON_CTR0, + .event_ctl = SNBEP_PCI_PMON_CTL0, + .event_mask = SNBEP_QPI_PCI_PMON_RAW_EVENT_MASK, + .box_ctl = SNBEP_PCI_PMON_BOX_CTL, + .num_shared_regs = 1, + .ops = &snbep_uncore_qpi_ops, + .format_group = &snbep_uncore_qpi_format_group, +}; static struct event_constraint bdx_uncore_r2pcie_constraints[] = { UNCORE_EVENT_CONSTRAINT(0x10, 0x3), @@ -2432,6 +2461,8 @@ static struct event_constraint bdx_uncore_r2pcie_constraints[] = { UNCORE_EVENT_CONSTRAINT(0x23, 0x1), UNCORE_EVENT_CONSTRAINT(0x25, 0x1), UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x28, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), EVENT_CONSTRAINT_END }; @@ -2445,18 +2476,65 @@ static struct intel_uncore_type bdx_uncore_r2pcie = { SNBEP_UNCORE_PCI_COMMON_INIT(), }; +static struct event_constraint bdx_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x7), + UNCORE_EVENT_CONSTRAINT(0x07, 0x7), + UNCORE_EVENT_CONSTRAINT(0x08, 0x7), + UNCORE_EVENT_CONSTRAINT(0x09, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0a, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0e, 0x7), + UNCORE_EVENT_CONSTRAINT(0x10, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), + UNCORE_EVENT_CONSTRAINT(0x14, 0x3), + UNCORE_EVENT_CONSTRAINT(0x15, 0x3), + UNCORE_EVENT_CONSTRAINT(0x1f, 0x3), + UNCORE_EVENT_CONSTRAINT(0x20, 0x3), + UNCORE_EVENT_CONSTRAINT(0x21, 0x3), + UNCORE_EVENT_CONSTRAINT(0x22, 0x3), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x25, 0x3), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x28, 0x3), + UNCORE_EVENT_CONSTRAINT(0x29, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), + UNCORE_EVENT_CONSTRAINT(0x33, 0x3), + UNCORE_EVENT_CONSTRAINT(0x34, 0x3), + UNCORE_EVENT_CONSTRAINT(0x36, 0x3), + UNCORE_EVENT_CONSTRAINT(0x37, 0x3), + UNCORE_EVENT_CONSTRAINT(0x38, 0x3), + UNCORE_EVENT_CONSTRAINT(0x39, 0x3), + EVENT_CONSTRAINT_END +}; + +static struct intel_uncore_type bdx_uncore_r3qpi = { + .name = "r3qpi", + .num_counters = 3, + .num_boxes = 3, + .perf_ctr_bits = 48, + .constraints = bdx_uncore_r3qpi_constraints, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + enum { BDX_PCI_UNCORE_HA, BDX_PCI_UNCORE_IMC, BDX_PCI_UNCORE_IRP, + BDX_PCI_UNCORE_QPI, BDX_PCI_UNCORE_R2PCIE, + BDX_PCI_UNCORE_R3QPI, }; static struct intel_uncore_type *bdx_pci_uncores[] = { [BDX_PCI_UNCORE_HA] = &bdx_uncore_ha, [BDX_PCI_UNCORE_IMC] = &bdx_uncore_imc, [BDX_PCI_UNCORE_IRP] = &bdx_uncore_irp, + [BDX_PCI_UNCORE_QPI] = &bdx_uncore_qpi, [BDX_PCI_UNCORE_R2PCIE] = &bdx_uncore_r2pcie, + [BDX_PCI_UNCORE_R3QPI] = &bdx_uncore_r3qpi, NULL, }; @@ -2465,6 +2543,10 @@ static const struct pci_device_id bdx_uncore_pci_ids[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f30), .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_HA, 0), }, + { /* Home Agent 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f38), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_HA, 1), + }, { /* MC0 Channel 0 */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb0), .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 0), @@ -2473,14 +2555,74 @@ static const struct pci_device_id bdx_uncore_pci_ids[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb1), .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 1), }, + { /* MC0 Channel 2 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb4), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 2), + }, + { /* MC0 Channel 3 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fb5), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 3), + }, + { /* MC1 Channel 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd0), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 4), + }, + { /* MC1 Channel 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd1), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 5), + }, + { /* MC1 Channel 2 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd4), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 6), + }, + { /* MC1 Channel 3 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6fd5), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IMC, 7), + }, { /* IRP */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f39), .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_IRP, 0), }, + { /* QPI0 Port 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f32), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 0), + }, + { /* QPI0 Port 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f33), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 1), + }, + { /* QPI1 Port 2 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f3a), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_QPI, 2), + }, { /* R2PCIe */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f34), .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R2PCIE, 0), }, + { /* R3QPI0 Link 0 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f36), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 0), + }, + { /* R3QPI0 Link 1 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f37), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 1), + }, + { /* R3QPI1 Link 2 */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f3e), + .driver_data = UNCORE_PCI_DEV_DATA(BDX_PCI_UNCORE_R3QPI, 2), + }, + { /* QPI Port 0 filter */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f86), + .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 0), + }, + { /* QPI Port 1 filter */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f96), + .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 1), + }, + { /* QPI Port 2 filter */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x6f46), + .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, 2), + }, { /* end: all zeroes */ } }; @@ -2500,4 +2642,4 @@ int bdx_uncore_pci_init(void) return 0; } -/* end of BDX-DE uncore support */ +/* end of BDX uncore support */ From 1e7b93906249a7ccca730be03168ace15f95709e Mon Sep 17 00:00:00 2001 From: Harish Chegondi Date: Mon, 7 Dec 2015 14:28:18 -0800 Subject: [PATCH 209/271] perf/x86/intel: Add perf core PMU support for Intel Knights Landing Knights Landing core is based on Silvermont core with several differences. Like Silvermont, Knights Landing has 8 pairs of LBR MSRs. However, the LBR MSRs addresses match those of the Xeon cores' first 8 pairs of LBR MSRs Unlike Silvermont, Knights Landing supports hyperthreading. Knights Landing offcore response events config register mask is different from that of the Silvermont. This patch was developed based on a patch from Andi Kleen. For more details, please refer to the public document: https://software.intel.com/sites/default/files/managed/15/8d/IntelXeonPhi%E2%84%A2x200ProcessorPerformanceMonitoringReferenceManual_Volume1_Registers_v0%206.pdf Signed-off-by: Harish Chegondi Signed-off-by: Peter Zijlstra (Intel) Cc: Andi Kleen Cc: Arnaldo Carvalho de Melo Cc: Harish Chegondi Cc: Jiri Olsa Cc: Kan Liang Cc: Linus Torvalds Cc: Lukasz Anaczkowski Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/d14593c7311f78c93c9cf6b006be843777c5ad5c.1449517401.git.harish.chegondi@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_intel.c | 62 ++++++++++++++++++++++ arch/x86/kernel/cpu/perf_event_intel_lbr.c | 14 +++++ 3 files changed, 78 insertions(+) diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index ce8768f27714..7bb61e32fb29 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h @@ -902,6 +902,8 @@ void intel_pmu_lbr_init_hsw(void); void intel_pmu_lbr_init_skl(void); +void intel_pmu_lbr_init_knl(void); + int intel_pmu_setup_lbr_filter(struct perf_event *event); void intel_pt_interrupt(void); diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 95980c0b9561..a667078a5180 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -185,6 +185,14 @@ struct event_constraint intel_skl_event_constraints[] = { EVENT_CONSTRAINT_END }; +static struct extra_reg intel_knl_extra_regs[] __read_mostly = { + INTEL_UEVENT_EXTRA_REG(0x01b7, + MSR_OFFCORE_RSP_0, 0x7f9ffbffffull, RSP_0), + INTEL_UEVENT_EXTRA_REG(0x02b7, + MSR_OFFCORE_RSP_1, 0x3f9ffbffffull, RSP_1), + EVENT_EXTRA_END +}; + static struct extra_reg intel_snb_extra_regs[] __read_mostly = { /* must define OFFCORE_RSP_X first, see intel_fixup_er() */ INTEL_UEVENT_EXTRA_REG(0x01b7, MSR_OFFCORE_RSP_0, 0x3f807f8fffull, RSP_0), @@ -1457,6 +1465,42 @@ static __initconst const u64 slm_hw_cache_event_ids }, }; +#define KNL_OT_L2_HITE BIT_ULL(19) /* Other Tile L2 Hit */ +#define KNL_OT_L2_HITF BIT_ULL(20) /* Other Tile L2 Hit */ +#define KNL_MCDRAM_LOCAL BIT_ULL(21) +#define KNL_MCDRAM_FAR BIT_ULL(22) +#define KNL_DDR_LOCAL BIT_ULL(23) +#define KNL_DDR_FAR BIT_ULL(24) +#define KNL_DRAM_ANY (KNL_MCDRAM_LOCAL | KNL_MCDRAM_FAR | \ + KNL_DDR_LOCAL | KNL_DDR_FAR) +#define KNL_L2_READ SLM_DMND_READ +#define KNL_L2_WRITE SLM_DMND_WRITE +#define KNL_L2_PREFETCH SLM_DMND_PREFETCH +#define KNL_L2_ACCESS SLM_LLC_ACCESS +#define KNL_L2_MISS (KNL_OT_L2_HITE | KNL_OT_L2_HITF | \ + KNL_DRAM_ANY | SNB_SNP_ANY | \ + SNB_NON_DRAM) + +static __initconst const u64 knl_hw_cache_extra_regs + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { + [C(LL)] = { + [C(OP_READ)] = { + [C(RESULT_ACCESS)] = KNL_L2_READ | KNL_L2_ACCESS, + [C(RESULT_MISS)] = 0, + }, + [C(OP_WRITE)] = { + [C(RESULT_ACCESS)] = KNL_L2_WRITE | KNL_L2_ACCESS, + [C(RESULT_MISS)] = KNL_L2_WRITE | KNL_L2_MISS, + }, + [C(OP_PREFETCH)] = { + [C(RESULT_ACCESS)] = KNL_L2_PREFETCH | KNL_L2_ACCESS, + [C(RESULT_MISS)] = KNL_L2_PREFETCH | KNL_L2_MISS, + }, + }, +}; + /* * Use from PMIs where the LBRs are already disabled. */ @@ -3553,6 +3597,24 @@ __init int intel_pmu_init(void) pr_cont("Broadwell events, "); break; + case 87: /* Knights Landing Xeon Phi */ + memcpy(hw_cache_event_ids, + slm_hw_cache_event_ids, sizeof(hw_cache_event_ids)); + memcpy(hw_cache_extra_regs, + knl_hw_cache_extra_regs, sizeof(hw_cache_extra_regs)); + intel_pmu_lbr_init_knl(); + + x86_pmu.event_constraints = intel_slm_event_constraints; + x86_pmu.pebs_constraints = intel_slm_pebs_event_constraints; + x86_pmu.extra_regs = intel_knl_extra_regs; + + /* all extra regs are per-cpu when HT is on */ + x86_pmu.flags |= PMU_FL_HAS_RSP_1; + x86_pmu.flags |= PMU_FL_NO_HT_SHARING; + + pr_cont("Knights Landing events, "); + break; + case 78: /* 14nm Skylake Mobile */ case 94: /* 14nm Skylake Desktop */ x86_pmu.late_ack = true; diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index 1390148ee2e6..653f88d25987 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -1046,3 +1046,17 @@ void __init intel_pmu_lbr_init_atom(void) */ pr_cont("8-deep LBR, "); } + +/* Knights Landing */ +void intel_pmu_lbr_init_knl(void) +{ + x86_pmu.lbr_nr = 8; + x86_pmu.lbr_tos = MSR_LBR_TOS; + x86_pmu.lbr_from = MSR_LBR_NHM_FROM; + x86_pmu.lbr_to = MSR_LBR_NHM_TO; + + x86_pmu.lbr_sel_mask = LBR_SEL_MASK; + x86_pmu.lbr_sel_map = snb_lbr_sel_map; + + pr_cont("8-deep LBR, "); +} From dae25530a44ad9e6523495ebc8b37bb0a1640490 Mon Sep 17 00:00:00 2001 From: Harish Chegondi Date: Mon, 7 Dec 2015 14:32:31 -0800 Subject: [PATCH 210/271] perf/x86/intel/uncore: Remove hard coding of PMON box control MSR offset Call uncore_pci_box_ctl() function to get the PMON box control MSR offset instead of hard coding the offset. This would allow us to use this snbep_uncore_pci_init_box() function for other PCI PMON devices whose box control MSR offset is different from SNBEP_PCI_PMON_BOX_CTL. Signed-off-by: Harish Chegondi Signed-off-by: Peter Zijlstra (Intel) Cc: Andi Kleen Cc: Arnaldo Carvalho de Melo Cc: Harish Chegondi Cc: Jiri Olsa Cc: Kan Liang Cc: Linus Torvalds Cc: Lukasz Anaczkowski Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/872e8ef16cfc38e5ff3b45fac1094e6f1722e4ad.1449470704.git.harish.chegondi@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index f2ddfccfaa36..bfb96566311c 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -315,8 +315,9 @@ static u64 snbep_uncore_pci_read_counter(struct intel_uncore_box *box, struct pe static void snbep_uncore_pci_init_box(struct intel_uncore_box *box) { struct pci_dev *pdev = box->pci_dev; + int box_ctl = uncore_pci_box_ctl(box); - pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, SNBEP_PMON_BOX_CTL_INT); + pci_write_config_dword(pdev, box_ctl, SNBEP_PMON_BOX_CTL_INT); } static void snbep_uncore_msr_disable_box(struct intel_uncore_box *box) From 77af0037de0a280eeabc632890de871f062ea7be Mon Sep 17 00:00:00 2001 From: Harish Chegondi Date: Mon, 7 Dec 2015 14:32:32 -0800 Subject: [PATCH 211/271] perf/x86/intel/uncore: Add Knights Landing uncore PMU support Knights Landing uncore performance monitoring (perfmon) is derived from Haswell-EP uncore perfmon with several differences. One notable difference is in PCI device IDs. Knights Landing uses common PCI device ID for multiple instances of an uncore PMU device type. In Haswell-EP, each instance of a PMU device type has a unique device ID. Knights Landing uncore components that have performance monitoring units are UBOX, CHA, EDC, MC, M2PCIe, IRP and PCU. Perfmon registers in EDC, MC, IRP, and M2PCIe reside in the PCIe configuration space. Perfmon registers in UBOX, CHA and PCU are accessed via the MSR interface. For more details, please refer to the public document: https://software.intel.com/sites/default/files/managed/15/8d/IntelXeonPhi%E2%84%A2x200ProcessorPerformanceMonitoringReferenceManual_Volume1_Registers_v0%206.pdf Signed-off-by: Harish Chegondi Signed-off-by: Peter Zijlstra (Intel) Cc: Andi Kleen Cc: Arnaldo Carvalho de Melo Cc: Harish Chegondi Cc: Jiri Olsa Cc: Kan Liang Cc: Linus Torvalds Cc: Lukasz Anaczkowski Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Link: http://lkml.kernel.org/r/8ac513981264c3eb10343a3f523f19cc5a2d12fe.1449470704.git.harish.chegondi@intel.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_intel_uncore.c | 15 + arch/x86/kernel/cpu/perf_event_intel_uncore.h | 3 + .../kernel/cpu/perf_event_intel_uncore_snb.c | 2 +- .../cpu/perf_event_intel_uncore_snbep.c | 480 ++++++++++++++++++ 4 files changed, 499 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index b63271c09248..f97f8075bf04 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c @@ -884,6 +884,15 @@ static int uncore_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id * each box has a different function id. */ pmu = &type->pmus[UNCORE_PCI_DEV_IDX(id->driver_data)]; + /* Knights Landing uses a common PCI device ID for multiple instances of + * an uncore PMU device type. There is only one entry per device type in + * the knl_uncore_pci_ids table inspite of multiple devices present for + * some device types. Hence PCI device idx would be 0 for all devices. + * So increment pmu pointer to point to an unused array element. + */ + if (boot_cpu_data.x86_model == 87) + while (pmu->func_id >= 0) + pmu++; if (pmu->func_id < 0) pmu->func_id = pdev->devfn; else @@ -983,6 +992,9 @@ static int __init uncore_pci_init(void) case 61: /* Broadwell */ ret = bdw_uncore_pci_init(); break; + case 87: /* Knights Landing */ + ret = knl_uncore_pci_init(); + break; default: return 0; } @@ -1292,6 +1304,9 @@ static int __init uncore_cpu_init(void) case 86: /* BDX-DE */ bdx_uncore_cpu_init(); break; + case 87: /* Knights Landing */ + knl_uncore_cpu_init(); + break; default: return 0; } diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index 2f0a4a98e16b..07aa2d6bd710 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h @@ -338,6 +338,7 @@ int hsw_uncore_pci_init(void); int bdw_uncore_pci_init(void); void snb_uncore_cpu_init(void); void nhm_uncore_cpu_init(void); +int snb_pci2phy_map_init(int devid); /* perf_event_intel_uncore_snbep.c */ int snbep_uncore_pci_init(void); @@ -348,6 +349,8 @@ int hswep_uncore_pci_init(void); void hswep_uncore_cpu_init(void); int bdx_uncore_pci_init(void); void bdx_uncore_cpu_init(void); +int knl_uncore_pci_init(void); +void knl_uncore_cpu_init(void); /* perf_event_intel_uncore_nhmex.c */ void nhmex_uncore_cpu_init(void); diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c index 845256158a10..0b934820fafd 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snb.c @@ -417,7 +417,7 @@ static void snb_uncore_imc_event_del(struct perf_event *event, int flags) } } -static int snb_pci2phy_map_init(int devid) +int snb_pci2phy_map_init(int devid) { struct pci_dev *dev = NULL; struct pci2phy_map *map; diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index bfb96566311c..33acb884ccf1 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c @@ -209,31 +209,98 @@ #define HSWEP_PCU_MSR_PMON_BOX_CTL 0x710 #define HSWEP_PCU_MSR_PMON_BOX_FILTER 0x715 +/* KNL Ubox */ +#define KNL_U_MSR_PMON_RAW_EVENT_MASK \ + (SNBEP_U_MSR_PMON_RAW_EVENT_MASK | \ + SNBEP_CBO_PMON_CTL_TID_EN) +/* KNL CHA */ +#define KNL_CHA_MSR_OFFSET 0xc +#define KNL_CHA_MSR_PMON_CTL_QOR (1 << 16) +#define KNL_CHA_MSR_PMON_RAW_EVENT_MASK \ + (SNBEP_CBO_MSR_PMON_RAW_EVENT_MASK | \ + KNL_CHA_MSR_PMON_CTL_QOR) +#define KNL_CHA_MSR_PMON_BOX_FILTER_TID 0x1ff +#define KNL_CHA_MSR_PMON_BOX_FILTER_STATE (7 << 18) +#define KNL_CHA_MSR_PMON_BOX_FILTER_OP (0xfffffe2aULL << 32) + +/* KNL EDC/MC UCLK */ +#define KNL_UCLK_MSR_PMON_CTR0_LOW 0x400 +#define KNL_UCLK_MSR_PMON_CTL0 0x420 +#define KNL_UCLK_MSR_PMON_BOX_CTL 0x430 +#define KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW 0x44c +#define KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL 0x454 +#define KNL_PMON_FIXED_CTL_EN 0x1 + +/* KNL EDC */ +#define KNL_EDC0_ECLK_MSR_PMON_CTR0_LOW 0xa00 +#define KNL_EDC0_ECLK_MSR_PMON_CTL0 0xa20 +#define KNL_EDC0_ECLK_MSR_PMON_BOX_CTL 0xa30 +#define KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_LOW 0xa3c +#define KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_CTL 0xa44 + +/* KNL MC */ +#define KNL_MC0_CH0_MSR_PMON_CTR0_LOW 0xb00 +#define KNL_MC0_CH0_MSR_PMON_CTL0 0xb20 +#define KNL_MC0_CH0_MSR_PMON_BOX_CTL 0xb30 +#define KNL_MC0_CH0_MSR_PMON_FIXED_LOW 0xb3c +#define KNL_MC0_CH0_MSR_PMON_FIXED_CTL 0xb44 + +/* KNL IRP */ +#define KNL_IRP_PCI_PMON_BOX_CTL 0xf0 +#define KNL_IRP_PCI_PMON_RAW_EVENT_MASK (SNBEP_PMON_RAW_EVENT_MASK | \ + KNL_CHA_MSR_PMON_CTL_QOR) +/* KNL PCU */ +#define KNL_PCU_PMON_CTL_EV_SEL_MASK 0x0000007f +#define KNL_PCU_PMON_CTL_USE_OCC_CTR (1 << 7) +#define KNL_PCU_MSR_PMON_CTL_TRESH_MASK 0x3f000000 +#define KNL_PCU_MSR_PMON_RAW_EVENT_MASK \ + (KNL_PCU_PMON_CTL_EV_SEL_MASK | \ + KNL_PCU_PMON_CTL_USE_OCC_CTR | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \ + SNBEP_PMON_CTL_EDGE_DET | \ + SNBEP_CBO_PMON_CTL_TID_EN | \ + SNBEP_PMON_CTL_EV_SEL_EXT | \ + SNBEP_PMON_CTL_INVERT | \ + KNL_PCU_MSR_PMON_CTL_TRESH_MASK | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \ + SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET) DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); +DEFINE_UNCORE_FORMAT_ATTR(event2, event, "config:0-6"); DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); +DEFINE_UNCORE_FORMAT_ATTR(use_occ_ctr, use_occ_ctr, "config:7"); DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); +DEFINE_UNCORE_FORMAT_ATTR(qor, qor, "config:16"); DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18"); DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19"); DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23"); DEFINE_UNCORE_FORMAT_ATTR(thresh8, thresh, "config:24-31"); +DEFINE_UNCORE_FORMAT_ATTR(thresh6, thresh, "config:24-29"); DEFINE_UNCORE_FORMAT_ATTR(thresh5, thresh, "config:24-28"); DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15"); DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); +DEFINE_UNCORE_FORMAT_ATTR(occ_edge_det, occ_edge_det, "config:31"); DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4"); DEFINE_UNCORE_FORMAT_ATTR(filter_tid2, filter_tid, "config1:0"); DEFINE_UNCORE_FORMAT_ATTR(filter_tid3, filter_tid, "config1:0-5"); +DEFINE_UNCORE_FORMAT_ATTR(filter_tid4, filter_tid, "config1:0-8"); DEFINE_UNCORE_FORMAT_ATTR(filter_cid, filter_cid, "config1:5"); DEFINE_UNCORE_FORMAT_ATTR(filter_link, filter_link, "config1:5-8"); DEFINE_UNCORE_FORMAT_ATTR(filter_link2, filter_link, "config1:6-8"); +DEFINE_UNCORE_FORMAT_ATTR(filter_link3, filter_link, "config1:12"); DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17"); DEFINE_UNCORE_FORMAT_ATTR(filter_nid2, filter_nid, "config1:32-47"); DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22"); DEFINE_UNCORE_FORMAT_ATTR(filter_state2, filter_state, "config1:17-22"); DEFINE_UNCORE_FORMAT_ATTR(filter_state3, filter_state, "config1:17-23"); +DEFINE_UNCORE_FORMAT_ATTR(filter_state4, filter_state, "config1:18-20"); +DEFINE_UNCORE_FORMAT_ATTR(filter_local, filter_local, "config1:33"); +DEFINE_UNCORE_FORMAT_ATTR(filter_all_op, filter_all_op, "config1:35"); +DEFINE_UNCORE_FORMAT_ATTR(filter_nnm, filter_nnm, "config1:37"); DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31"); DEFINE_UNCORE_FORMAT_ATTR(filter_opc2, filter_opc, "config1:52-60"); +DEFINE_UNCORE_FORMAT_ATTR(filter_opc3, filter_opc, "config1:41-60"); DEFINE_UNCORE_FORMAT_ATTR(filter_nc, filter_nc, "config1:62"); DEFINE_UNCORE_FORMAT_ATTR(filter_c6, filter_c6, "config1:61"); DEFINE_UNCORE_FORMAT_ATTR(filter_isoc, filter_isoc, "config1:63"); @@ -1729,6 +1796,419 @@ int ivbep_uncore_pci_init(void) } /* end of IvyTown uncore support */ +/* KNL uncore support */ +static struct attribute *knl_uncore_ubox_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_edge.attr, + &format_attr_tid_en.attr, + &format_attr_inv.attr, + &format_attr_thresh5.attr, + NULL, +}; + +static struct attribute_group knl_uncore_ubox_format_group = { + .name = "format", + .attrs = knl_uncore_ubox_formats_attr, +}; + +static struct intel_uncore_type knl_uncore_ubox = { + .name = "ubox", + .num_counters = 2, + .num_boxes = 1, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .perf_ctr = HSWEP_U_MSR_PMON_CTR0, + .event_ctl = HSWEP_U_MSR_PMON_CTL0, + .event_mask = KNL_U_MSR_PMON_RAW_EVENT_MASK, + .fixed_ctr = HSWEP_U_MSR_PMON_UCLK_FIXED_CTR, + .fixed_ctl = HSWEP_U_MSR_PMON_UCLK_FIXED_CTL, + .ops = &snbep_uncore_msr_ops, + .format_group = &knl_uncore_ubox_format_group, +}; + +static struct attribute *knl_uncore_cha_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_qor.attr, + &format_attr_edge.attr, + &format_attr_tid_en.attr, + &format_attr_inv.attr, + &format_attr_thresh8.attr, + &format_attr_filter_tid4.attr, + &format_attr_filter_link3.attr, + &format_attr_filter_state4.attr, + &format_attr_filter_local.attr, + &format_attr_filter_all_op.attr, + &format_attr_filter_nnm.attr, + &format_attr_filter_opc3.attr, + &format_attr_filter_nc.attr, + &format_attr_filter_isoc.attr, + NULL, +}; + +static struct attribute_group knl_uncore_cha_format_group = { + .name = "format", + .attrs = knl_uncore_cha_formats_attr, +}; + +static struct event_constraint knl_uncore_cha_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x11, 0x1), + UNCORE_EVENT_CONSTRAINT(0x1f, 0x1), + UNCORE_EVENT_CONSTRAINT(0x36, 0x1), + EVENT_CONSTRAINT_END +}; + +static struct extra_reg knl_uncore_cha_extra_regs[] = { + SNBEP_CBO_EVENT_EXTRA_REG(SNBEP_CBO_PMON_CTL_TID_EN, + SNBEP_CBO_PMON_CTL_TID_EN, 0x1), + SNBEP_CBO_EVENT_EXTRA_REG(0x3d, 0xff, 0x2), + SNBEP_CBO_EVENT_EXTRA_REG(0x35, 0xff, 0x4), + SNBEP_CBO_EVENT_EXTRA_REG(0x36, 0xff, 0x4), + EVENT_EXTRA_END +}; + +static u64 knl_cha_filter_mask(int fields) +{ + u64 mask = 0; + + if (fields & 0x1) + mask |= KNL_CHA_MSR_PMON_BOX_FILTER_TID; + if (fields & 0x2) + mask |= KNL_CHA_MSR_PMON_BOX_FILTER_STATE; + if (fields & 0x4) + mask |= KNL_CHA_MSR_PMON_BOX_FILTER_OP; + return mask; +} + +static struct event_constraint * +knl_cha_get_constraint(struct intel_uncore_box *box, struct perf_event *event) +{ + return __snbep_cbox_get_constraint(box, event, knl_cha_filter_mask); +} + +static int knl_cha_hw_config(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; + struct extra_reg *er; + int idx = 0; + + for (er = knl_uncore_cha_extra_regs; er->msr; er++) { + if (er->event != (event->hw.config & er->config_mask)) + continue; + idx |= er->idx; + } + + if (idx) { + reg1->reg = HSWEP_C0_MSR_PMON_BOX_FILTER0 + + KNL_CHA_MSR_OFFSET * box->pmu->pmu_idx; + reg1->config = event->attr.config1 & knl_cha_filter_mask(idx); + reg1->idx = idx; + } + return 0; +} + +static void hswep_cbox_enable_event(struct intel_uncore_box *box, + struct perf_event *event); + +static struct intel_uncore_ops knl_uncore_cha_ops = { + .init_box = snbep_uncore_msr_init_box, + .disable_box = snbep_uncore_msr_disable_box, + .enable_box = snbep_uncore_msr_enable_box, + .disable_event = snbep_uncore_msr_disable_event, + .enable_event = hswep_cbox_enable_event, + .read_counter = uncore_msr_read_counter, + .hw_config = knl_cha_hw_config, + .get_constraint = knl_cha_get_constraint, + .put_constraint = snbep_cbox_put_constraint, +}; + +static struct intel_uncore_type knl_uncore_cha = { + .name = "cha", + .num_counters = 4, + .num_boxes = 38, + .perf_ctr_bits = 48, + .event_ctl = HSWEP_C0_MSR_PMON_CTL0, + .perf_ctr = HSWEP_C0_MSR_PMON_CTR0, + .event_mask = KNL_CHA_MSR_PMON_RAW_EVENT_MASK, + .box_ctl = HSWEP_C0_MSR_PMON_BOX_CTL, + .msr_offset = KNL_CHA_MSR_OFFSET, + .num_shared_regs = 1, + .constraints = knl_uncore_cha_constraints, + .ops = &knl_uncore_cha_ops, + .format_group = &knl_uncore_cha_format_group, +}; + +static struct attribute *knl_uncore_pcu_formats_attr[] = { + &format_attr_event2.attr, + &format_attr_use_occ_ctr.attr, + &format_attr_occ_sel.attr, + &format_attr_edge.attr, + &format_attr_tid_en.attr, + &format_attr_inv.attr, + &format_attr_thresh6.attr, + &format_attr_occ_invert.attr, + &format_attr_occ_edge_det.attr, + NULL, +}; + +static struct attribute_group knl_uncore_pcu_format_group = { + .name = "format", + .attrs = knl_uncore_pcu_formats_attr, +}; + +static struct intel_uncore_type knl_uncore_pcu = { + .name = "pcu", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 48, + .perf_ctr = HSWEP_PCU_MSR_PMON_CTR0, + .event_ctl = HSWEP_PCU_MSR_PMON_CTL0, + .event_mask = KNL_PCU_MSR_PMON_RAW_EVENT_MASK, + .box_ctl = HSWEP_PCU_MSR_PMON_BOX_CTL, + .ops = &snbep_uncore_msr_ops, + .format_group = &knl_uncore_pcu_format_group, +}; + +static struct intel_uncore_type *knl_msr_uncores[] = { + &knl_uncore_ubox, + &knl_uncore_cha, + &knl_uncore_pcu, + NULL, +}; + +void knl_uncore_cpu_init(void) +{ + uncore_msr_uncores = knl_msr_uncores; +} + +static void knl_uncore_imc_enable_box(struct intel_uncore_box *box) +{ + struct pci_dev *pdev = box->pci_dev; + int box_ctl = uncore_pci_box_ctl(box); + + pci_write_config_dword(pdev, box_ctl, 0); +} + +static void knl_uncore_imc_enable_event(struct intel_uncore_box *box, + struct perf_event *event) +{ + struct pci_dev *pdev = box->pci_dev; + struct hw_perf_event *hwc = &event->hw; + + if ((event->attr.config & SNBEP_PMON_CTL_EV_SEL_MASK) + == UNCORE_FIXED_EVENT) + pci_write_config_dword(pdev, hwc->config_base, + hwc->config | KNL_PMON_FIXED_CTL_EN); + else + pci_write_config_dword(pdev, hwc->config_base, + hwc->config | SNBEP_PMON_CTL_EN); +} + +static struct intel_uncore_ops knl_uncore_imc_ops = { + .init_box = snbep_uncore_pci_init_box, + .disable_box = snbep_uncore_pci_disable_box, + .enable_box = knl_uncore_imc_enable_box, + .read_counter = snbep_uncore_pci_read_counter, + .enable_event = knl_uncore_imc_enable_event, + .disable_event = snbep_uncore_pci_disable_event, +}; + +static struct intel_uncore_type knl_uncore_imc_uclk = { + .name = "imc_uclk", + .num_counters = 4, + .num_boxes = 2, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .perf_ctr = KNL_UCLK_MSR_PMON_CTR0_LOW, + .event_ctl = KNL_UCLK_MSR_PMON_CTL0, + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, + .fixed_ctr = KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW, + .fixed_ctl = KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL, + .box_ctl = KNL_UCLK_MSR_PMON_BOX_CTL, + .ops = &knl_uncore_imc_ops, + .format_group = &snbep_uncore_format_group, +}; + +static struct intel_uncore_type knl_uncore_imc_dclk = { + .name = "imc", + .num_counters = 4, + .num_boxes = 6, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .perf_ctr = KNL_MC0_CH0_MSR_PMON_CTR0_LOW, + .event_ctl = KNL_MC0_CH0_MSR_PMON_CTL0, + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, + .fixed_ctr = KNL_MC0_CH0_MSR_PMON_FIXED_LOW, + .fixed_ctl = KNL_MC0_CH0_MSR_PMON_FIXED_CTL, + .box_ctl = KNL_MC0_CH0_MSR_PMON_BOX_CTL, + .ops = &knl_uncore_imc_ops, + .format_group = &snbep_uncore_format_group, +}; + +static struct intel_uncore_type knl_uncore_edc_uclk = { + .name = "edc_uclk", + .num_counters = 4, + .num_boxes = 8, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .perf_ctr = KNL_UCLK_MSR_PMON_CTR0_LOW, + .event_ctl = KNL_UCLK_MSR_PMON_CTL0, + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, + .fixed_ctr = KNL_UCLK_MSR_PMON_UCLK_FIXED_LOW, + .fixed_ctl = KNL_UCLK_MSR_PMON_UCLK_FIXED_CTL, + .box_ctl = KNL_UCLK_MSR_PMON_BOX_CTL, + .ops = &knl_uncore_imc_ops, + .format_group = &snbep_uncore_format_group, +}; + +static struct intel_uncore_type knl_uncore_edc_eclk = { + .name = "edc_eclk", + .num_counters = 4, + .num_boxes = 8, + .perf_ctr_bits = 48, + .fixed_ctr_bits = 48, + .perf_ctr = KNL_EDC0_ECLK_MSR_PMON_CTR0_LOW, + .event_ctl = KNL_EDC0_ECLK_MSR_PMON_CTL0, + .event_mask = SNBEP_PMON_RAW_EVENT_MASK, + .fixed_ctr = KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_LOW, + .fixed_ctl = KNL_EDC0_ECLK_MSR_PMON_ECLK_FIXED_CTL, + .box_ctl = KNL_EDC0_ECLK_MSR_PMON_BOX_CTL, + .ops = &knl_uncore_imc_ops, + .format_group = &snbep_uncore_format_group, +}; + +static struct event_constraint knl_uncore_m2pcie_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), + EVENT_CONSTRAINT_END +}; + +static struct intel_uncore_type knl_uncore_m2pcie = { + .name = "m2pcie", + .num_counters = 4, + .num_boxes = 1, + .perf_ctr_bits = 48, + .constraints = knl_uncore_m2pcie_constraints, + SNBEP_UNCORE_PCI_COMMON_INIT(), +}; + +static struct attribute *knl_uncore_irp_formats_attr[] = { + &format_attr_event.attr, + &format_attr_umask.attr, + &format_attr_qor.attr, + &format_attr_edge.attr, + &format_attr_inv.attr, + &format_attr_thresh8.attr, + NULL, +}; + +static struct attribute_group knl_uncore_irp_format_group = { + .name = "format", + .attrs = knl_uncore_irp_formats_attr, +}; + +static struct intel_uncore_type knl_uncore_irp = { + .name = "irp", + .num_counters = 2, + .num_boxes = 1, + .perf_ctr_bits = 48, + .perf_ctr = SNBEP_PCI_PMON_CTR0, + .event_ctl = SNBEP_PCI_PMON_CTL0, + .event_mask = KNL_IRP_PCI_PMON_RAW_EVENT_MASK, + .box_ctl = KNL_IRP_PCI_PMON_BOX_CTL, + .ops = &snbep_uncore_pci_ops, + .format_group = &knl_uncore_irp_format_group, +}; + +enum { + KNL_PCI_UNCORE_MC_UCLK, + KNL_PCI_UNCORE_MC_DCLK, + KNL_PCI_UNCORE_EDC_UCLK, + KNL_PCI_UNCORE_EDC_ECLK, + KNL_PCI_UNCORE_M2PCIE, + KNL_PCI_UNCORE_IRP, +}; + +static struct intel_uncore_type *knl_pci_uncores[] = { + [KNL_PCI_UNCORE_MC_UCLK] = &knl_uncore_imc_uclk, + [KNL_PCI_UNCORE_MC_DCLK] = &knl_uncore_imc_dclk, + [KNL_PCI_UNCORE_EDC_UCLK] = &knl_uncore_edc_uclk, + [KNL_PCI_UNCORE_EDC_ECLK] = &knl_uncore_edc_eclk, + [KNL_PCI_UNCORE_M2PCIE] = &knl_uncore_m2pcie, + [KNL_PCI_UNCORE_IRP] = &knl_uncore_irp, + NULL, +}; + +/* + * KNL uses a common PCI device ID for multiple instances of an Uncore PMU + * device type. prior to KNL, each instance of a PMU device type had a unique + * device ID. + * + * PCI Device ID Uncore PMU Devices + * ---------------------------------- + * 0x7841 MC0 UClk, MC1 UClk + * 0x7843 MC0 DClk CH 0, MC0 DClk CH 1, MC0 DClk CH 2, + * MC1 DClk CH 0, MC1 DClk CH 1, MC1 DClk CH 2 + * 0x7833 EDC0 UClk, EDC1 UClk, EDC2 UClk, EDC3 UClk, + * EDC4 UClk, EDC5 UClk, EDC6 UClk, EDC7 UClk + * 0x7835 EDC0 EClk, EDC1 EClk, EDC2 EClk, EDC3 EClk, + * EDC4 EClk, EDC5 EClk, EDC6 EClk, EDC7 EClk + * 0x7817 M2PCIe + * 0x7814 IRP +*/ + +static const struct pci_device_id knl_uncore_pci_ids[] = { + { /* MC UClk */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7841), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_MC_UCLK, 0), + }, + { /* MC DClk Channel */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7843), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_MC_DCLK, 0), + }, + { /* EDC UClk */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7833), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_EDC_UCLK, 0), + }, + { /* EDC EClk */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7835), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_EDC_ECLK, 0), + }, + { /* M2PCIe */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7817), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_M2PCIE, 0), + }, + { /* IRP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7814), + .driver_data = UNCORE_PCI_DEV_DATA(KNL_PCI_UNCORE_IRP, 0), + }, + { /* end: all zeroes */ } +}; + +static struct pci_driver knl_uncore_pci_driver = { + .name = "knl_uncore", + .id_table = knl_uncore_pci_ids, +}; + +int knl_uncore_pci_init(void) +{ + int ret; + + /* All KNL PCI based PMON units are on the same PCI bus except IRP */ + ret = snb_pci2phy_map_init(0x7814); /* IRP */ + if (ret) + return ret; + ret = snb_pci2phy_map_init(0x7817); /* M2PCIe */ + if (ret) + return ret; + uncore_pci_uncores = knl_pci_uncores; + uncore_pci_driver = &knl_uncore_pci_driver; + return 0; +} + +/* end of KNL uncore support */ + /* Haswell-EP uncore support */ static struct attribute *hswep_uncore_ubox_formats_attr[] = { &format_attr_event.attr, From 9cc2617de5b9222abb39cd02e90d57dfea99c6d7 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Wed, 9 Dec 2015 11:34:45 -0500 Subject: [PATCH 212/271] perf/x86/amd: Remove l1-dcache-stores event for AMD This is a long standing bug with the l1-dcache-stores generic event on AMD machines. My perf_event testsuite has been complaining about this for years and I'm finally getting around to trying to get it fixed. The data_cache_refills:system event does not make sense for l1-dcache-stores. Maybe this was a typo and it was meant to be for l1-dcache-store-misses? In any case, the values returned are nowhere near correct for l1-dcache-stores and in fact the umask values for the event have completely changed with fam15h so it makes even less sense than ever. So just remove it. Signed-off-by: Vince Weaver Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/alpine.DEB.2.20.1512091134350.24311@vincent-weaver-1.umelst.maine.edu Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index 1cee5d2d7ece..05e76bf65781 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c @@ -18,7 +18,7 @@ static __initconst const u64 amd_hw_cache_event_ids [ C(RESULT_MISS) ] = 0x0141, /* Data Cache Misses */ }, [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */ + [ C(RESULT_ACCESS) ] = 0, [ C(RESULT_MISS) ] = 0, }, [ C(OP_PREFETCH) ] = { From fd36f3dd79331b9610664b867ff205465bf9ce68 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:06:58 +0900 Subject: [PATCH 213/271] 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/builtin-annotate.c | 7 +++++-- tools/perf/builtin-diff.c | 11 +++++------ tools/perf/tests/hists_link.c | 6 +++--- tools/perf/util/hist.c | 31 +++++++++++++++++-------------- tools/perf/util/hist.h | 4 ++-- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index e18f1b995ffd..b5b8db0b0338 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -47,7 +47,7 @@ struct perf_annotate { }; static int perf_evsel__add_sample(struct perf_evsel *evsel, - struct perf_sample *sample __maybe_unused, + struct perf_sample *sample, struct addr_location *al, struct perf_annotate *ann) { @@ -72,7 +72,10 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, return 0; } - he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true); + sample->period = 1; + sample->weight = 1; + + he = __hists__add_entry(hists, al, NULL, NULL, NULL, sample, true); if (he == NULL) return -ENOMEM; diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 0b180a885ba3..69f5b1feff39 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -311,11 +311,11 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, } static int hists__add_entry(struct hists *hists, - struct addr_location *al, u64 period, - u64 weight, u64 transaction) + struct addr_location *al, + struct perf_sample *sample) { - if (__hists__add_entry(hists, al, NULL, NULL, NULL, period, weight, - transaction, true) != NULL) + if (__hists__add_entry(hists, al, NULL, NULL, NULL, + sample, true) != NULL) return 0; return -ENOMEM; } @@ -336,8 +336,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (hists__add_entry(hists, &al, sample->period, - sample->weight, sample->transaction)) { + if (hists__add_entry(hists, &al, sample)) { pr_warning("problem incrementing symbol period, skipping event\n"); goto out_put; } diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 6243e2b2a245..9eac98daecb8 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -64,7 +64,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) struct perf_evsel *evsel; struct addr_location al; struct hist_entry *he; - struct perf_sample sample = { .period = 1, }; + struct perf_sample sample = { .period = 1, .weight = 1, }; size_t i = 0, k; /* @@ -90,7 +90,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he = __hists__add_entry(hists, &al, NULL, - NULL, NULL, 1, 1, 0, true); + NULL, NULL, &sample, true); if (he == NULL) { addr_location__put(&al); goto out; @@ -116,7 +116,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) goto out; he = __hists__add_entry(hists, &al, NULL, - NULL, NULL, 1, 1, 0, true); + NULL, NULL, &sample, true); if (he == NULL) { addr_location__put(&al); goto out; 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); From 723928340c9d28d92dcaff8b8fbc9100a1cf9429 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 24 Dec 2015 11:16:17 +0900 Subject: [PATCH 214/271] 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(+) 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 */ }; From be45d40efec96558c489579bbf93465e90b10abe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 22:08:41 +0900 Subject: [PATCH 215/271] tools lib traceevent: Factor out and export print_event_field[s]() The print_event_field() and print_event_fields() functions print basic information of a given field or event without the print format. They'll be used by dynamic sort keys later. Committer note: Rename it to pevent_print_field[s]() to get proper namespacing, as discussed with Steven Rostedt. Signed-off-by: Namhyung Kim Acked-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Andi Kleen Cc: David Ahern Cc: Frederic Weisbecker Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/r/1450876121-22494-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/traceevent/event-parse.c | 131 +++++++++++++++-------------- tools/lib/traceevent/event-parse.h | 4 + 2 files changed, 73 insertions(+), 62 deletions(-) diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 68276f35e323..ea69ce35e902 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -4735,73 +4735,80 @@ static int is_printable_array(char *p, unsigned int len) return 1; } -static void print_event_fields(struct trace_seq *s, void *data, - int size __maybe_unused, - struct event_format *event) +void pevent_print_field(struct trace_seq *s, void *data, + struct format_field *field) { - struct format_field *field; unsigned long long val; unsigned int offset, len, i; + struct pevent *pevent = field->event->pevent; + + if (field->flags & FIELD_IS_ARRAY) { + offset = field->offset; + len = field->size; + if (field->flags & FIELD_IS_DYNAMIC) { + val = pevent_read_number(pevent, data + offset, len); + offset = val; + len = offset >> 16; + offset &= 0xffff; + } + if (field->flags & FIELD_IS_STRING && + is_printable_array(data + offset, len)) { + trace_seq_printf(s, "%s", (char *)data + offset); + } else { + trace_seq_puts(s, "ARRAY["); + for (i = 0; i < len; i++) { + if (i) + trace_seq_puts(s, ", "); + trace_seq_printf(s, "%02x", + *((unsigned char *)data + offset + i)); + } + trace_seq_putc(s, ']'); + field->flags &= ~FIELD_IS_STRING; + } + } else { + val = pevent_read_number(pevent, data + field->offset, + field->size); + if (field->flags & FIELD_IS_POINTER) { + trace_seq_printf(s, "0x%llx", val); + } else if (field->flags & FIELD_IS_SIGNED) { + switch (field->size) { + case 4: + /* + * If field is long then print it in hex. + * A long usually stores pointers. + */ + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%x", (int)val); + else + trace_seq_printf(s, "%d", (int)val); + break; + case 2: + trace_seq_printf(s, "%2d", (short)val); + break; + case 1: + trace_seq_printf(s, "%1d", (char)val); + break; + default: + trace_seq_printf(s, "%lld", val); + } + } else { + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%llx", val); + else + trace_seq_printf(s, "%llu", val); + } + } +} + +void pevent_print_fields(struct trace_seq *s, void *data, + int size __maybe_unused, struct event_format *event) +{ + struct format_field *field; field = event->format.fields; while (field) { trace_seq_printf(s, " %s=", field->name); - if (field->flags & FIELD_IS_ARRAY) { - offset = field->offset; - len = field->size; - if (field->flags & FIELD_IS_DYNAMIC) { - val = pevent_read_number(event->pevent, data + offset, len); - offset = val; - len = offset >> 16; - offset &= 0xffff; - } - if (field->flags & FIELD_IS_STRING && - is_printable_array(data + offset, len)) { - trace_seq_printf(s, "%s", (char *)data + offset); - } else { - trace_seq_puts(s, "ARRAY["); - for (i = 0; i < len; i++) { - if (i) - trace_seq_puts(s, ", "); - trace_seq_printf(s, "%02x", - *((unsigned char *)data + offset + i)); - } - trace_seq_putc(s, ']'); - field->flags &= ~FIELD_IS_STRING; - } - } else { - val = pevent_read_number(event->pevent, data + field->offset, - field->size); - if (field->flags & FIELD_IS_POINTER) { - trace_seq_printf(s, "0x%llx", val); - } else if (field->flags & FIELD_IS_SIGNED) { - switch (field->size) { - case 4: - /* - * If field is long then print it in hex. - * A long usually stores pointers. - */ - if (field->flags & FIELD_IS_LONG) - trace_seq_printf(s, "0x%x", (int)val); - else - trace_seq_printf(s, "%d", (int)val); - break; - case 2: - trace_seq_printf(s, "%2d", (short)val); - break; - case 1: - trace_seq_printf(s, "%1d", (char)val); - break; - default: - trace_seq_printf(s, "%lld", val); - } - } else { - if (field->flags & FIELD_IS_LONG) - trace_seq_printf(s, "0x%llx", val); - else - trace_seq_printf(s, "%llu", val); - } - } + pevent_print_field(s, data, field); field = field->next; } } @@ -4827,7 +4834,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event if (event->flags & EVENT_FL_FAILED) { trace_seq_printf(s, "[FAILED TO PARSE]"); - print_event_fields(s, data, size, event); + pevent_print_fields(s, data, size, event); return; } @@ -5301,7 +5308,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, int print_pretty = 1; if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) - print_event_fields(s, record->data, record->size, event); + pevent_print_fields(s, record->data, record->size, event); else { if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 6fc83c7edbe9..706d9bc24066 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -705,6 +705,10 @@ struct cmdline *pevent_data_pid_from_comm(struct pevent *pevent, const char *com struct cmdline *next); int pevent_cmdline_pid(struct pevent *pevent, struct cmdline *cmdline); +void pevent_print_field(struct trace_seq *s, void *data, + struct format_field *field); +void pevent_print_fields(struct trace_seq *s, void *data, + int size __maybe_unused, struct event_format *event); void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record); int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, From 54f8f40384ab940e15585afde5c278c8e7726214 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:01 +0900 Subject: [PATCH 216/271] perf top: Create the evlist sooner 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, so we need to pass the evlist to the sort routines, create it sooner so that the next patch can do that. 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 [ Split from the patch passing the evlist to the sort routines ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 9ebd67a42ede..4e913d852518 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1231,6 +1231,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) if (argc) usage_with_options(top_usage, options); + if (!top.evlist->nr_entries && + perf_evlist__add_default(top.evlist) < 0) { + pr_err("Not enough memory for event selector list\n"); + goto out_delete_evlist; + } + sort__mode = SORT_MODE__TOP; /* display thread wants entries to be collapsed in a different tree */ sort__need_collapse = 1; @@ -1277,12 +1283,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) goto out_delete_evlist; } - if (!top.evlist->nr_entries && - perf_evlist__add_default(top.evlist) < 0) { - ui__error("Not enough memory for event selector list\n"); - goto out_delete_evlist; - } - symbol_conf.nr_events = top.evlist->nr_entries; if (top.delay_secs < 1) From 40184c46a3055a97e2efa69da6f17c05bff4b776 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:01 +0900 Subject: [PATCH 217/271] 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/builtin-annotate.c | 2 +- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/tests/hists_cumulate.c | 8 ++++---- tools/perf/tests/hists_filter.c | 2 +- tools/perf/tests/hists_link.c | 2 +- tools/perf/tests/hists_output.c | 10 +++++----- tools/perf/util/sort.c | 15 +++++++++------ tools/perf/util/sort.h | 5 +++-- 10 files changed, 27 insertions(+), 23 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b5b8db0b0338..cc5c1267c738 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -370,7 +370,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) if (ret < 0) goto out_delete; - if (setup_sorting() < 0) + if (setup_sorting(NULL) < 0) usage_with_options(annotate_usage, options); if (annotate.use_stdio) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 69f5b1feff39..87063835d741 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -1279,7 +1279,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) sort__mode = SORT_MODE__DIFF; - if (setup_sorting() < 0) + if (setup_sorting(NULL) < 0) usage_with_options(diff_usage, options); setup_pager(); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 2a7330b99b82..ea53c816640a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -897,7 +897,7 @@ repeat: symbol_conf.cumulate_callchain = false; } - if (setup_sorting() < 0) { + if (setup_sorting(session->evlist) < 0) { if (sort_order) parse_options_usage(report_usage, options, "s", 1); if (field_order) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4e913d852518..005825942441 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1241,7 +1241,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) /* display thread wants entries to be collapsed in a different tree */ sort__need_collapse = 1; - if (setup_sorting() < 0) { + if (setup_sorting(top.evlist) < 0) { if (sort_order) parse_options_usage(top_usage, options, "s", 1); if (field_order) diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 8292948bc5f9..e36089212061 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c @@ -281,7 +281,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = false; perf_evsel__reset_sample_bit(evsel, CALLCHAIN); - setup_sorting(); + setup_sorting(NULL); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -428,7 +428,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = false; perf_evsel__set_sample_bit(evsel, CALLCHAIN); - setup_sorting(); + setup_sorting(NULL); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -486,7 +486,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = true; perf_evsel__reset_sample_bit(evsel, CALLCHAIN); - setup_sorting(); + setup_sorting(NULL); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); @@ -670,7 +670,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) symbol_conf.cumulate_callchain = true; perf_evsel__set_sample_bit(evsel, CALLCHAIN); - setup_sorting(); + setup_sorting(NULL); callchain_register_param(&callchain_param); err = add_hist_entries(hists, machine); diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index ccb5b4921f25..2a784befd9ce 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c @@ -122,7 +122,7 @@ int test__hists_filter(int subtest __maybe_unused) goto out; /* default sort order (comm,dso,sym) will be used */ - if (setup_sorting() < 0) + if (setup_sorting(NULL) < 0) goto out; machines__init(&machines); diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 9eac98daecb8..c764d69ac6ef 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c @@ -294,7 +294,7 @@ int test__hists_link(int subtest __maybe_unused) goto out; /* default sort order (comm,dso,sym) will be used */ - if (setup_sorting() < 0) + if (setup_sorting(NULL) < 0) goto out; machines__init(&machines); diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index 248beec1d917..ebe6cd485b5d 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c @@ -134,7 +134,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) field_order = NULL; sort_order = NULL; /* equivalent to sort_order = "comm,dso,sym" */ - setup_sorting(); + setup_sorting(NULL); /* * expected output: @@ -236,7 +236,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) field_order = "overhead,cpu"; sort_order = "pid"; - setup_sorting(); + setup_sorting(NULL); /* * expected output: @@ -292,7 +292,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) field_order = "comm,overhead,dso"; sort_order = NULL; - setup_sorting(); + setup_sorting(NULL); /* * expected output: @@ -366,7 +366,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) field_order = "dso,sym,comm,overhead,dso"; sort_order = "sym"; - setup_sorting(); + setup_sorting(NULL); /* * expected output: @@ -468,7 +468,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine) field_order = "cpu,pid,comm,dso,sym"; sort_order = "dso,pid"; - setup_sorting(); + setup_sorting(NULL); /* * expected output: 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); From c7c2a5e40f17ab3b14716d4f08d03792a9b683e7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:02 +0900 Subject: [PATCH 218/271] 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(+) 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; } From 60517d28fbd91629686dcf9a39aef4e068a3d5f6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:03 +0900 Subject: [PATCH 219/271] 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(-) 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 */ }; From a34bb6a08d6020bde0475bc901793771858a1112 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:04 +0900 Subject: [PATCH 220/271] 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(-) 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, From 053a3989e12fdf3be45c00ec1cb0ce09fba0ee4a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:05 +0900 Subject: [PATCH 221/271] 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/Documentation/perf-report.txt | 3 +++ tools/perf/Documentation/perf-top.txt | 3 +++ tools/perf/builtin-report.c | 2 ++ tools/perf/builtin-top.c | 2 ++ tools/perf/util/sort.c | 32 +++++++++++++++++++++--- tools/perf/util/symbol.h | 3 ++- 6 files changed, 41 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index dab99ed2b339..ae7cd91727f6 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -371,6 +371,9 @@ include::itrace.txt[] --socket-filter:: Only report the samples on the processor socket that match with this filter +--raw-trace:: + When displaying traceevent output, do not use print fmt or plugins. + include::callchain-overhead-calculation.txt[] SEE ALSO diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 556cec09bf50..b0e60e17db38 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -230,6 +230,9 @@ Default is to monitor all CPUS. The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k Note that this feature may not be available on all processors. +--raw-trace:: + When displaying traceevent output, do not use print fmt or plugins. + INTERACTIVE PROMPTING KEYS -------------------------- diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index ea53c816640a..f10c663af996 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -788,6 +788,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) "Show callgraph from reference event"), OPT_INTEGER(0, "socket-filter", &report.socket_filter, "only show processor socket that match with this filter"), + OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, + "Show raw trace event output (do not use print fmt or plugins)"), OPT_END() }; struct perf_data_file file = { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 005825942441..bf01cbb0ef23 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1210,6 +1210,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_CALLBACK('j', "branch-filter", &opts->branch_stack, "branch filter mask", "branch stack filter modes", parse_branch_stack), + OPT_BOOLEAN(0, "raw-trace", &symbol_conf.raw_trace, + "Show raw trace event output (do not use print fmt or plugins)"), OPT_END() }; const char * const top_usage[] = { 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, From 5d0cff93bb7aa85349230d4e29902b2648640c53 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:06 +0900 Subject: [PATCH 222/271] 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(-) 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; From 3b099bf5898ac1bf44d822f0bc15a7517e6fa117 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:07 +0900 Subject: [PATCH 223/271] 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(-) 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); From 361459f163fa1ec7ff4700ec876c3b7ff5f36cc6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:08 +0900 Subject: [PATCH 224/271] 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/ui/browsers/hists.c | 7 ++++--- tools/perf/ui/gtk/hists.c | 4 ++-- tools/perf/ui/hist.c | 2 +- tools/perf/ui/stdio/hist.c | 6 +++--- tools/perf/util/hist.c | 2 +- tools/perf/util/hist.h | 14 ++++++++++++-- tools/perf/util/sort.c | 20 ++++++++++++++------ 7 files changed, 37 insertions(+), 18 deletions(-) diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ec331969b7d7..901d481e6cea 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1041,7 +1041,8 @@ static int hist_browser__show_entry(struct hist_browser *browser, hist_browser__gotorc(browser, row, 0); perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll) + if (perf_hpp__should_skip(fmt, entry->hists) || + column++ < browser->b.horiz_scroll) continue; if (current_entry && browser->b.navkeypressed) { @@ -1144,7 +1145,7 @@ static int hists_browser__scnprintf_headers(struct hist_browser *browser, char * } perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt) || column++ < browser->b.horiz_scroll) + if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll) continue; ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); @@ -1414,7 +1415,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, printed += fprintf(fp, "%c ", folded_sign); perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, he->hists)) continue; if (!first) { diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 467717276ab6..0f8dcfdfb10f 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -318,7 +318,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, col_idx = 0; perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, hists)) continue; /* @@ -368,7 +368,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, col_idx = 0; perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, h->hists)) continue; if (fmt->color) diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 5029ba2b55af..8263c0eb9fb5 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -619,7 +619,7 @@ unsigned int hists__sort_list_width(struct hists *hists) struct perf_hpp dummy_hpp; perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, hists)) continue; if (first) diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 7ebc661be267..387110d50b00 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -385,7 +385,7 @@ static int hist_entry__snprintf(struct hist_entry *he, struct perf_hpp *hpp) return 0; perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, he->hists)) continue; /* @@ -464,7 +464,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, fprintf(fp, "# "); perf_hpp__for_each_format(fmt) { - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, hists)) continue; if (!first) @@ -490,7 +490,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, perf_hpp__for_each_format(fmt) { unsigned int i; - if (perf_hpp__should_skip(fmt)) + if (perf_hpp__should_skip(fmt, hists)) continue; if (!first) 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) { From 2e422fd1e4b0a1c0ca11d360be2147c87911dd1a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:09 +0900 Subject: [PATCH 225/271] 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(-) 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) { From d49dadea78624353d1df660efb49f187bd5c5971 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 Dec 2015 02:07:10 +0900 Subject: [PATCH 226/271] 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(-) 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 { From d0018b495c0429af3efc1b54f16d291a9fa8b4be Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 23 Dec 2015 18:58:30 +0100 Subject: [PATCH 227/271] tools build feature: Fix feature_check_display_code typo This function is cursed.. ;-) Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama Link: http://lkml.kernel.org/r/1450893514-9158-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 6c0519de765d..a8b4befdef7e 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -143,7 +143,7 @@ ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)") endif feature_display_check = $(eval $(feature_check_display_code)) -define feature_display_check_code +define feature_check_display_code ifneq ($(feature-$(1)), 1) feature_display := 1 endif From 76ee2ff342743e57cb6dc059d0aba90e0c4c9bfc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 23 Dec 2015 18:58:31 +0100 Subject: [PATCH 228/271] tools build feature: Move dwarf post unwind choice output into perf We decide what dwarf unwind to choose way after the Makefile.feature makefile is included. The $(dwarf-post-unwind) is not even set at that time. For the same reason it was never included in FEATURE-DUMP file. Moving it into perf VF=1 verbose display. $ make VF=1 BUILD: Doing 'make -j4' parallel build Auto-detecting system features: ... dwarf: [ on ] ... ... LIBUNWIND_DIR: ... LIBDW_DIR: ... DWARF post unwind library: libunwind ... Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama Link: http://lkml.kernel.org/r/1450893514-9158-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 9 --------- tools/perf/config/Makefile | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index a8b4befdef7e..20753124c8f4 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -126,10 +126,6 @@ FEATURE_DUMP_FILENAME = $(OUTPUT)FEATURE-DUMP$(FEATURE_USER) FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat)))) FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME)) -ifeq ($(dwarf-post-unwind),1) - FEATURE_DUMP += dwarf-post-unwind($(dwarf-post-unwind-text)) -endif - # The $(feature_display) controls the default detection message # output. It's set if: # - detected features differes from stored features from @@ -160,11 +156,6 @@ ifeq ($(feature_display),1) $(info ) $(info Auto-detecting system features:) $(foreach feat,$(FEATURE_DISPLAY),$(call feature_print_status,$(feat),)) - - ifeq ($(dwarf-post-unwind),1) - $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text)) - endif - ifneq ($(feature_verbose),1) $(info ) endif diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index a5524179d26e..18b2f96d0941 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -751,6 +751,10 @@ ifeq ($(VF),1) $(call print_var,sysconfdir) $(call print_var,LIBUNWIND_DIR) $(call print_var,LIBDW_DIR) + + ifeq ($(dwarf-post-unwind),1) + $(call feature_print_text,"DWARF post unwind library", $(dwarf-post-unwind-text)) + endif $(info ) endif From c6a5f88f335ec43d2850d62bc4924f719d265670 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 23 Dec 2015 18:58:32 +0100 Subject: [PATCH 229/271] tools build feature: Introduce feature_assign macro The feature_assign macro generates feature value assignment for name, like: $(call feature_assign,dwarf) == feature-dwarf=1 This will be used more in following patches. Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama Link: http://lkml.kernel.org/r/1450893514-9158-4-git-send-email-jolsa@kernel.org [ Rename it to feature_assign, the original shorter name was misleading, to say the least ;-) ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 20753124c8f4..b1b262eb5bea 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -122,8 +122,14 @@ define feature_print_text_code MSG = $(shell printf '...%30s: %s' $(1) $(2)) endef +# +# generates feature value assignment for name, like: +# $(call feature_assign,dwarf) == feature-dwarf=1 +# +feature_assign = feature-$(1)=$(feature-$(1)) + FEATURE_DUMP_FILENAME = $(OUTPUT)FEATURE-DUMP$(FEATURE_USER) -FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),feature-$(feat)($(feature-$(feat)))) +FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),$(call feature_assign,$(feat))) FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME)) # The $(feature_display) controls the default detection message From 936d120d5f6406377e622da3167cafc811d053ea Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 23 Dec 2015 18:58:33 +0100 Subject: [PATCH 230/271] tools build feature: Use value assignment form for FEATURE-DUMP file Changing the contents of the FEATURE-DUMP file, so it looks like: feature-backtrace=1 feature-dwarf=0 feature-fortify-source=1 feature-sync-compare-and-swap=0 This way it could get included in sub projects, so they won't be forced to redo features detection. Also now storing the complete set of features. Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama Link: http://lkml.kernel.org/r/1450893514-9158-5-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index b1b262eb5bea..02db3cdff20f 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -129,8 +129,24 @@ endef feature_assign = feature-$(1)=$(feature-$(1)) FEATURE_DUMP_FILENAME = $(OUTPUT)FEATURE-DUMP$(FEATURE_USER) -FEATURE_DUMP := $(foreach feat,$(FEATURE_DISPLAY),$(call feature_assign,$(feat))) -FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME)) +FEATURE_DUMP := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_FILENAME)) + +feature_dump_check = $(eval $(feature_dump_check_code)) +define feature_dump_check_code + ifeq ($(findstring $(1),$(FEATURE_DUMP)),) + $(2) := 1 + endif +endef + +# +# First check if any test from FEATURE_DISPLAY +# and set feature_display := 1 if it does +$(foreach feat,$(FEATURE_DISPLAY),$(call feature_dump_check,$(call feature_assign,$(feat)),feature_display)) + +# +# Now also check if any other test changed, +# so we force FEATURE-DUMP generation +$(foreach feat,$(FEATURE_TESTS),$(call feature_dump_check,$(call feature_assign,$(feat)),feature_dump_changed)) # The $(feature_display) controls the default detection message # output. It's set if: @@ -139,9 +155,9 @@ FEATURE_DUMP_FILE := $(shell touch $(FEATURE_DUMP_FILENAME); cat $(FEATURE_DUMP_ # - one of the $(FEATURE_DISPLAY) is not detected # - VF is enabled -ifneq ("$(FEATURE_DUMP)","$(FEATURE_DUMP_FILE)") - $(shell echo "$(FEATURE_DUMP)" > $(FEATURE_DUMP_FILENAME)) - feature_display := 1 +ifeq ($(feature_dump_changed),1) + $(shell rm -f $(FEATURE_DUMP_FILENAME)) + $(foreach feat,$(FEATURE_TESTS),$(shell echo "$(call feature_assign,$(feat))" >> $(FEATURE_DUMP_FILENAME))) endif feature_display_check = $(eval $(feature_check_display_code)) From 58683600dfe377c883eb8217b5a9bfcfe231b3ff Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 23 Dec 2015 18:58:34 +0100 Subject: [PATCH 231/271] perf build: Use FEATURE-DUMP in bpf subproject Using FEATURE-DUMP in bpf subproject for features detection in case bpf is built via perf. Keeping the current features detection otherwise. Signed-off-by: Jiri Olsa Tested-by: Wang Nan Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama Link: http://lkml.kernel.org/r/1450893514-9158-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/bpf/Makefile | 4 ++++ tools/perf/Makefile.perf | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 636e3ddb93a1..919b71780710 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -80,7 +80,11 @@ endif endif ifeq ($(check_feat),1) +ifeq ($(FEATURES_DUMP),) include $(srctree)/tools/build/Makefile.feature +else +include $(FEATURES_DUMP) +endif endif export prefix libdir src obj diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 569fcf022531..404e3b1c4e31 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -436,7 +436,7 @@ $(LIBAPI)-clean: $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null $(LIBBPF): fixdep FORCE - $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a + $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a FEATURES_DUMP=$(realpath $(OUTPUT)FEATURE-DUMP) $(LIBBPF)-clean: $(call QUIET_CLEAN, libbpf) From 9735be24ec086fbccee321471cc21dedefa956a6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Jan 2016 19:58:35 +0900 Subject: [PATCH 232/271] 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(-) 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); From 4c96bee03247c6eab27287fa66457a231b9fab79 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Jan 2016 19:58:36 +0900 Subject: [PATCH 233/271] perf report: Add documentation for dynamic sort keys 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-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-report.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index ae7cd91727f6..8a301f6afb37 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -117,6 +117,30 @@ OPTIONS And default sort keys are changed to comm, dso_from, symbol_from, dso_to and symbol_to, see '--branch-stack'. + If the data file has tracepoint event(s), following (dynamic) sort keys + are also available: + trace, trace_fields, [.][/raw] + + - trace: pretty printed trace output in a single column + - trace_fields: fields in tracepoints in separate columns + - : optional event and field name for a specific field + + The last form consists of event and field names. If event name is + omitted, it searches all events for matching field name. The matched + field will be shown only for the event has the field. The event name + supports substring match so user doesn't need to specify full subsystem + and event name everytime. For example, 'sched:sched_switch' event can + be shortened to 'switch' as long as it's not ambiguous. Also event can + be specified by its index (starting from 1) preceded by the '%'. + So '%1' is the first event, '%2' is the second, and so on. + + The field name can have '/raw' suffix which disables pretty printing + and shows raw field value like hex numbers. The --raw-trace option + has the same effect for all dynamic sort keys. + + The default sort keys are changed to 'trace' if all events in the data + file are tracepoint. + -F:: --fields=:: Specify output field - multiple keys can be specified in CSV format. From 6db1a5c190d6abe416ea36aa28a6c53e0b3bbd5e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:05 +0100 Subject: [PATCH 234/271] perf stat record: Keep sample_type 0 for pipe session For pipe sessions we need to keep sample_type zero, because script's perf_evsel__check_attr is triggered by sample_type != 0, and the check would fail on stat session. I was tempted to keep it zero unconditionally, but the pipe session is sufficient. In perf.data session we are guarded by HEADER_STAT feature. Signed-off-by: Jiri Olsa Cc: David Ahern Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452028152-26762-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9805e03ab163..7f568244662b 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -184,11 +184,18 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) * like tracepoints. Clear it up for counting. */ attr->sample_period = 0; + /* * But set sample_type to PERF_SAMPLE_IDENTIFIER, which should be harmless * while avoiding that older tools show confusing messages. + * + * However for pipe sessions we need to keep it zero, + * because script's perf_evsel__check_attr is triggered + * by attr->sample_type != 0, and we can't run it on + * stat sessions. */ - attr->sample_type = PERF_SAMPLE_IDENTIFIER; + if (!(STAT_RECORD && perf_stat.file.is_pipe)) + attr->sample_type = PERF_SAMPLE_IDENTIFIER; /* * Disabling all counters initially, they will be enabled From cfc8874a485992491b865dde64965bb7c18c26b5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:06 +0100 Subject: [PATCH 235/271] perf script: Process cpu/threads maps Adding processing of cpu/threads maps. Configuring session's evlist with these maps. 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-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index bcc3542d9df5..aa6d7cf87dab 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -18,7 +18,11 @@ #include "util/sort.h" #include "util/data.h" #include "util/auxtrace.h" +#include "util/cpumap.h" +#include "util/thread_map.h" +#include "util/stat.h" #include +#include "asm/bug.h" static char const *script_name; static char const *generate_script_lang; @@ -606,6 +610,9 @@ struct perf_script { bool show_task_events; bool show_mmap_events; bool show_switch_events; + bool allocated; + struct cpu_map *cpus; + struct thread_map *threads; }; static void process_event(struct perf_script *script __maybe_unused, union perf_event *event, @@ -1682,6 +1689,63 @@ static void script__setup_sample_type(struct perf_script *script) } } +static int set_maps(struct perf_script *script) +{ + struct perf_evlist *evlist = script->session->evlist; + + if (!script->cpus || !script->threads) + return 0; + + if (WARN_ONCE(script->allocated, "stats double allocation\n")) + return -EINVAL; + + perf_evlist__set_maps(evlist, script->cpus, script->threads); + + if (perf_evlist__alloc_stats(evlist, true)) + return -ENOMEM; + + script->allocated = true; + return 0; +} + +static +int process_thread_map_event(struct perf_tool *tool, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_script *script = container_of(tool, struct perf_script, tool); + + if (script->threads) { + pr_warning("Extra thread map event, ignoring.\n"); + return 0; + } + + script->threads = thread_map__new_event(&event->thread_map); + if (!script->threads) + return -ENOMEM; + + return set_maps(script); +} + +static +int process_cpu_map_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + struct perf_script *script = container_of(tool, struct perf_script, tool); + + if (script->cpus) { + pr_warning("Extra cpu map event, ignoring.\n"); + return 0; + } + + script->cpus = cpu_map__new_data(&event->cpu_map.data); + if (!script->cpus) + return -ENOMEM; + + return set_maps(script); +} + int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) { bool show_full_info = false; @@ -1710,6 +1774,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) .auxtrace_info = perf_event__process_auxtrace_info, .auxtrace = perf_event__process_auxtrace, .auxtrace_error = perf_event__process_auxtrace_error, + .thread_map = process_thread_map_event, + .cpu_map = process_cpu_map_event, .ordered_events = true, .ordering_requires_timestamps = true, }, @@ -2063,6 +2129,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) flush_scripting(); out_delete: + perf_evlist__free_stats(session->evlist); perf_session__delete(session); if (script_started) From 91a2c3d54fd4b5be4e25acc1d8c1cc9a28319774 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:07 +0100 Subject: [PATCH 236/271] perf script: Process stat config event Adding processing of stat config event and initialize stat_config object. 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-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index aa6d7cf87dab..a90bc0b81e70 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -36,6 +36,7 @@ static bool print_flags; static bool nanosecs; static const char *cpu_list; static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); +static struct perf_stat_config stat_config; unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; @@ -1689,6 +1690,14 @@ static void script__setup_sample_type(struct perf_script *script) } } +static int process_stat_config_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session __maybe_unused) +{ + perf_event__read_stat_config(&stat_config, &event->stat_config); + return 0; +} + static int set_maps(struct perf_script *script) { struct perf_evlist *evlist = script->session->evlist; @@ -1774,6 +1783,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) .auxtrace_info = perf_event__process_auxtrace_info, .auxtrace = perf_event__process_auxtrace, .auxtrace_error = perf_event__process_auxtrace_error, + .stat_config = process_stat_config_event, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, .ordered_events = true, From 8058a30ce174060a3c8156dc87b4d4ae39e8281b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:08 +0100 Subject: [PATCH 237/271] 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(+) 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); }; From e099eba8c8df0f96e7cd6ddc5fc3151fe37be24e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:09 +0100 Subject: [PATCH 238/271] perf script: Add stat default handlers Implement struct scripting_ops::(process_stat|process_stat_interval) handlers - calling scripting handlers from stat events handlers. 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-6-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/builtin-script.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index a90bc0b81e70..5e1865408aa5 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -221,6 +221,9 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, struct perf_event_attr *attr = &evsel->attr; bool allow_user_set; + if (perf_header__has_feat(&session->header, HEADER_STAT)) + return 0; + allow_user_set = perf_header__has_feat(&session->header, HEADER_AUXTRACE); @@ -674,6 +677,18 @@ static void process_event(struct perf_script *script __maybe_unused, union perf_ static struct scripting_ops *scripting_ops; +static void process_stat(struct perf_evsel *counter, u64 tstamp) +{ + if (scripting_ops && scripting_ops->process_stat) + scripting_ops->process_stat(&stat_config, counter, tstamp); +} + +static void process_stat_interval(u64 tstamp) +{ + if (scripting_ops && scripting_ops->process_stat_interval) + scripting_ops->process_stat_interval(tstamp); +} + static void setup_scripting(void) { setup_perl_scripting(); @@ -1690,6 +1705,22 @@ static void script__setup_sample_type(struct perf_script *script) } } +static int process_stat_round_event(struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_session *session) +{ + struct stat_round_event *round = &event->stat_round; + struct perf_evsel *counter; + + evlist__for_each(session->evlist, counter) { + perf_stat_process_counter(&stat_config, counter); + process_stat(counter, round->time); + } + + process_stat_interval(round->time); + return 0; +} + static int process_stat_config_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_session *session __maybe_unused) @@ -1783,6 +1814,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) .auxtrace_info = perf_event__process_auxtrace_info, .auxtrace = perf_event__process_auxtrace, .auxtrace_error = perf_event__process_auxtrace_error, + .stat = perf_event__process_stat_event, + .stat_round = process_stat_round_event, .stat_config = process_stat_config_event, .thread_map = process_thread_map_event, .cpu_map = process_cpu_map_event, From aef90263561a87ae6d9c6a0f4071d825ce636eef Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 5 Jan 2016 22:09:11 +0100 Subject: [PATCH 239/271] 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 --- .../scripting-engines/trace-event-python.c | 115 +++++++++++++++++- 1 file changed, 109 insertions(+), 6 deletions(-) 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, }; From 15d2b9956b41ffb5961b897bf61cdc09f722dfbf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 6 Jan 2016 11:49:55 +0100 Subject: [PATCH 240/271] 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(-) 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; From 36e33c53f4b693d96fb8dd4529fe14306d4e3e76 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 6 Jan 2016 11:49:56 +0100 Subject: [PATCH 241/271] perf script: Display stat events by default If no script is specified for stat data, display stat events in raw form. $ perf stat record ls SNIP Performance counter stats for 'ls': 0.851585 task-clock (msec) # 0.717 CPUs utilized 0 context-switches # 0.000 K/sec 0 cpu-migrations # 0.000 K/sec 114 page-faults # 0.134 M/sec 2,620,918 cycles # 3.078 GHz stalled-cycles-frontend stalled-cycles-backend 2,714,111 instructions # 1.04 insns per cycle 542,434 branches # 636.970 M/sec 15,946 branch-misses # 2.94% of all branches 0.001186954 seconds time elapsed $ perf script CPU THREAD VAL ENA RUN TIME EVENT -1 26185 851585 851585 851585 1186954 task-clock -1 26185 0 851585 851585 1186954 context-switches -1 26185 0 851585 851585 1186954 cpu-migrations -1 26185 114 851585 851585 1186954 page-faults -1 26185 2620918 853340 853340 1186954 cycles -1 26185 0 0 0 1186954 stalled-cycles-frontend -1 26185 0 0 0 1186954 stalled-cycles-backend -1 26185 2714111 853340 853340 1186954 instructions -1 26185 542434 853340 853340 1186954 branches -1 26185 15946 853340 853340 1186954 branch-misses 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/1452077397-31958-3-git-send-email-jolsa@kernel.org [ Rename 'time' parameter to 'tstamp' to fix build on older distros ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 5e1865408aa5..5e2f9d20a296 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -677,10 +677,46 @@ static void process_event(struct perf_script *script __maybe_unused, union perf_ static struct scripting_ops *scripting_ops; +static void __process_stat(struct perf_evsel *counter, u64 tstamp) +{ + int nthreads = thread_map__nr(counter->threads); + int ncpus = perf_evsel__nr_cpus(counter); + int cpu, thread; + static int header_printed; + + if (counter->system_wide) + nthreads = 1; + + if (!header_printed) { + printf("%3s %8s %15s %15s %15s %15s %s\n", + "CPU", "THREAD", "VAL", "ENA", "RUN", "TIME", "EVENT"); + header_printed = 1; + } + + for (thread = 0; thread < nthreads; thread++) { + for (cpu = 0; cpu < ncpus; cpu++) { + struct perf_counts_values *counts; + + counts = perf_counts(counter->counts, cpu, thread); + + printf("%3d %8d %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %15" PRIu64 " %s\n", + counter->cpus->map[cpu], + thread_map__pid(counter->threads, thread), + counts->val, + counts->ena, + counts->run, + tstamp, + perf_evsel__name(counter)); + } + } +} + static void process_stat(struct perf_evsel *counter, u64 tstamp) { if (scripting_ops && scripting_ops->process_stat) scripting_ops->process_stat(&stat_config, counter, tstamp); + else + __process_stat(counter, tstamp); } static void process_stat_interval(u64 tstamp) From b8a1962d17b4e3cfdd7b7dc9ebd94affbcb4c1c5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 6 Jan 2016 11:49:57 +0100 Subject: [PATCH 242/271] perf script: Add stat-cpi.py script Adding stat-cpi.py as an example of how to do stat scripting. It computes the CPI metrics from cycles and instructions events. The CPI is based performance metric showing the Cycles Per Instructions ratio, which helps to identify cycles-hungry code. Following stat record/report/script combinations could be used: - get CPI for given workload $ perf stat -e cycles,instructions record ls SNIP Performance counter stats for 'ls': 2,904,431 cycles 3,346,878 instructions # 1.15 insns per cycle 0.001782686 seconds time elapsed $ perf script -s ./scripts/python/stat-cpi.py 0.001783: cpu -1, thread -1 -> cpi 0.867803 (2904431/3346878) $ perf stat -e cycles,instructions record ls | perf script -s ./scripts/python/stat-cpi.py SNIP 0.001730: cpu -1, thread -1 -> cpi 0.869026 (2928292/3369627) - get CPI systemwide: $ perf stat -e cycles,instructions -a -I 1000 record sleep 3 # time counts unit events 1.000158618 594,274,711 cycles (100.00%) 1.000158618 441,898,250 instructions 2.000350973 567,649,705 cycles (100.00%) 2.000350973 432,669,206 instructions 3.000559210 561,940,430 cycles (100.00%) 3.000559210 420,403,465 instructions 3.000670798 780,105 cycles (100.00%) 3.000670798 326,516 instructions $ perf script -s ./scripts/python/stat-cpi.py 1.000159: cpu -1, thread -1 -> cpi 1.344823 (594274711/441898250) 2.000351: cpu -1, thread -1 -> cpi 1.311972 (567649705/432669206) 3.000559: cpu -1, thread -1 -> cpi 1.336669 (561940430/420403465) 3.000671: cpu -1, thread -1 -> cpi 2.389178 (780105/326516) $ perf stat -e cycles,instructions -a -I 1000 record sleep 3 | perf script -s ./scripts/python/stat-cpi.py 1.000202: cpu -1, thread -1 -> cpi 1.035091 (940778881/908885530) 2.000392: cpu -1, thread -1 -> cpi 1.442600 (627493992/434974455) 3.000545: cpu -1, thread -1 -> cpi 1.353612 (741463930/547766890) 3.000622: cpu -1, thread -1 -> cpi 2.642110 (784083/296764) 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/1452077397-31958-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/stat-cpi.py | 77 +++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tools/perf/scripts/python/stat-cpi.py diff --git a/tools/perf/scripts/python/stat-cpi.py b/tools/perf/scripts/python/stat-cpi.py new file mode 100644 index 000000000000..8b60f343dd07 --- /dev/null +++ b/tools/perf/scripts/python/stat-cpi.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +data = {} +times = [] +threads = [] +cpus = [] + +def get_key(time, event, cpu, thread): + return "%d-%s-%d-%d" % (time, event, cpu, thread) + +def store_key(time, cpu, thread): + if (time not in times): + times.append(time) + + if (cpu not in cpus): + cpus.append(cpu) + + if (thread not in threads): + threads.append(thread) + +def store(time, event, cpu, thread, val, ena, run): + #print "event %s cpu %d, thread %d, time %d, val %d, ena %d, run %d" % \ + # (event, cpu, thread, time, val, ena, run) + + store_key(time, cpu, thread) + key = get_key(time, event, cpu, thread) + data[key] = [ val, ena, run] + +def get(time, event, cpu, thread): + key = get_key(time, event, cpu, thread) + return data[key][0] + +def stat__cycles_k(cpu, thread, time, val, ena, run): + store(time, "cycles", cpu, thread, val, ena, run); + +def stat__instructions_k(cpu, thread, time, val, ena, run): + store(time, "instructions", cpu, thread, val, ena, run); + +def stat__cycles_u(cpu, thread, time, val, ena, run): + store(time, "cycles", cpu, thread, val, ena, run); + +def stat__instructions_u(cpu, thread, time, val, ena, run): + store(time, "instructions", cpu, thread, val, ena, run); + +def stat__cycles(cpu, thread, time, val, ena, run): + store(time, "cycles", cpu, thread, val, ena, run); + +def stat__instructions(cpu, thread, time, val, ena, run): + store(time, "instructions", cpu, thread, val, ena, run); + +def stat__interval(time): + for cpu in cpus: + for thread in threads: + cyc = get(time, "cycles", cpu, thread) + ins = get(time, "instructions", cpu, thread) + cpi = 0 + + if ins != 0: + cpi = cyc/float(ins) + + print "%15f: cpu %d, thread %d -> cpi %f (%d/%d)" % (time/(float(1000000000)), cpu, thread, cpi, cyc, ins) + +def trace_end(): + pass +# XXX trace_end callback could be used as an alternative place +# to compute same values as in the script above: +# +# for time in times: +# for cpu in cpus: +# for thread in threads: +# cyc = get(time, "cycles", cpu, thread) +# ins = get(time, "instructions", cpu, thread) +# +# if ins != 0: +# cpi = cyc/float(ins) +# +# print "time %.9f, cpu %d, thread %d -> cpi %f" % (time/(float(1000000000)), cpu, thread, cpi) From 84530920de3c6ccb92c6661da784f6cdb66d3304 Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Wed, 6 Jan 2016 19:50:01 +0100 Subject: [PATCH 243/271] 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(+) 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) { From 4f4ba0e6afac4b181471f5f12bd87294bd8807f6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 09:54:56 -0300 Subject: [PATCH 244/271] perf tests: No need to set attr.sample_freq in the perf time to TSC test We were asking for a 4kHz sample_freq, making the test fail needlessly when the system reduced /proc/sys/kernel/perf_event_max_sample_rate below that. In this test we only look at the PERF_SAMPLE_TIME fields in PERF_RECORD_ meta events, no need to set sample_freq. Thanks to Namhyung for suggesting that max_sample_rate could be the reason for the test failure, seeing the 'perf test -vv' output I sent. Before: # echo 1000 > /proc/sys/kernel/perf_event_max_sample_rate # perf test TSC 45: Test converting perf time to TSC : FAILED! After: # perf test TSC 45: Test converting perf time to TSC : Ok # cat /proc/sys/kernel/perf_event_max_sample_rate 1000 Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-lcob05qhawkuvsyuu9g1fld5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/perf-time-to-tsc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/perf-time-to-tsc.c b/tools/perf/arch/x86/tests/perf-time-to-tsc.c index a289aa8a083a..9d29ee283ac5 100644 --- a/tools/perf/arch/x86/tests/perf-time-to-tsc.c +++ b/tools/perf/arch/x86/tests/perf-time-to-tsc.c @@ -41,7 +41,6 @@ int test__perf_time_to_tsc(int subtest __maybe_unused) .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 4000, .target = { .uses_mmap = true, }, From 5bae0250237f7a5ec4355f9920701de247b8db91 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 13:14:56 -0300 Subject: [PATCH 245/271] 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(+) 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); From 69ef8f475586974d3921c4799bfa75b8fef877a8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 13:17:00 -0300 Subject: [PATCH 246/271] perf test: Use "dummy" events in the PERF_RECORD_ test As we're test just the !PERF_RECORD_SAMPLE records. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-qp8radcz3il4q9wbnseh337d@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/perf-record.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 9d5f0b57c4c1..8dc0baba8798 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -45,7 +45,7 @@ int test__PERF_RECORD(int subtest __maybe_unused) }; cpu_set_t cpu_mask; size_t cpu_mask_size = sizeof(cpu_mask); - struct perf_evlist *evlist = perf_evlist__new_default(); + struct perf_evlist *evlist = perf_evlist__new_dummy(); struct perf_evsel *evsel; struct perf_sample sample; const char *cmd = "sleep"; @@ -61,6 +61,9 @@ int test__PERF_RECORD(int subtest __maybe_unused) int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; char sbuf[STRERR_BUFSIZE]; + if (evlist == NULL) /* Fallback for kernels lacking PERF_COUNT_SW_DUMMY */ + evlist = perf_evlist__new_default(); + if (evlist == NULL || argv == NULL) { pr_debug("Not enough memory to create evlist\n"); goto out; From 2e4f81ee90e801017115afb7397198f65e512031 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 13:20:22 -0300 Subject: [PATCH 247/271] perf test: No need for setting attr.sample_freq on the RECORD test We're not looking at PERF_RECORD_SAMPLE entries and now by default we use PERF_COUNT_SW_DUMMY, so just remove that setting. Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-cly7cnotktv5rqao13pkorem@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/perf-record.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 8dc0baba8798..1cc78cefe399 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c @@ -40,7 +40,6 @@ int test__PERF_RECORD(int subtest __maybe_unused) .uses_mmap = true, }, .no_buffering = true, - .freq = 10, .mmap_pages = 256, }; cpu_set_t cpu_mask; From 372b212263998ae8e3b2f80ce52af3168d749158 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 16:47:11 -0300 Subject: [PATCH 248/271] 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(+) 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 From 239849dde350c9f7356974ca4f62171f50a2c86e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 16:51:58 -0300 Subject: [PATCH 249/271] perf tests: No need to set attr.sample_freq for tracking !PERF_RECORD_SAMPLE We were asking for a 4kHz sample_freq, making the test fail needlessly when the system reduced /proc/sys/kernel/perf_event_max_sample_rate below that. Before: # perf test -vv dummy 23: Test using a dummy software event to keep tracking : --- start --- test child forked, pid 32421 ------------------------------------------------------------ perf_event_attr: type 1 size 112 config 0x9 { sample_period, sample_freq } 4000 sample_type IP|TID|ID|PERIOD sys_perf_event_open failed, error -22 Unable to open dummy and cycles event test child finished with -2 ---- end ---- Test using a dummy software event to keep tracking: Skip # [root@zoo ~]# cat /proc/sys/kernel/perf_event_max_sample_rate 1000 After: [root@zoo ~]# perf test dummy 23: Test using a dummy software event to keep tracking : Ok Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-487iquegrs2379e5n0pi0tcp@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/keep-tracking.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index a337a6da1f39..615813278334 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -55,7 +55,6 @@ int test__keep_tracking(int subtest __maybe_unused) .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 4000, .target = { .uses_mmap = true, }, From a831e67913e356be63d5bb0509fc1af3c4e6ceb4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 7 Jan 2016 16:59:27 -0300 Subject: [PATCH 250/271] perf tests: Give a bit more information on the CQM test failure path Before: $ perf test -v cqm 48: Test intel cqm nmi context read : --- start --- test child forked, pid 1681 parse_events failed test child finished with -2 ---- end ---- Test intel cqm nmi context read: Skip $ After: $ perf test -v cqm 48: Test intel cqm nmi context read : --- start --- test child forked, pid 1681 parse_events failed, is "intel_cqm/llc_occupancy/" available? test child finished with -2 ---- end ---- Test intel cqm nmi context read: Skip $ Cc: Adrian Hunter Cc: David Ahern Cc: Jiri Olsa Cc: Matt Fleming Cc: Namhyung Kim Cc: Wang Nan Link: http://lkml.kernel.org/n/tip-eidpiv5x4nkbsx37xwikbnir@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/intel-cqm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/tests/intel-cqm.c b/tools/perf/arch/x86/tests/intel-cqm.c index 94e0cb7462f9..3e89ba825f6b 100644 --- a/tools/perf/arch/x86/tests/intel-cqm.c +++ b/tools/perf/arch/x86/tests/intel-cqm.c @@ -54,7 +54,7 @@ int test__intel_cqm_count_nmi_context(int subtest __maybe_unused) ret = parse_events(evlist, "intel_cqm/llc_occupancy/", NULL); if (ret) { - pr_debug("parse_events failed\n"); + pr_debug("parse_events failed, is \"intel_cqm/llc_occupancy/\" available?\n"); err = TEST_SKIP; goto out; } From 552eb975b83756e3d95aeb5edaeb5c3c032b0021 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 10:46:52 -0300 Subject: [PATCH 251/271] 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/lib/{util/find_next_bit.c => find_bit.c} | 2 +- tools/perf/MANIFEST | 2 +- tools/perf/util/Build | 6 +++--- tools/perf/util/python-ext-sources | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename tools/lib/{util/find_next_bit.c => find_bit.c} (97%) diff --git a/tools/lib/util/find_next_bit.c b/tools/lib/find_bit.c similarity index 97% rename from tools/lib/util/find_next_bit.c rename to tools/lib/find_bit.c index 41b44f65a79e..732d8c38d2c3 100644 --- a/tools/lib/util/find_next_bit.c +++ b/tools/lib/find_bit.c @@ -1,6 +1,6 @@ /* find_next_bit.c: fallback find next bit implementation * - * Copied from lib/find_next_bit.c to tools/lib/next_bit.c + * Copied from lib/find_next_bit.c to tools/lib/find_bit.c * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index ce3932ee4893..b3db8dfebbf2 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -26,7 +26,7 @@ tools/lib/rbtree.c tools/lib/string.c tools/lib/symbol/kallsyms.c tools/lib/symbol/kallsyms.h -tools/lib/util/find_next_bit.c +tools/lib/find_bit.c tools/include/asm/atomic.h tools/include/asm/barrier.h tools/include/asm/bug.h 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 From 64af4e0da419ef9e9db0d34a3b5836adbf90a5e8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 11:26:43 -0300 Subject: [PATCH 252/271] 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/lib/find_bit.c | 103 ++++++++++++------------- tools/perf/util/include/linux/bitmap.h | 2 + 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/tools/lib/find_bit.c b/tools/lib/find_bit.c index 732d8c38d2c3..9122a9e80046 100644 --- a/tools/lib/find_bit.c +++ b/tools/lib/find_bit.c @@ -1,10 +1,17 @@ -/* find_next_bit.c: fallback find next bit implementation +/* bit search implementation * - * Copied from lib/find_next_bit.c to tools/lib/find_bit.c + * Copied from lib/find_bit.c to tools/lib/find_bit.c * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * + * Copyright (C) 2008 IBM Corporation + * 'find_last_bit' is written by Rusty Russell + * (Inspired by David Howell's find_next_bit implementation) + * + * Rewritten by Yury Norov to decrease + * size and improve performance, 2015. + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -12,10 +19,41 @@ */ #include -#include -#include +#include +#include -#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) +#if !defined(find_next_bit) + +/* + * This is a common helper function for find_next_bit and + * find_next_zero_bit. The difference is the "invert" argument, which + * is XORed with each fetched word before searching it for one bits. + */ +static unsigned long _find_next_bit(const unsigned long *addr, + unsigned long nbits, unsigned long start, unsigned long invert) +{ + unsigned long tmp; + + if (!nbits || start >= nbits) + return nbits; + + tmp = addr[start / BITS_PER_LONG] ^ invert; + + /* Handle 1st word. */ + tmp &= BITMAP_FIRST_WORD_MASK(start); + start = round_down(start, BITS_PER_LONG); + + while (!tmp) { + start += BITS_PER_LONG; + if (start >= nbits) + return nbits; + + tmp = addr[start / BITS_PER_LONG] ^ invert; + } + + return min(start + __ffs(tmp), nbits); +} +#endif #ifndef find_next_bit /* @@ -24,40 +62,7 @@ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { - const unsigned long *p = addr + BITOP_WORD(offset); - unsigned long result = offset & ~(BITS_PER_LONG-1); - unsigned long tmp; - - if (offset >= size) - return size; - size -= result; - offset %= BITS_PER_LONG; - if (offset) { - tmp = *(p++); - tmp &= (~0UL << offset); - if (size < BITS_PER_LONG) - goto found_first; - if (tmp) - goto found_middle; - size -= BITS_PER_LONG; - result += BITS_PER_LONG; - } - while (size & ~(BITS_PER_LONG-1)) { - if ((tmp = *(p++))) - goto found_middle; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; - } - if (!size) - return result; - tmp = *p; - -found_first: - tmp &= (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found_middle: - return result + __ffs(tmp); + return _find_next_bit(addr, size, offset, 0UL); } #endif @@ -67,23 +72,13 @@ found_middle: */ unsigned long find_first_bit(const unsigned long *addr, unsigned long size) { - const unsigned long *p = addr; - unsigned long result = 0; - unsigned long tmp; + unsigned long idx; - while (size & ~(BITS_PER_LONG-1)) { - if ((tmp = *(p++))) - goto found; - result += BITS_PER_LONG; - size -= BITS_PER_LONG; + for (idx = 0; idx * BITS_PER_LONG < size; idx++) { + if (addr[idx]) + return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size); } - if (!size) - return result; - tmp = (*p) & (~0UL >> (BITS_PER_LONG - size)); - if (tmp == 0UL) /* Are any bits set? */ - return result + size; /* Nope. */ -found: - return result + __ffs(tmp); + return size; } #endif 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) ? \ From 915b0882c3108a21e9b3b5e176d3151ad522242d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 8 Jan 2016 12:33:37 -0300 Subject: [PATCH 253/271] 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 => }/include/linux/bitmap.h | 0 tools/{perf/util => lib}/bitmap.c | 0 tools/perf/util/Build | 5 +++++ tools/perf/util/python-ext-sources | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) rename tools/{perf/util => }/include/linux/bitmap.h (100%) rename tools/{perf/util => lib}/bitmap.c (100%) diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/include/linux/bitmap.h similarity index 100% rename from tools/perf/util/include/linux/bitmap.h rename to tools/include/linux/bitmap.h diff --git a/tools/perf/util/bitmap.c b/tools/lib/bitmap.c similarity index 100% rename from tools/perf/util/bitmap.c rename to tools/lib/bitmap.c 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/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 From 42b276a2351517409d55b1202a1fa8b05c0cdc99 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 5 Jan 2016 12:06:00 +0900 Subject: [PATCH 254/271] 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(+) 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; From 1e9abf8b03c8f9352f54171647296c41317679a4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 29 Nov 2015 23:24:17 +0900 Subject: [PATCH 255/271] 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(+) 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[] = { From cbd08b7335c9d559f424dcef7bea333605597490 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:04 +0100 Subject: [PATCH 256/271] 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/command-list.txt | 2 +- tools/perf/util/generate-cmdlist.sh | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index acc3ea7d90b7..ab5cbaa170d0 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt @@ -26,4 +26,4 @@ perf-stat mainporcelain common perf-test mainporcelain common perf-timechart mainporcelain common perf-top mainporcelain common -perf-trace mainporcelain common +perf-trace mainporcelain audit 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 "};" From 2d7c03e6b0c604decae33b0ce03e69b79b2a39a1 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Fri, 8 Jan 2016 14:23:57 +0000 Subject: [PATCH 257/271] perf tools: Add missing headers in perf's MANIFEST These lost headers are found in arm64 cross buildings, failing to build perf using tarballs generated using: $ make perf-targz-src-pkg Signed-off-by: Wang Nan Cc: Jiri Olsa Cc: Namhyung Kim Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1452263041-225488-3-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/MANIFEST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index b3db8dfebbf2..ddf922f93aa1 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -1,6 +1,7 @@ tools/perf tools/arch/alpha/include/asm/barrier.h tools/arch/arm/include/asm/barrier.h +tools/arch/arm64/include/asm/barrier.h tools/arch/ia64/include/asm/barrier.h tools/arch/mips/include/asm/barrier.h tools/arch/powerpc/include/asm/barrier.h @@ -30,6 +31,7 @@ tools/lib/find_bit.c tools/include/asm/atomic.h tools/include/asm/barrier.h tools/include/asm/bug.h +tools/include/asm-generic/atomic-gcc.h tools/include/asm-generic/barrier.h tools/include/asm-generic/bitops/arch_hweight.h tools/include/asm-generic/bitops/atomic.h From 9cdbc409626b29ab30f06a6393db6763f040f753 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:05 +0100 Subject: [PATCH 258/271] perf script: Align event name properly Adding code to align event names, so we get aligned output in case of multiple events with different names. Before: $ perf script :13757 13757 163918.230829: cpu/mem-snp-none/P: ffff88085f20d010 :13757 13757 163918.230832: cpu/mem-loads,ldlat=30/P: 7f5a5f719f00 :13757 13757 163918.230835: cpu/mem-loads,ldlat=30/P: 7f5a5f719f00 :13758 13758 163918.230838: cpu/mem-snp-none/P: ffff88085f4ad810 :13758 13758 163918.154093: cpu/mem-stores/P: ffff88085bb53f28 :13757 13757 163918.155264: cpu/mem-snp-hitm/P: 601080 ... After: $ perf script :13757 13757 163918.228831: cpu/mem-snp-none/P: ffffffff81a841c0 :13757 13757 163918.228834: cpu/mem-loads,ldlat=30/P: 7f5a5f719f08 :13757 13757 163918.228837: cpu/mem-loads,ldlat=30/P: 7f5a5f719f08 :13758 13758 163918.228837: cpu/mem-snp-none/P: ffff88085f4ad800 :13758 13758 163918.154093: cpu/mem-stores/P: ffff88085bb53f28 :13757 13757 163918.155264: cpu/mem-snp-hitm/P: 601080 ... Signed-off-by: Jiri Olsa Acked-by: David Ahern Cc: Adrian Hunter Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-9-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 5e2f9d20a296..c691214d820f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -617,9 +617,24 @@ struct perf_script { bool allocated; struct cpu_map *cpus; struct thread_map *threads; + int name_width; }; -static void process_event(struct perf_script *script __maybe_unused, union perf_event *event, +static int perf_evlist__max_name_len(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + int max = 0; + + evlist__for_each(evlist, evsel) { + int len = strlen(perf_evsel__name(evsel)); + + max = MAX(len, max); + } + + return max; +} + +static void process_event(struct perf_script *script, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct addr_location *al) { @@ -636,7 +651,12 @@ static void process_event(struct perf_script *script __maybe_unused, union perf_ if (PRINT_FIELD(EVNAME)) { const char *evname = perf_evsel__name(evsel); - printf("%s: ", evname ? evname : "[unknown]"); + + if (!script->name_width) + script->name_width = perf_evlist__max_name_len(script->session->evlist); + + printf("%*s: ", script->name_width, + evname ? evname : "[unknown]"); } if (print_flags) From 685c84154cef61dd7d961f36ab92f13c3ef5d354 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:06 +0100 Subject: [PATCH 259/271] perf tools: Include all tools/lib directory for tags/cscope/TAGS targets Besides lockdep we use all the 'tools/lib' code in perf, so include it completely in tags. 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-10-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 404e3b1c4e31..1025ea79b90b 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -488,7 +488,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html $(DOC_TARGETS): $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) -TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../include ../lib/bpf +TAG_FOLDERS= . ../lib ../include TAG_FILES= ../../include/uapi/linux/perf_event.h TAGS: From bb4ced29f5d5ff1d4d51b602dad34a0d15495a67 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:07 +0100 Subject: [PATCH 260/271] 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(-) 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 *); From b97511c5bc94ef12613f485ab82f989df04088da Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:08 +0100 Subject: [PATCH 261/271] 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/ui/hist.c | 12 ------------ tools/perf/util/sort.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 8263c0eb9fb5..bf2a66e254ea 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -443,7 +443,6 @@ LIST_HEAD(perf_hpp__sort_list); void perf_hpp__init(void) { - struct list_head *list; int i; for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { @@ -484,17 +483,6 @@ void perf_hpp__init(void) if (symbol_conf.show_total_period) hpp_dimension__add_output(PERF_HPP__PERIOD); - - /* prepend overhead field for backward compatiblity. */ - list = &perf_hpp__format[PERF_HPP__OVERHEAD].sort_list; - if (list_empty(list)) - list_add(list, &perf_hpp__sort_list); - - if (symbol_conf.cumulate_callchain) { - list = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC].sort_list; - if (list_empty(list)) - list_add(list, &perf_hpp__sort_list); - } } void perf_hpp__column_register(struct perf_hpp_fmt *format) 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); From 21e6d8428664293f203be3004dcd8d70f68ebdb9 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:09 +0100 Subject: [PATCH 262/271] perf diff: Use perf_hpp__register_sort_field interface Using perf_hpp__register_sort_field interface instead of directly adding the entry. 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-13-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 87063835d741..36ccc2b8827f 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -1207,7 +1207,7 @@ static int ui_init(void) BUG_ON(1); } - list_add(&fmt->sort_list, &perf_hpp__sort_list); + perf_hpp__register_sort_field(fmt); return 0; } From fc284be9d88528dd2a28d5471e40a6acde6c3036 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 10:14:10 +0100 Subject: [PATCH 263/271] 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(-) 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; From 14cbfbeb76540cc0c53fbb0ba34b3a4900ebe40f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 20:41:53 +0900 Subject: [PATCH 264/271] 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/Build | 1 + tools/perf/Documentation/tips.txt | 14 ++++++++++++++ tools/perf/Makefile.perf | 3 +++ tools/perf/builtin-report.c | 2 +- tools/perf/config/Makefile | 6 ++++++ tools/perf/perf.c | 4 ++++ tools/perf/util/util.c | 27 +++++++++++++++++++++++++++ tools/perf/util/util.h | 2 ++ 8 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tools/perf/Documentation/tips.txt diff --git a/tools/perf/Build b/tools/perf/Build index 00c4b8c3d8ca..6b67e6f4179f 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -41,6 +41,7 @@ CFLAGS_perf.o += -DPERF_HTML_PATH="BUILD_STR($(htmldir_SQ))" \ -DPREFIX="BUILD_STR($(prefix_SQ))" \ -include $(OUTPUT)PERF-VERSION-FILE CFLAGS_builtin-trace.o += -DSTRACE_GROUPS_DIR="BUILD_STR($(STRACE_GROUPS_DIR_SQ))" +CFLAGS_builtin-report.o += -DTIPDIR="BUILD_STR($(tipdir_SQ))" libperf-y += util/ libperf-y += arch/ diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt new file mode 100644 index 000000000000..a1c10e360db5 --- /dev/null +++ b/tools/perf/Documentation/tips.txt @@ -0,0 +1,14 @@ +For a higher level overview, try: perf report --sort comm,dso +Sample related events with: perf record -e '{cycles,instructions}:S' +Compare performance results with: perf diff [ ] +Boolean options have negative forms, e.g.: perf report --no-children +Customize output of perf script with: perf script -F event,ip,sym +Generate a script for your data: perf script -g +Save output of perf stat using: perf stat record +Create an archive with symtabs to analyse on other machine: perf archive +Search options using a keyword: perf report -h +Use parent filter to see specific call path: perf report -p +List events using substring match: perf list +To see list of saved events and attributes: perf evlist -v +Use --symfs if your symbol files are in non-standard locations +To see callchains in a more compact form: perf report -g folded diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 1025ea79b90b..0a22407e1d7d 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -567,6 +567,9 @@ endif $(call QUIET_INSTALL, perf_completion-script) \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \ $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' + $(call QUIET_INSTALL, perf-tip) \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(tip_instdir_SQ)'; \ + $(INSTALL) Documentation/tips.txt -t '$(DESTDIR_SQ)$(tip_instdir_SQ)' install-tests: all install-gtk $(call QUIET_INSTALL, tests) \ diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f10c663af996..d5a42ee12529 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -433,7 +433,7 @@ static int report__browse_hists(struct report *rep) int ret; struct perf_session *session = rep->session; struct perf_evlist *evlist = session->evlist; - const char *help = "For a higher level overview, try: perf report --sort comm,dso"; + const char *help = perf_tip(TIPDIR); switch (use_browser) { case 1: diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 18b2f96d0941..254d06e39bea 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -691,6 +691,7 @@ sharedir = $(prefix)/share template_dir = share/perf-core/templates STRACE_GROUPS_DIR = share/perf-core/strace/groups htmldir = share/doc/perf-doc +tipdir = share/doc/perf-tip ifeq ($(prefix),/usr) sysconfdir = /etc ETC_PERFCONFIG = $(sysconfdir)/perfconfig @@ -717,6 +718,7 @@ infodir_SQ = $(subst ','\'',$(infodir)) perfexecdir_SQ = $(subst ','\'',$(perfexecdir)) template_dir_SQ = $(subst ','\'',$(template_dir)) htmldir_SQ = $(subst ','\'',$(htmldir)) +tipdir_SQ = $(subst ','\'',$(tipdir)) prefix_SQ = $(subst ','\'',$(prefix)) sysconfdir_SQ = $(subst ','\'',$(sysconfdir)) libdir_SQ = $(subst ','\'',$(libdir)) @@ -724,12 +726,15 @@ libdir_SQ = $(subst ','\'',$(libdir)) ifneq ($(filter /%,$(firstword $(perfexecdir))),) perfexec_instdir = $(perfexecdir) STRACE_GROUPS_INSTDIR = $(STRACE_GROUPS_DIR) +tip_instdir = $(tipdir) else perfexec_instdir = $(prefix)/$(perfexecdir) STRACE_GROUPS_INSTDIR = $(prefix)/$(STRACE_GROUPS_DIR) +tip_instdir = $(prefix)/$(tipdir) endif perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) STRACE_GROUPS_INSTDIR_SQ = $(subst ','\'',$(STRACE_GROUPS_INSTDIR)) +tip_instdir_SQ = $(subst ','\'',$(tip_instdir)) # If we install to $(HOME) we keep the traceevent default: # $(HOME)/.traceevent/plugins @@ -770,6 +775,7 @@ $(call detected_var,ETC_PERFCONFIG_SQ) $(call detected_var,STRACE_GROUPS_DIR_SQ) $(call detected_var,prefix_SQ) $(call detected_var,perfexecdir_SQ) +$(call detected_var,tipdir_SQ) $(call detected_var,LIBDIR) $(call detected_var,GTK_CFLAGS) $(call detected_var,PERL_EMBED_CCOPTS) diff --git a/tools/perf/perf.c b/tools/perf/perf.c index cb1d2499c45c..a929618b8eb6 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -19,6 +19,8 @@ #include "util/debug.h" #include #include +#include +#include const char perf_usage_string[] = "perf [--version] [--help] [OPTIONS] COMMAND [ARGS]"; @@ -542,6 +544,8 @@ int main(int argc, const char **argv) if (!cmd) cmd = "perf-help"; + srandom(time(NULL)); + /* get debugfs/tracefs mount point from /proc/mounts */ tracing_path_mount(); 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 */ From 23df7f798435796aff07d641456326b81cb34a77 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 Jan 2016 10:13:59 +0100 Subject: [PATCH 265/271] 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(-) 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; } From d2190a8091124f832c8862ace3a3d7d70a2506a5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:13:58 +0100 Subject: [PATCH 266/271] 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/arch/x86/util/intel-bts.c | 4 +-- tools/perf/arch/x86/util/intel-pt.c | 4 +-- tools/perf/tests/keep-tracking.c | 2 +- tools/perf/tests/switch-tracking.c | 6 ++-- tools/perf/util/evlist.c | 42 ---------------------------- tools/perf/util/evlist.h | 4 --- 6 files changed, 8 insertions(+), 54 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 9b94ce520917..8d8150f1cf9b 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -327,7 +327,7 @@ static int intel_bts_snapshot_start(struct auxtrace_record *itr) evlist__for_each(btsr->evlist, evsel) { if (evsel->attr.type == btsr->intel_bts_pmu->type) - return perf_evlist__disable_event(btsr->evlist, evsel); + return perf_evsel__disable(evsel); } return -EINVAL; } @@ -340,7 +340,7 @@ static int intel_bts_snapshot_finish(struct auxtrace_record *itr) evlist__for_each(btsr->evlist, evsel) { if (evsel->attr.type == btsr->intel_bts_pmu->type) - return perf_evlist__enable_event(btsr->evlist, evsel); + return perf_evsel__enable(evsel); } return -EINVAL; } diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index b64d46285ebb..f05daacc9e78 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -725,7 +725,7 @@ static int intel_pt_snapshot_start(struct auxtrace_record *itr) evlist__for_each(ptr->evlist, evsel) { if (evsel->attr.type == ptr->intel_pt_pmu->type) - return perf_evlist__disable_event(ptr->evlist, evsel); + return perf_evsel__disable(evsel); } return -EINVAL; } @@ -738,7 +738,7 @@ static int intel_pt_snapshot_finish(struct auxtrace_record *itr) evlist__for_each(ptr->evlist, evsel) { if (evsel->attr.type == ptr->intel_pt_pmu->type) - return perf_evlist__enable_event(ptr->evlist, evsel); + return perf_evsel__enable(evsel); } return -EINVAL; } diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 615813278334..ddb78fae064a 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c @@ -123,7 +123,7 @@ int test__keep_tracking(int subtest __maybe_unused) evsel = perf_evlist__last(evlist); - CHECK__(perf_evlist__disable_event(evlist, evsel)); + CHECK__(perf_evsel__disable(evsel)); comm = "Test COMM 2"; CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0)); diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index dfbd8d69ce89..ebd80168d51e 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c @@ -455,7 +455,7 @@ int test__switch_tracking(int subtest __maybe_unused) perf_evlist__enable(evlist); - err = perf_evlist__disable_event(evlist, cpu_clocks_evsel); + err = perf_evsel__disable(cpu_clocks_evsel); if (err) { pr_debug("perf_evlist__disable_event failed!\n"); goto out_err; @@ -474,7 +474,7 @@ int test__switch_tracking(int subtest __maybe_unused) goto out_err; } - err = perf_evlist__disable_event(evlist, cycles_evsel); + err = perf_evsel__disable(cycles_evsel); if (err) { pr_debug("perf_evlist__disable_event failed!\n"); goto out_err; @@ -500,7 +500,7 @@ int test__switch_tracking(int subtest __maybe_unused) goto out_err; } - err = perf_evlist__enable_event(evlist, cycles_evsel); + err = perf_evsel__enable(cycles_evsel); if (err) { pr_debug("perf_evlist__disable_event failed!\n"); goto out_err; 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); From f22ed827a8d5ff5a85e7c8e865baaaaf71a8d0cc Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:00 +0100 Subject: [PATCH 267/271] 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(-) 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)); From 0ddf5246f70ecc04e1bb4c4dc2be65977d1c03a7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:01 +0100 Subject: [PATCH 268/271] 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(+) 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; } From 0ba98149f8c8b6b2ba36be9938afb731fa719004 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 10:14:02 +0100 Subject: [PATCH 269/271] 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(+) 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; From 5c0cf22477eaa890beeb4bc3554e5bebbea4b007 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 7 Jan 2016 14:30:22 +0100 Subject: [PATCH 270/271] perf record: Store data mmaps for dwarf unwind Currently we don't synthesize data mmap by default. It depends on -d option, that enables data address sampling. But we've seen cases (softice) where DWARF unwinder went through non executable mmaps, which we need to lookup in MAP__VARIABLE tree. Making data mmaps to be synthesized for dwarf unwind as well. Reported-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/20160107133022.GA32115@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 9c5cdc2c4471..dc4e0adf5c5b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -815,8 +815,12 @@ int record_parse_callchain_opt(const struct option *opt, } ret = parse_callchain_record_opt(arg, &callchain_param); - if (!ret) + if (!ret) { + /* Enable data address sampling for DWARF unwind. */ + if (callchain_param.record_mode == CALLCHAIN_DWARF) + record->sample_address = true; callchain_debug(); + } return ret; } From 775d8a1b0d75211cc6123915c6b5b688f2002478 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 7 Jan 2016 09:12:29 +0900 Subject: [PATCH 271/271] 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/Documentation/perf-evlist.txt | 3 +++ tools/perf/builtin-evlist.c | 11 ++++++++++- tools/perf/util/evsel.c | 23 +++++++++++++++++++++++ tools/perf/util/evsel.h | 1 + 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt index 1ceb3700ffbb..6f7200fb85cf 100644 --- a/tools/perf/Documentation/perf-evlist.txt +++ b/tools/perf/Documentation/perf-evlist.txt @@ -32,6 +32,9 @@ OPTIONS --group:: Show event group information. +--trace-fields:: + Show tracepoint field names. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-list[1], diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 08a7d36a2cf8..8a31f511e1a0 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -26,14 +26,22 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details .mode = PERF_DATA_MODE_READ, .force = details->force, }; + bool has_tracepoint = false; session = perf_session__new(&file, 0, NULL); if (session == NULL) return -1; - evlist__for_each(session->evlist, pos) + evlist__for_each(session->evlist, pos) { perf_evsel__fprintf(pos, details, stdout); + if (pos->attr.type == PERF_TYPE_TRACEPOINT) + has_tracepoint = true; + } + + if (has_tracepoint && !details->trace_fields) + printf("# Tip: use 'perf evlist --trace-fields' to show fields for tracepoint events\n"); + perf_session__delete(session); return 0; } @@ -49,6 +57,7 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) OPT_BOOLEAN('g', "group", &details.event_group, "Show event group information"), OPT_BOOLEAN('f', "force", &details.force, "don't complain, do it"), + OPT_BOOLEAN(0, "trace-fields", &details.trace_fields, "Show tracepoint fields"), OPT_END() }; const char * const evlist_usage[] = { 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,