2015-11-19 17:53:53

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [GIT PULL 00/37] perf/core improvements and fixes

Hi Ingo,

Please consider pulling, this was based on tip/perf/urgent and I did a
test merge of tip/perf/core with tip/perf/urgent and then with this branch,
haven't noticed problems,

Best regards,

- Arnaldo

The following changes since commit e15bf88a44d1fcb685754b2868b1cd28927af3aa:

Merge tag 'perf-urgent-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent (2015-11-18 06:56:48 +0100)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo

for you to fetch changes up to 2c6caff2b26fde8f3f87183f8c97f2cebfdbcb98:

perf ui/gtk: Support folded callchains (2015-11-19 13:19:26 -0300)

----------------------------------------------------------------
perf/core improvements and fixes:

User visible:

- Allows BPF scriptlets specify arguments to be fetched using
DWARF info, using a prologue generated at compile/build time (He Kuang, Wang Nan)

- Allow attaching BPF scriptlets to module symbols (Wang Nan)

- Allow attaching BPF scriptlets to userspace code using uprobe (Wang Nan)

- BPF programs now can specify 'perf probe' tunables via its section name,
separating key=val values using semicolons (Wang Nan)

Testing some of these new BPF features:

Use case: get callchains when receiving SSL packets, filter then in the
kernel, at arbitrary place.

# cat ssl.bpf.c
#define SEC(NAME) __attribute__((section(NAME), used))

struct pt_regs;

SEC("func=__inet_lookup_established hnum")
int func(struct pt_regs *ctx, int err, unsigned short port)
{
return err == 0 && port == 443;
}

char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
#
# perf record -a -g -e ssl.bpf.c
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.787 MB perf.data (3 samples) ]
# perf script | head -30
swapper 0 [000] 58783.268118: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
8a0f61 __inet_lookup_established (/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)
2dede5 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)

qemu-system-x86 9178 [003] 58785.792417: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
8a0f61 __inet_lookup_established (/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)
856660 netif_receive_skb_internal (/lib/modules/4.3.0+/build/vmlinux)
8566ec netif_receive_skb_sk (/lib/modules/4.3.0+/build/vmlinux)
430a br_handle_frame_finish ([bridge])
48bc br_handle_frame ([bridge])
855f44 __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux)
8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux)
#

Use 'perf probe' various options to list functions, see what variables can
be collected at any given point, experiment first collecting without a filter,
then filter, use it together with 'perf trace', 'perf top', with or without
callchains, if it explodes, please tell us!

- Introduce a new callchain mode: "folded", that will list per line
representations of all callchains for a give histogram entry, facilitating
'perf report' output processing by other tools, such as Brendan Gregg's
flamegraph tools (Namhyung Kim)

E.g:

# perf report | grep -v ^# | head
18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
|
---cpu_startup_entry
|
|--12.07%--start_secondary
|
--6.30%--rest_init
start_kernel
x86_64_start_reservations
x86_64_start_kernel
#

Becomes, in "folded" mode:

# perf report -g folded | grep -v ^# | head -5
18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
12.07% cpu_startup_entry;start_secondary
6.30% cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
16.90% 0.00% swapper [kernel.kallsyms] [k] call_cpuidle
11.23% call_cpuidle;cpu_startup_entry;start_secondary
5.67% call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
16.90% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter
11.23% cpuidle_enter;call_cpuidle;cpu_startup_entry;start_secondary
5.67% cpuidle_enter;call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
15.12% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter_state
#

The user can also select one of "count", "period" or "percent" as the first column.

Infrastructure:

- Fix multiple leaks found with valgrind and a refcount
debugger (Masami Hiramatsu)

- Add further 'perf test' entries for BPF and LLVM (Wang Nan)

- Improve 'perf test' to suport subtests, so that the series of tests
performed in the LLVM and BPF main tests appear in the default 'perf test'
output (Wang Nan)

- Move memdup() from tools/perf to tools/lib/string.c (Arnaldo Carvalho de Melo)

- Adopt strtobool() from the kernel into tools/lib/ (Wang Nan)

- Fix selftests_install tools/ Makefile rule (Kevin Hilman)

Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>

----------------------------------------------------------------
Arnaldo Carvalho de Melo (3):
perf test: Fix build of BPF and LLVM on older glibc libraries
tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c
perf tests: Pass the subtest index to each test routine

He Kuang (1):
perf bpf: Add prologue for BPF programs for fetching arguments

Kevin Hilman (1):
tools: Fix selftests_install Makefile rule

Masami Hiramatsu (9):
perf probe: Fix to free temporal Dwarf_Frame
perf machine: Fix machine__findnew_module_map to put registered map
perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references
perf machine: Fix to destroy kernel maps when machine exits
perf tools: Make perf_exec_path() always return malloc'd string
perf tools: Fix to put new map after inserting to map_groups in dso__load_sym
perf tools: Fix __dsos__addnew to put dso after adding it to the list
perf tools: Fix machine__create_kernel_maps to put kernel dso refcount
perf machine: Fix machine__findnew_module_map to put dso

Namhyung Kim (9):
perf report: Support folded callchain mode on --stdio
perf callchain: Abstract callchain print function
perf callchain: Add count fields to struct callchain_node
perf report: Add callchain value option
perf hists browser: Factor out hist_browser__show_callchain_list()
perf hists browser: Support flat callchains
perf hists browser: Support folded callchains
perf ui/gtk: Support flat callchains
perf ui/gtk: Support folded callchains

Wang Nan (14):
tools: Clone the kernel's strtobool function
bpf tools: Load a program with different instances using preprocessor
perf bpf: Add BPF_PROLOGUE config options for further patches
perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
perf bpf: Allow BPF program attach to uprobe events
perf bpf: Allow attaching BPF programs to modules symbols
perf bpf: Allow BPF program config probing options
perf bpf: Generate prologue for BPF programs
perf test: Test the BPF prologue adding infrastructure
perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux
perf bpf: Use same BPF program if arguments are identical
perf test: Print result for each LLVM subtest
perf test: Print result for each BPF subtest
perf test: Mute test cases error messages if verbose == 0

tools/Makefile | 2 +-
tools/include/linux/string.h | 11 +
tools/lib/bpf/libbpf.c | 146 ++++++++-
tools/lib/bpf/libbpf.h | 64 ++++
tools/lib/string.c | 62 ++++
tools/perf/Documentation/perf-report.txt | 14 +-
tools/perf/MANIFEST | 2 +
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/arch/x86/util/Build | 1 +
tools/perf/builtin-report.c | 4 +-
tools/perf/config/Makefile | 12 +
tools/perf/tests/.gitignore | 1 +
tools/perf/tests/Build | 9 +-
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-script-test-prologue.c | 35 +++
tools/perf/tests/bpf.c | 93 ++++--
tools/perf/tests/builtin-test.c | 112 ++++++-
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 | 75 +++--
tools/perf/tests/llvm.h | 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 | 89 +++---
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 +-
tools/perf/ui/browsers/hists.c | 315 +++++++++++++++++--
tools/perf/ui/gtk/hists.c | 148 ++++++++-
tools/perf/ui/stdio/hist.c | 94 +++++-
tools/perf/util/Build | 7 +
tools/perf/util/bpf-loader.c | 434 ++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 4 +
tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 ++
tools/perf/util/callchain.c | 135 +++++++-
tools/perf/util/callchain.h | 28 +-
tools/perf/util/dso.c | 2 +
tools/perf/util/exec_cmd.c | 21 +-
tools/perf/util/exec_cmd.h | 5 +-
tools/perf/util/help.c | 6 +-
tools/perf/util/include/linux/string.h | 3 -
tools/perf/util/machine.c | 17 +-
tools/perf/util/probe-event.c | 7 +-
tools/perf/util/probe-finder.c | 9 +-
tools/perf/util/string.c | 16 -
tools/perf/util/symbol-elf.c | 2 +
tools/perf/util/util.c | 3 +-
77 files changed, 2286 insertions(+), 282 deletions(-)
create mode 100644 tools/include/linux/string.h
create mode 100644 tools/lib/string.c
create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.h
delete mode 100644 tools/perf/util/include/linux/string.h


2015-11-19 17:53:59

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 01/37] perf test: Fix build of BPF and LLVM on older glibc libraries

From: Arnaldo Carvalho de Melo <[email protected]>

$ rpm -q glibc
glibc-2.12-1.166.el6_7.1.x86_64

<SNIP>
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
<SNIP>
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
<SNIP>

Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: [email protected]
Cc: Wang Nan <[email protected]>
Cc: Zefan Li <[email protected]>
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/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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);

--
2.1.0

2015-11-19 17:53:50

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 02/37] tools: Fix selftests_install Makefile rule

From: Kevin Hilman <[email protected]>

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 <[email protected]>
Cc: Bamvor Jian Zhang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Pali Rohar <[email protected]>
Cc: Pavel Machek <[email protected]>
Cc: Roberta Dobrescu <[email protected]>
Cc: Shuah Khan <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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)
--
2.1.0

2015-11-19 17:53:39

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 03/37] tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c

From: Arnaldo Carvalho de Melo <[email protected]>

That will contain more string functions with counterparts, sometimes
verbatim copies, in the kernel.

Acked-by: Wang Nan <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Alexey Dobriyan <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Link: http://lkml.kernel.org/n/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <linux/types.h> /* 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 <stdlib.h>
+#include <string.h>
+#include <linux/string.h>
+
+/**
+ * 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 <string.h>
-
-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)
{
/*
--
2.1.0

2015-11-19 18:02:56

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 04/37] tools: Clone the kernel's strtobool function

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <[email protected]>
+ */
+
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <linux/string.h>

/**
@@ -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;
+}
--
2.1.0

2015-11-19 18:02:59

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 05/37] bpf tools: Load a program with different instances using preprocessor

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
[ Enclosed multi-line if/else blocks with {}, (*func_ptr)() -> func_ptr() ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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.
--
2.1.0

2015-11-19 17:53:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 06/37] perf bpf: Add BPF_PROLOGUE config options for further patches

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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

--
2.1.0

2015-11-19 17:53:57

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 07/37] perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on

From: Wang Nan <[email protected]>

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 <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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
--
2.1.0

2015-11-19 18:02:54

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 08/37] perf bpf: Allow BPF program attach to uprobe events

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Changed the separator from \n to ; ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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
@@ -110,6 +110,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
}

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=<full path of file>",
+ .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)
{
struct perf_probe_event *pev = NULL;
@@ -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,
};

--
2.1.0

2015-11-19 17:55:42

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 09/37] perf bpf: Allow attaching BPF programs to modules symbols

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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=<module name> ",
+ .desc = "Set kprobe module",
+ .func = config__module,
+ }
};

static int
--
2.1.0

2015-11-19 17:54:00

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 10/37] perf bpf: Allow BPF program config probing options

From: Wang Nan <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <bpf/libbpf.h>
#include <linux/err.h>
+#include <linux/string.h>
#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=<module name> ",
.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;
}
--
2.1.0

2015-11-19 18:02:58

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 11/37] perf bpf: Add prologue for BPF programs for fetching arguments

From: He Kuang <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Wang Nan <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-loader.h"
+#include "bpf-prologue.h"
+#include "probe-finder.h"
+#include <dwarf-regs.h>
+#include <linux/filter.h>
+
+#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 <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_PROLOGUE_H
+#define __BPF_PROLOGUE_H
+
+#include <linux/compiler.h>
+#include <linux/filter.h>
+#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 */
--
2.1.0

2015-11-19 18:02:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 12/37] perf bpf: Generate prologue for BPF programs

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <linux/bpf.h>
#include <bpf/libbpf.h>
#include <linux/err.h>
#include <linux/string.h>
#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;
--
2.1.0

2015-11-19 18:02:50

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 13/37] perf test: Test the BPF prologue adding infrastructure

From: Wang Nan <[email protected]>

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
<SNIP>
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 <uapi/linux/fs.h>

#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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Added tools/perf/tests/llvm-src-prologue.c to .gitignore ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <tests/llvm.h>' > $@
+ $(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 <uapi/linux/fs.h>
+
+#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,
};

--
2.1.0

2015-11-19 18:02:53

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 14/37] perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;

--
2.1.0

2015-11-19 17:58:37

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 15/37] perf bpf: Use same BPF program if arguments are identical

From: Wang Nan <[email protected]>

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 <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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");
--
2.1.0

2015-11-19 18:00:07

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 16/37] perf tests: Pass the subtest index to each test routine

From: Arnaldo Carvalho de Melo <[email protected]>

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 <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Wang Nan <[email protected]>
Link: http://lkml.kernel.org/n/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 <stdio.h>
#include <stdlib.h>
+#include <linux/compiler.h>
#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;
--
2.1.0

2015-11-19 17:56:49

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 17/37] perf test: Print result for each LLVM subtest

From: Wang Nan <[email protected]>

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 <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Changed so that func is not on an anonymous union ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/builtin-test.c | 91 ++++++++++++++++++++++++++++++++++-------
tools/perf/tests/llvm.c | 65 ++++++++++++++---------------
tools/perf/tests/tests.h | 9 ++++
3 files changed, 115 insertions(+), 50 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);
-
- 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 (!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;
+
+ 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 <stdbool.h>
+
#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);

--
2.1.0

2015-11-19 18:00:18

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 18/37] perf test: Print result for each BPF subtest

From: Wang Nan <[email protected]>

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 <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Fixed up not to use .func in an anonymous union ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/bpf.c | 38 ++++++++++++++++++++++++++++----------
tools/perf/tests/builtin-test.c | 5 +++++
tools/perf/tests/tests.h | 2 ++
3 files changed, 35 insertions(+), 10 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);
+ err = __test__bpf(i);
+ return err;
+}

- if (err != TEST_OK)
- return err;
- }
+#else
+int test__bpf_subtest_get_nr(void)
+{
+ return 0;
+}

- return TEST_OK;
+const char *test__bpf_subtest_get_desc(int i __maybe_unused)
+{
+ return NULL;
}

-#else
-int test__bpf(void)
+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__)
--
2.1.0

2015-11-19 18:00:16

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 19/37] perf test: Mute test cases error messages if verbose == 0

From: Wang Nan <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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);
}
--
2.1.0

2015-11-19 18:02:52

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 20/37] perf probe: Fix to free temporal Dwarf_Frame

From: Masami Hiramatsu <[email protected]>

Since dwarf_cfi_addrframe returns malloc'd Dwarf_Frame object, it has to
be freed after it is used.

Signed-off-by: Masami Hiramatsu <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
--
2.1.0

2015-11-19 18:00:13

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 21/37] perf machine: Fix machine__findnew_module_map to put registered map

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
--
2.1.0

2015-11-19 18:00:10

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 22/37] perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
}
}
--
2.1.0

2015-11-19 18:00:03

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 23/37] perf machine: Fix to destroy kernel maps when machine exits

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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);
--
2.1.0

2015-11-19 17:58:39

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 24/37] perf tools: Make perf_exec_path() always return malloc'd string

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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) {
--
2.1.0

2015-11-19 18:00:00

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 25/37] perf tools: Fix to put new map after inserting to map_groups in dso__load_sym

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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
--
2.1.0

2015-11-19 18:02:49

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 26/37] perf tools: Fix __dsos__addnew to put dso after adding it to the list

From: Masami Hiramatsu <[email protected]>

__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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
}
--
2.1.0

2015-11-19 17:58:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 27/37] perf tools: Fix machine__create_kernel_maps to put kernel dso refcount

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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) {
--
2.1.0

2015-11-19 18:00:05

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 28/37] perf machine: Fix machine__findnew_module_map to put dso

From: Masami Hiramatsu <[email protected]>

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 <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
}
--
2.1.0

2015-11-19 17:56:56

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 29/37] perf report: Support folded callchain mode on --stdio

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Signed-off-by: Namhyung Kim <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Acked-by: Jiri Olsa <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 (<percent>)\n" \
HELP_PAD "print_limit:\tmaximum number of call graph entry (<number>)\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 {
--
2.1.0

2015-11-19 17:56:51

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 30/37] perf callchain: Abstract callchain print function

From: Namhyung Kim <[email protected]>

This is a preparation to support for printing other type of callchain
value like count or period.

Signed-off-by: Namhyung Kim <[email protected]>
Tested-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
[ renamed new _sprintf_ operation to _scnprintf_ ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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);

--
2.1.0

2015-11-19 17:55:59

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 31/37] perf callchain: Add count fields to struct callchain_node

From: Namhyung Kim <[email protected]>

It's to track the count of occurrences of the callchains.

Signed-off-by: Namhyung Kim <[email protected]>
Acked-by: Brendan Gregg <[email protected]>
Acked-by: Jiri Olsa <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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,
--
2.1.0

2015-11-19 17:56:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 32/37] perf report: Add callchain value option

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Acked-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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=<print_type,threshold[,print_limit],order,sort_key,branch>::
+--call-graph=<print_type,threshold[,print_limit],order,sort_key[,branch],value>::
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 (<number>)\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,
};

/*
--
2.1.0

2015-11-19 17:56:53

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 33/37] perf hists browser: Factor out hist_browser__show_callchain_list()

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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;
}
--
2.1.0

2015-11-19 17:55:58

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 34/37] perf hists browser: Support flat callchains

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Tested-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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 */
--
2.1.0

2015-11-19 17:58:35

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 35/37] perf hists browser: Support folded callchains

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Tested-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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,
--
2.1.0

2015-11-19 17:56:58

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 36/37] perf ui/gtk: Support flat callchains

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Tested-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Pekka Enberg <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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)
--
2.1.0

2015-11-19 17:56:34

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: [PATCH 37/37] perf ui/gtk: Support folded callchains

From: Namhyung Kim <[email protected]>

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 <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Tested-by: Brendan Gregg <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: David Ahern <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kan Liang <[email protected]>
Cc: Pekka Enberg <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
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);
}
--
2.1.0

Subject: RE: [GIT PULL 00/37] perf/core improvements and fixes

Hi Arnaldo,

I just have a question about the refcnt debugger patch. It seems
that the debugger itself is dropped from this series, would you
have a plan to merge it afterwards?

Since I'd like to change other refcnts with my refcnt API, and
also to add some features, I'd like to decide I'd better update
my local patches or wait for your work :)

Thanks!


>From: Arnaldo Carvalho de Melo [mailto:[email protected]]
>
>Hi Ingo,
>
> Please consider pulling, this was based on tip/perf/urgent and I did a
>test merge of tip/perf/core with tip/perf/urgent and then with this branch,
>haven't noticed problems,
>
>Best regards,
>
>- Arnaldo
>
>The following changes since commit e15bf88a44d1fcb685754b2868b1cd28927af3aa:
>
> Merge tag 'perf-urgent-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
>(2015-11-18 06:56:48 +0100)
>
>are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo
>
>for you to fetch changes up to 2c6caff2b26fde8f3f87183f8c97f2cebfdbcb98:
>
> perf ui/gtk: Support folded callchains (2015-11-19 13:19:26 -0300)
>
>----------------------------------------------------------------
>perf/core improvements and fixes:
>
>User visible:
>
>- Allows BPF scriptlets specify arguments to be fetched using
> DWARF info, using a prologue generated at compile/build time (He Kuang, Wang Nan)
>
>- Allow attaching BPF scriptlets to module symbols (Wang Nan)
>
>- Allow attaching BPF scriptlets to userspace code using uprobe (Wang Nan)
>
>- BPF programs now can specify 'perf probe' tunables via its section name,
> separating key=val values using semicolons (Wang Nan)
>
>Testing some of these new BPF features:
>
>Use case: get callchains when receiving SSL packets, filter then in the
> kernel, at arbitrary place.
>
> # cat ssl.bpf.c
> #define SEC(NAME) __attribute__((section(NAME), used))
>
> struct pt_regs;
>
> SEC("func=__inet_lookup_established hnum")
> int func(struct pt_regs *ctx, int err, unsigned short port)
> {
> return err == 0 && port == 443;
> }
>
> char _license[] SEC("license") = "GPL";
> int _version SEC("version") = LINUX_VERSION_CODE;
> #
> # perf record -a -g -e ssl.bpf.c
> ^C[ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.787 MB perf.data (3 samples) ]
> # perf script | head -30
> swapper 0 [000] 58783.268118: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
> 8a0f61 __inet_lookup_established (/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)
> 2dede5 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)
>
> qemu-system-x86 9178 [003] 58785.792417: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
> 8a0f61 __inet_lookup_established (/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)
> 856660 netif_receive_skb_internal (/lib/modules/4.3.0+/build/vmlinux)
> 8566ec netif_receive_skb_sk (/lib/modules/4.3.0+/build/vmlinux)
> 430a br_handle_frame_finish ([bridge])
> 48bc br_handle_frame ([bridge])
> 855f44 __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux)
> 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux)
> #
>
> Use 'perf probe' various options to list functions, see what variables can
> be collected at any given point, experiment first collecting without a filter,
> then filter, use it together with 'perf trace', 'perf top', with or without
> callchains, if it explodes, please tell us!
>
>- Introduce a new callchain mode: "folded", that will list per line
> representations of all callchains for a give histogram entry, facilitating
> 'perf report' output processing by other tools, such as Brendan Gregg's
> flamegraph tools (Namhyung Kim)
>
> E.g:
>
> # perf report | grep -v ^# | head
> 18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
> |
> ---cpu_startup_entry
> |
> |--12.07%--start_secondary
> |
> --6.30%--rest_init
> start_kernel
> x86_64_start_reservations
> x86_64_start_kernel
> #
>
> Becomes, in "folded" mode:
>
> # perf report -g folded | grep -v ^# | head -5
> 18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
> 12.07% cpu_startup_entry;start_secondary
> 6.30% cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 16.90% 0.00% swapper [kernel.kallsyms] [k] call_cpuidle
> 11.23% call_cpuidle;cpu_startup_entry;start_secondary
> 5.67% call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 16.90% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter
> 11.23% cpuidle_enter;call_cpuidle;cpu_startup_entry;start_secondary
> 5.67%
>cpuidle_enter;call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 15.12% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter_state
> #
>
> The user can also select one of "count", "period" or "percent" as the first column.
>
>Infrastructure:
>
>- Fix multiple leaks found with valgrind and a refcount
> debugger (Masami Hiramatsu)
>
>- Add further 'perf test' entries for BPF and LLVM (Wang Nan)
>
>- Improve 'perf test' to suport subtests, so that the series of tests
> performed in the LLVM and BPF main tests appear in the default 'perf test'
> output (Wang Nan)
>
>- Move memdup() from tools/perf to tools/lib/string.c (Arnaldo Carvalho de Melo)
>
>- Adopt strtobool() from the kernel into tools/lib/ (Wang Nan)
>
>- Fix selftests_install tools/ Makefile rule (Kevin Hilman)
>
>Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
>
>----------------------------------------------------------------
>Arnaldo Carvalho de Melo (3):
> perf test: Fix build of BPF and LLVM on older glibc libraries
> tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c
> perf tests: Pass the subtest index to each test routine
>
>He Kuang (1):
> perf bpf: Add prologue for BPF programs for fetching arguments
>
>Kevin Hilman (1):
> tools: Fix selftests_install Makefile rule
>
>Masami Hiramatsu (9):
> perf probe: Fix to free temporal Dwarf_Frame
> perf machine: Fix machine__findnew_module_map to put registered map
> perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references
> perf machine: Fix to destroy kernel maps when machine exits
> perf tools: Make perf_exec_path() always return malloc'd string
> perf tools: Fix to put new map after inserting to map_groups in dso__load_sym
> perf tools: Fix __dsos__addnew to put dso after adding it to the list
> perf tools: Fix machine__create_kernel_maps to put kernel dso refcount
> perf machine: Fix machine__findnew_module_map to put dso
>
>Namhyung Kim (9):
> perf report: Support folded callchain mode on --stdio
> perf callchain: Abstract callchain print function
> perf callchain: Add count fields to struct callchain_node
> perf report: Add callchain value option
> perf hists browser: Factor out hist_browser__show_callchain_list()
> perf hists browser: Support flat callchains
> perf hists browser: Support folded callchains
> perf ui/gtk: Support flat callchains
> perf ui/gtk: Support folded callchains
>
>Wang Nan (14):
> tools: Clone the kernel's strtobool function
> bpf tools: Load a program with different instances using preprocessor
> perf bpf: Add BPF_PROLOGUE config options for further patches
> perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
> perf bpf: Allow BPF program attach to uprobe events
> perf bpf: Allow attaching BPF programs to modules symbols
> perf bpf: Allow BPF program config probing options
> perf bpf: Generate prologue for BPF programs
> perf test: Test the BPF prologue adding infrastructure
> perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux
> perf bpf: Use same BPF program if arguments are identical
> perf test: Print result for each LLVM subtest
> perf test: Print result for each BPF subtest
> perf test: Mute test cases error messages if verbose == 0
>
> tools/Makefile | 2 +-
> tools/include/linux/string.h | 11 +
> tools/lib/bpf/libbpf.c | 146 ++++++++-
> tools/lib/bpf/libbpf.h | 64 ++++
> tools/lib/string.c | 62 ++++
> tools/perf/Documentation/perf-report.txt | 14 +-
> tools/perf/MANIFEST | 2 +
> 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/arch/x86/util/Build | 1 +
> tools/perf/builtin-report.c | 4 +-
> tools/perf/config/Makefile | 12 +
> tools/perf/tests/.gitignore | 1 +
> tools/perf/tests/Build | 9 +-
> 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-script-test-prologue.c | 35 +++
> tools/perf/tests/bpf.c | 93 ++++--
> tools/perf/tests/builtin-test.c | 112 ++++++-
> 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 | 75 +++--
> tools/perf/tests/llvm.h | 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 | 89 +++---
> 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 +-
> tools/perf/ui/browsers/hists.c | 315 +++++++++++++++++--
> tools/perf/ui/gtk/hists.c | 148 ++++++++-
> tools/perf/ui/stdio/hist.c | 94 +++++-
> tools/perf/util/Build | 7 +
> tools/perf/util/bpf-loader.c | 434 ++++++++++++++++++++++++-
> tools/perf/util/bpf-loader.h | 4 +
> tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++
> tools/perf/util/bpf-prologue.h | 34 ++
> tools/perf/util/callchain.c | 135 +++++++-
> tools/perf/util/callchain.h | 28 +-
> tools/perf/util/dso.c | 2 +
> tools/perf/util/exec_cmd.c | 21 +-
> tools/perf/util/exec_cmd.h | 5 +-
> tools/perf/util/help.c | 6 +-
> tools/perf/util/include/linux/string.h | 3 -
> tools/perf/util/machine.c | 17 +-
> tools/perf/util/probe-event.c | 7 +-
> tools/perf/util/probe-finder.c | 9 +-
> tools/perf/util/string.c | 16 -
> tools/perf/util/symbol-elf.c | 2 +
> tools/perf/util/util.c | 3 +-
> 77 files changed, 2286 insertions(+), 282 deletions(-)
> create mode 100644 tools/include/linux/string.h
> create mode 100644 tools/lib/string.c
> create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.h
> delete mode 100644 tools/perf/util/include/linux/string.h
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-11-20 12:08:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [GIT PULL 00/37] perf/core improvements and fixes

Em Fri, Nov 20, 2015 at 10:01:30AM +0000, 平松雅巳 / HIRAMATU,MASAMI escreveu:
> Hi Arnaldo,
>
> I just have a question about the refcnt debugger patch. It seems
> that the debugger itself is dropped from this series, would you
> have a plan to merge it afterwards?

I concentrated on the fixes, leaving the debugger itself to be reviewed
later.

> Since I'd like to change other refcnts with my refcnt API, and
> also to add some features, I'd like to decide I'd better update
> my local patches or wait for your work :)

You can continue using it as you did, and the fixes can continue to be
applied before the debugger itself gets reviewed.

- Arnaldo

Subject: RE: [GIT PULL 00/37] perf/core improvements and fixes

>From: 'Arnaldo Carvalho de Melo' [mailto:[email protected]]
>
>Em Fri, Nov 20, 2015 at 10:01:30AM +0000, 平松雅巳 / HIRAMATU,MASAMI escreveu:
>> Hi Arnaldo,
>>
>> I just have a question about the refcnt debugger patch. It seems
>> that the debugger itself is dropped from this series, would you
>> have a plan to merge it afterwards?
>
>I concentrated on the fixes, leaving the debugger itself to be reviewed
>later.
>
>> Since I'd like to change other refcnts with my refcnt API, and
>> also to add some features, I'd like to decide I'd better update
>> my local patches or wait for your work :)
>
>You can continue using it as you did, and the fixes can continue to be
>applied before the debugger itself gets reviewed.

OK, so I'll continue to play on my series :)

Thank you!

--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

2015-11-23 08:16:53

by Ingo Molnar

[permalink] [raw]
Subject: Re: [GIT PULL 00/37] perf/core improvements and fixes


* Arnaldo Carvalho de Melo <[email protected]> wrote:

> Hi Ingo,
>
> Please consider pulling, this was based on tip/perf/urgent and I did a
> test merge of tip/perf/core with tip/perf/urgent and then with this branch,
> haven't noticed problems,
>
> Best regards,
>
> - Arnaldo
>
> The following changes since commit e15bf88a44d1fcb685754b2868b1cd28927af3aa:
>
> Merge tag 'perf-urgent-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent (2015-11-18 06:56:48 +0100)
>
> are available in the git repository at:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo
>
> for you to fetch changes up to 2c6caff2b26fde8f3f87183f8c97f2cebfdbcb98:
>
> perf ui/gtk: Support folded callchains (2015-11-19 13:19:26 -0300)
>
> ----------------------------------------------------------------
> perf/core improvements and fixes:
>
> User visible:
>
> - Allows BPF scriptlets specify arguments to be fetched using
> DWARF info, using a prologue generated at compile/build time (He Kuang, Wang Nan)
>
> - Allow attaching BPF scriptlets to module symbols (Wang Nan)
>
> - Allow attaching BPF scriptlets to userspace code using uprobe (Wang Nan)
>
> - BPF programs now can specify 'perf probe' tunables via its section name,
> separating key=val values using semicolons (Wang Nan)
>
> Testing some of these new BPF features:
>
> Use case: get callchains when receiving SSL packets, filter then in the
> kernel, at arbitrary place.
>
> # cat ssl.bpf.c
> #define SEC(NAME) __attribute__((section(NAME), used))
>
> struct pt_regs;
>
> SEC("func=__inet_lookup_established hnum")
> int func(struct pt_regs *ctx, int err, unsigned short port)
> {
> return err == 0 && port == 443;
> }
>
> char _license[] SEC("license") = "GPL";
> int _version SEC("version") = LINUX_VERSION_CODE;
> #
> # perf record -a -g -e ssl.bpf.c
> ^C[ perf record: Woken up 1 times to write data ]
> [ perf record: Captured and wrote 0.787 MB perf.data (3 samples) ]
> # perf script | head -30
> swapper 0 [000] 58783.268118: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
> 8a0f61 __inet_lookup_established (/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)
> 2dede5 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)
>
> qemu-system-x86 9178 [003] 58785.792417: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
> 8a0f61 __inet_lookup_established (/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)
> 856660 netif_receive_skb_internal (/lib/modules/4.3.0+/build/vmlinux)
> 8566ec netif_receive_skb_sk (/lib/modules/4.3.0+/build/vmlinux)
> 430a br_handle_frame_finish ([bridge])
> 48bc br_handle_frame ([bridge])
> 855f44 __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux)
> 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux)
> #
>
> Use 'perf probe' various options to list functions, see what variables can
> be collected at any given point, experiment first collecting without a filter,
> then filter, use it together with 'perf trace', 'perf top', with or without
> callchains, if it explodes, please tell us!
>
> - Introduce a new callchain mode: "folded", that will list per line
> representations of all callchains for a give histogram entry, facilitating
> 'perf report' output processing by other tools, such as Brendan Gregg's
> flamegraph tools (Namhyung Kim)
>
> E.g:
>
> # perf report | grep -v ^# | head
> 18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
> |
> ---cpu_startup_entry
> |
> |--12.07%--start_secondary
> |
> --6.30%--rest_init
> start_kernel
> x86_64_start_reservations
> x86_64_start_kernel
> #
>
> Becomes, in "folded" mode:
>
> # perf report -g folded | grep -v ^# | head -5
> 18.37% 0.00% swapper [kernel.kallsyms] [k] cpu_startup_entry
> 12.07% cpu_startup_entry;start_secondary
> 6.30% cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 16.90% 0.00% swapper [kernel.kallsyms] [k] call_cpuidle
> 11.23% call_cpuidle;cpu_startup_entry;start_secondary
> 5.67% call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 16.90% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter
> 11.23% cpuidle_enter;call_cpuidle;cpu_startup_entry;start_secondary
> 5.67% cpuidle_enter;call_cpuidle;cpu_startup_entry;rest_init;start_kernel;x86_64_start_reservations;x86_64_start_kernel
> 15.12% 0.00% swapper [kernel.kallsyms] [k] cpuidle_enter_state
> #
>
> The user can also select one of "count", "period" or "percent" as the first column.
>
> Infrastructure:
>
> - Fix multiple leaks found with valgrind and a refcount
> debugger (Masami Hiramatsu)
>
> - Add further 'perf test' entries for BPF and LLVM (Wang Nan)
>
> - Improve 'perf test' to suport subtests, so that the series of tests
> performed in the LLVM and BPF main tests appear in the default 'perf test'
> output (Wang Nan)
>
> - Move memdup() from tools/perf to tools/lib/string.c (Arnaldo Carvalho de Melo)
>
> - Adopt strtobool() from the kernel into tools/lib/ (Wang Nan)
>
> - Fix selftests_install tools/ Makefile rule (Kevin Hilman)
>
> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
>
> ----------------------------------------------------------------
> Arnaldo Carvalho de Melo (3):
> perf test: Fix build of BPF and LLVM on older glibc libraries
> tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c
> perf tests: Pass the subtest index to each test routine
>
> He Kuang (1):
> perf bpf: Add prologue for BPF programs for fetching arguments
>
> Kevin Hilman (1):
> tools: Fix selftests_install Makefile rule
>
> Masami Hiramatsu (9):
> perf probe: Fix to free temporal Dwarf_Frame
> perf machine: Fix machine__findnew_module_map to put registered map
> perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references
> perf machine: Fix to destroy kernel maps when machine exits
> perf tools: Make perf_exec_path() always return malloc'd string
> perf tools: Fix to put new map after inserting to map_groups in dso__load_sym
> perf tools: Fix __dsos__addnew to put dso after adding it to the list
> perf tools: Fix machine__create_kernel_maps to put kernel dso refcount
> perf machine: Fix machine__findnew_module_map to put dso
>
> Namhyung Kim (9):
> perf report: Support folded callchain mode on --stdio
> perf callchain: Abstract callchain print function
> perf callchain: Add count fields to struct callchain_node
> perf report: Add callchain value option
> perf hists browser: Factor out hist_browser__show_callchain_list()
> perf hists browser: Support flat callchains
> perf hists browser: Support folded callchains
> perf ui/gtk: Support flat callchains
> perf ui/gtk: Support folded callchains
>
> Wang Nan (14):
> tools: Clone the kernel's strtobool function
> bpf tools: Load a program with different instances using preprocessor
> perf bpf: Add BPF_PROLOGUE config options for further patches
> perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
> perf bpf: Allow BPF program attach to uprobe events
> perf bpf: Allow attaching BPF programs to modules symbols
> perf bpf: Allow BPF program config probing options
> perf bpf: Generate prologue for BPF programs
> perf test: Test the BPF prologue adding infrastructure
> perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux
> perf bpf: Use same BPF program if arguments are identical
> perf test: Print result for each LLVM subtest
> perf test: Print result for each BPF subtest
> perf test: Mute test cases error messages if verbose == 0
>
> tools/Makefile | 2 +-
> tools/include/linux/string.h | 11 +
> tools/lib/bpf/libbpf.c | 146 ++++++++-
> tools/lib/bpf/libbpf.h | 64 ++++
> tools/lib/string.c | 62 ++++
> tools/perf/Documentation/perf-report.txt | 14 +-
> tools/perf/MANIFEST | 2 +
> 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/arch/x86/util/Build | 1 +
> tools/perf/builtin-report.c | 4 +-
> tools/perf/config/Makefile | 12 +
> tools/perf/tests/.gitignore | 1 +
> tools/perf/tests/Build | 9 +-
> 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-script-test-prologue.c | 35 +++
> tools/perf/tests/bpf.c | 93 ++++--
> tools/perf/tests/builtin-test.c | 112 ++++++-
> 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 | 75 +++--
> tools/perf/tests/llvm.h | 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 | 89 +++---
> 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 +-
> tools/perf/ui/browsers/hists.c | 315 +++++++++++++++++--
> tools/perf/ui/gtk/hists.c | 148 ++++++++-
> tools/perf/ui/stdio/hist.c | 94 +++++-
> tools/perf/util/Build | 7 +
> tools/perf/util/bpf-loader.c | 434 ++++++++++++++++++++++++-
> tools/perf/util/bpf-loader.h | 4 +
> tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++
> tools/perf/util/bpf-prologue.h | 34 ++
> tools/perf/util/callchain.c | 135 +++++++-
> tools/perf/util/callchain.h | 28 +-
> tools/perf/util/dso.c | 2 +
> tools/perf/util/exec_cmd.c | 21 +-
> tools/perf/util/exec_cmd.h | 5 +-
> tools/perf/util/help.c | 6 +-
> tools/perf/util/include/linux/string.h | 3 -
> tools/perf/util/machine.c | 17 +-
> tools/perf/util/probe-event.c | 7 +-
> tools/perf/util/probe-finder.c | 9 +-
> tools/perf/util/string.c | 16 -
> tools/perf/util/symbol-elf.c | 2 +
> tools/perf/util/util.c | 3 +-
> 77 files changed, 2286 insertions(+), 282 deletions(-)
> create mode 100644 tools/include/linux/string.h
> create mode 100644 tools/lib/string.c
> create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.h
> delete mode 100644 tools/perf/util/include/linux/string.h

Pulled into tip:perf/core, thanks a lot Arnaldo!

Ingo

2015-11-23 14:35:37

by Frederic Weisbecker

[permalink] [raw]
Subject: Re: [PATCH 31/37] perf callchain: Add count fields to struct callchain_node

Namhyung,

On Thu, Nov 19, 2015 at 02:53:17PM -0300, Arnaldo Carvalho de Melo wrote:
> From: Namhyung Kim <[email protected]>
>
> It's to track the count of occurrences of the callchains.

Please explain why you do something like this in the changelog, even just a single
line to tell which feature is going to use this and why.

No need to resend just for that, it's just for future patches.

Thanks.

2015-11-23 15:16:53

by Frederic Weisbecker

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> From: Namhyung Kim <[email protected]>
>
> 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 <[email protected]>
> Tested-by: Arnaldo Carvalho de Melo <[email protected]>
> Tested-by: Brendan Gregg <[email protected]>
> Cc: Andi Kleen <[email protected]>
> Cc: David Ahern <[email protected]>
> Cc: Frederic Weisbecker <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Kan Liang <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Link: http://lkml.kernel.org/r/[email protected]
> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
> ---

[...]

> +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;

I'm a bit puzzled with this, can't we rewind through the parents on printing or adding
to the flat rbtree instead of having this parent_val field?

Thanks.

2015-11-24 05:15:53

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 31/37] perf callchain: Add count fields to struct callchain_node

Hi Frederic,

On Mon, Nov 23, 2015 at 03:35:30PM +0100, Frederic Weisbecker wrote:
> Namhyung,
>
> On Thu, Nov 19, 2015 at 02:53:17PM -0300, Arnaldo Carvalho de Melo wrote:
> > From: Namhyung Kim <[email protected]>
> >
> > It's to track the count of occurrences of the callchains.
>
> Please explain why you do something like this in the changelog, even just a single
> line to tell which feature is going to use this and why.
>
> No need to resend just for that, it's just for future patches.

OK, I'll keep in mind that.

This patch is a preparation for supporting different callchain value
output style. Instead of current percent output, it could have period
or count with this change.

Thanks,
Namhyung

2015-11-24 05:27:13

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > From: Namhyung Kim <[email protected]>
> >
> > 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 <[email protected]>
> > Tested-by: Arnaldo Carvalho de Melo <[email protected]>
> > Tested-by: Brendan Gregg <[email protected]>
> > Cc: Andi Kleen <[email protected]>
> > Cc: David Ahern <[email protected]>
> > Cc: Frederic Weisbecker <[email protected]>
> > Cc: Jiri Olsa <[email protected]>
> > Cc: Kan Liang <[email protected]>
> > Cc: Peter Zijlstra <[email protected]>
> > Link: http://lkml.kernel.org/r/[email protected]
> > Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
> > ---
>
> [...]
>
> > +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;
>
> I'm a bit puzzled with this, can't we rewind through the parents on printing or adding
> to the flat rbtree instead of having this parent_val field?

Yes, this code is to simplify things on parent nodes. Maybe we could
go up to parents and print the callchain list there as you said.

However, problem I think is how to handle 'has_children' information
on parents. That info controls folding status of each callchain. As
the info is in the struct callchain_list and flat or folded callchain
mode require the info should be in the top-most entry, I cannot share
entries in parent nodes.

Thus I simply copied callchain lists in parents to leaf nodes. Yes,
it will consume some memory but can simplify the code.

Thank you for your review anyway!
Namhyung

2015-11-24 14:46:03

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:
> On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> > On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > > From: Namhyung Kim <[email protected]>
> > [...]
> >
> > > +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;
> >
> > I'm a bit puzzled with this, can't we rewind through the parents on printing or adding
> > to the flat rbtree instead of having this parent_val field?
>
> Yes, this code is to simplify things on parent nodes. Maybe we could
> go up to parents and print the callchain list there as you said.
>
> However, problem I think is how to handle 'has_children' information
> on parents. That info controls folding status of each callchain. As
> the info is in the struct callchain_list and flat or folded callchain
> mode require the info should be in the top-most entry, I cannot share
> entries in parent nodes.
>
> Thus I simply copied callchain lists in parents to leaf nodes. Yes,
> it will consume some memory but can simplify the code.

I haven't done any measuring, but I'm noticing that 'perf top -g' is
showing more warnings about not being able to process events fast enough
and so ends up losing events, I tried with --max-stack 16 and it helped,
this is just a heads up.

Perhaps my workstation workloads are gettning deeper callchains over
time, but perhaps this is the cost of processing callchains that is
increasing, I need to stop and try to quantify this.

We really need to look at reducing the overhead of processing
callchains.

- Arnaldo

2015-11-25 01:28:05

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

Hi Arnaldo,

On Tue, Nov 24, 2015 at 12:45:51PM -0200, Arnaldo Carvalho de Melo wrote:
> Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:
> > On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> > > On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > > > From: Namhyung Kim <[email protected]>
> > > [...]
> > >
> > > > +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;
> > >
> > > I'm a bit puzzled with this, can't we rewind through the parents on printing or adding
> > > to the flat rbtree instead of having this parent_val field?
> >
> > Yes, this code is to simplify things on parent nodes. Maybe we could
> > go up to parents and print the callchain list there as you said.
> >
> > However, problem I think is how to handle 'has_children' information
> > on parents. That info controls folding status of each callchain. As
> > the info is in the struct callchain_list and flat or folded callchain
> > mode require the info should be in the top-most entry, I cannot share
> > entries in parent nodes.
> >
> > Thus I simply copied callchain lists in parents to leaf nodes. Yes,
> > it will consume some memory but can simplify the code.
>
> I haven't done any measuring, but I'm noticing that 'perf top -g' is
> showing more warnings about not being able to process events fast enough
> and so ends up losing events, I tried with --max-stack 16 and it helped,
> this is just a heads up.

OK, but it seems that it's not related to this patch since this patch
only affects flat or folded callchain mode.

>
> Perhaps my workstation workloads are gettning deeper callchains over
> time, but perhaps this is the cost of processing callchains that is
> increasing, I need to stop and try to quantify this.
>
> We really need to look at reducing the overhead of processing
> callchains.

Right, but with my multi-thread work, I realized that perf is getting
heavier recently. I guess it's mostly due to the atomic refcount
work. I need to get back to the multi-thread work..

Anyway I made a initial multi-thread support for perf top too. I
think I posted it to the list, but I cannot find the link. You can
take a look at it on 'perf/top-threaded-v1' branch in my tree.

git.kernel.org/pub/scm/linux/kernel/git/namhyung/linux-perf.git



Thanks,
Namhyung

2015-11-25 01:34:31

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:
> On Tue, Nov 24, 2015 at 12:45:51PM -0200, Arnaldo Carvalho de Melo wrote:
> > Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:
> > > On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> > > > On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > > > > From: Namhyung Kim <[email protected]>
> > > > [...]
> > > Thus I simply copied callchain lists in parents to leaf nodes. Yes,
> > > it will consume some memory but can simplify the code.
> >
> > I haven't done any measuring, but I'm noticing that 'perf top -g' is
> > showing more warnings about not being able to process events fast enough
> > and so ends up losing events, I tried with --max-stack 16 and it helped,
> > this is just a heads up.
>
> OK, but it seems that it's not related to this patch since this patch
> only affects flat or folded callchain mode.

Well, doesn't this patch makes some of the involved data structures
larger, thus putting more pressure on the L1 cache, etc? It may well be
related, but we need to measure.

> > Perhaps my workstation workloads are gettning deeper callchains over
> > time, but perhaps this is the cost of processing callchains that is
> > increasing, I need to stop and try to quantify this.
> >
> > We really need to look at reducing the overhead of processing
> > callchains.
>
> Right, but with my multi-thread work, I realized that perf is getting
> heavier recently. I guess it's mostly due to the atomic refcount
> work. I need to get back to the multi-thread work..

We really need to measure this ;-)

- Arnaldo

2015-11-25 02:10:36

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

Em Tue, Nov 24, 2015 at 10:34:18PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:
> > On Tue, Nov 24, 2015 at 12:45:51PM -0200, Arnaldo Carvalho de Melo wrote:
> > > Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:
> > > > On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> > > > > On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > > > > > From: Namhyung Kim <[email protected]>
> > > > > [...]
> > > > Thus I simply copied callchain lists in parents to leaf nodes. Yes,
> > > > it will consume some memory but can simplify the code.
> > >
> > > I haven't done any measuring, but I'm noticing that 'perf top -g' is
> > > showing more warnings about not being able to process events fast enough
> > > and so ends up losing events, I tried with --max-stack 16 and it helped,
> > > this is just a heads up.
> >
> > OK, but it seems that it's not related to this patch since this patch
> > only affects flat or folded callchain mode.
>
> Well, doesn't this patch makes some of the involved data structures
> larger, thus putting more pressure on the L1 cache, etc? It may well be
> related, but we need to measure.
>
> > > Perhaps my workstation workloads are gettning deeper callchains over
> > > time, but perhaps this is the cost of processing callchains that is
> > > increasing, I need to stop and try to quantify this.
> > >
> > > We really need to look at reducing the overhead of processing
> > > callchains.
> >
> > Right, but with my multi-thread work, I realized that perf is getting
> > heavier recently. I guess it's mostly due to the atomic refcount
> > work. I need to get back to the multi-thread work..
>
> We really need to measure this ;-)

So, something strange, if I use:

[acme@zoo linux]$ cat ~/bin/allmod
rm -rf ../build/allmodconfig/ ; mkdir ../build/allmodconfig/ ; make O=../build/allmodconfig/ allmodconfig ; make -j32 O=../build/allmodconfig
[acme@zoo linux]$

To generate background load, I don't see that much this:

+ 8.55% 8.49% libc-2┌─Warning!──────────────────────────────────────────────┐ ▒
+ 7.08% 6.98% perf │Events are being lost, check IO/CPU overload! │ ▒
+ 6.84% 0.04% perf │ │ ▒
+ 6.01% 0.09% perf │You may want to run 'perf' using a RT scheduler policy:│ ▒
+ 5.26% 0.13% [kerne│ │t▒
+ 4.96% 1.50% perf │ perf top -r 80 │ ▒
+ 4.76% 3.58% perf │ │ ▒
+ 4.69% 0.05% perf │Or reduce the sampling frequency. │ ▒

Its with a low loadavg:

[acme@zoo linux]$ cat /proc/loadavg
0.75 0.79 0.64 3/549 21259

That it pops up :-\

If I take a snapshot with 'P'

# head -40 perf.hist.0
+ 21.43% 21.09% libc-2.20.so [.] _int_malloc
+ 19.49% 0.00% perf [.] cmd_top
+ 19.46% 0.02% perf [.] perf_top__mmap_read_idx
+ 19.03% 0.06% perf [.] hist_entry_iter__add
+ 16.46% 1.85% perf [.] iter_add_next_cumulative_entry
+ 12.09% 11.98% libc-2.20.so [.] free
+ 10.68% 10.61% libc-2.20.so [.] __libc_calloc
+ 9.61% 0.09% perf [.] hists__decay_entries
+ 8.92% 8.85% libc-2.20.so [.] malloc_consolidate
+ 8.17% 6.33% perf [.] free_callchain_node
+ 7.94% 0.09% perf [.] hist_entry__delete
+ 6.22% 0.03% perf [.] callchain_append
+ 6.20% 6.11% perf [.] append_chain_children
+ 5.44% 1.50% perf [.] __hists__add_entry
+ 4.34% 0.14% [kernel] [k] entry_SYSCALL_64_fastpath
+ 3.69% 3.67% perf [.] sort__dso_cmp
+ 3.12% 0.20% perf [.] hists__output_resort
+ 2.88% 0.00% [unknown] [.] 0x6d86258d4c544155
+ 2.88% 0.00% libc-2.20.so [.] __libc_start_main
+ 2.88% 0.00% perf [.] main
+ 2.88% 0.00% perf [.] run_builtin
+ 2.66% 0.00% libpthread-2.20.so [.] start_thread
+ 2.66% 0.00% perf [.] display_thread_tui
+ 2.66% 0.00% perf [.] perf_evlist__tui_browse_hists
+ 2.66% 0.00% perf [.] perf_evsel__hists_browse
+ 2.49% 0.07% [kernel] [k] sys_futex
+ 2.42% 0.06% [kernel] [k] do_futex
2.24% 0.00% perf [.] perf_top__sort_new_samples
+ 1.92% 0.51% perf [.] hists__collapse_resort
+ 1.87% 1.86% perf [.] hpp__sort_overhead_acc
+ 1.69% 0.09% libc-2.20.so [.] __lll_unlock_wake_private
+ 1.45% 1.44% perf [.] hpp__nop_cmp
1.45% 1.43% perf [.] rb_erase
+ 1.44% 1.42% perf [.] __sort__hpp_cmp
1.31% 0.16% libc-2.20.so [.] __lll_lock_wait_private
1.18% 0.19% [kernel] [k] futex_wake
+ 1.13% 1.13% perf [.] sort__sym_cmp
1.11% 0.02% [kernel] [k] schedule
1.09% 0.22% [kernel] [k] __schedule
0.99% 0.08% [kernel] [k] futex_wait

So its quite a lot of mallocs, i.e. just do a 'perf top -g' and wait a
bit, malloc goes on bubbling up to the top, about the same time it
starts showing that popup screen telling that we're losing events.

If I use --no-children, to see if there is a difference, using either
--call-graph caller or callee, it doesn't get more than about 1%.

Ok, now I tried with "perf top --call-graph caller" i.e. with
--children, and looked at the _int_malloc callchains I get really long,
bogus callchains, see below. That explains why I don't lose events when
I use --max-stack.

I'll have to stop now, and I put the full perf.hist.1 at
http://vger.kernel.org/~acme/perf/perf.hist.1.xz

- Arnaldo

[root@zoo ~]# head -60 perf.hist.1
- 17.92% 17.10% libc-2.20.so [.] _int_malloc
+ 112.80% _int_malloc
11.14% 0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068
0x41bf5118
0x41bf5068

2015-11-25 21:04:30

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH 34/37] perf hists browser: Support flat callchains

Hi Arnaldo,

On Tue, Nov 24, 2015 at 11:10:25PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Tue, Nov 24, 2015 at 10:34:18PM -0300, Arnaldo Carvalho de Melo escreveu:
> > Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:
> > > On Tue, Nov 24, 2015 at 12:45:51PM -0200, Arnaldo Carvalho de Melo wrote:
> > > > Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:
> > > > > On Mon, Nov 23, 2015 at 04:16:48PM +0100, Frederic Weisbecker wrote:
> > > > > > On Thu, Nov 19, 2015 at 02:53:20PM -0300, Arnaldo Carvalho de Melo wrote:
> > > > > > > From: Namhyung Kim <[email protected]>
> > > > > > [...]
> > > > > Thus I simply copied callchain lists in parents to leaf nodes. Yes,
> > > > > it will consume some memory but can simplify the code.
> > > >
> > > > I haven't done any measuring, but I'm noticing that 'perf top -g' is
> > > > showing more warnings about not being able to process events fast enough
> > > > and so ends up losing events, I tried with --max-stack 16 and it helped,
> > > > this is just a heads up.
> > >
> > > OK, but it seems that it's not related to this patch since this patch
> > > only affects flat or folded callchain mode.
> >
> > Well, doesn't this patch makes some of the involved data structures
> > larger, thus putting more pressure on the L1 cache, etc? It may well be
> > related, but we need to measure.
> >
> > > > Perhaps my workstation workloads are gettning deeper callchains over
> > > > time, but perhaps this is the cost of processing callchains that is
> > > > increasing, I need to stop and try to quantify this.
> > > >
> > > > We really need to look at reducing the overhead of processing
> > > > callchains.
> > >
> > > Right, but with my multi-thread work, I realized that perf is getting
> > > heavier recently. I guess it's mostly due to the atomic refcount
> > > work. I need to get back to the multi-thread work..
> >
> > We really need to measure this ;-)
>
> So, something strange, if I use:
>
> [acme@zoo linux]$ cat ~/bin/allmod
> rm -rf ../build/allmodconfig/ ; mkdir ../build/allmodconfig/ ; make O=../build/allmodconfig/ allmodconfig ; make -j32 O=../build/allmodconfig
> [acme@zoo linux]$
>
> To generate background load, I don't see that much this:
>
> + 8.55% 8.49% libc-2┌─Warning!──────────────────────────────────────────────┐ ▒
> + 7.08% 6.98% perf │Events are being lost, check IO/CPU overload! │ ▒
> + 6.84% 0.04% perf │ │ ▒
> + 6.01% 0.09% perf │You may want to run 'perf' using a RT scheduler policy:│ ▒
> + 5.26% 0.13% [kerne│ │t▒
> + 4.96% 1.50% perf │ perf top -r 80 │ ▒
> + 4.76% 3.58% perf │ │ ▒
> + 4.69% 0.05% perf │Or reduce the sampling frequency. │ ▒
>
> Its with a low loadavg:
>
> [acme@zoo linux]$ cat /proc/loadavg
> 0.75 0.79 0.64 3/549 21259
>
> That it pops up :-\
>
> If I take a snapshot with 'P'
>
> # head -40 perf.hist.0
> + 21.43% 21.09% libc-2.20.so [.] _int_malloc
> + 19.49% 0.00% perf [.] cmd_top
> + 19.46% 0.02% perf [.] perf_top__mmap_read_idx
> + 19.03% 0.06% perf [.] hist_entry_iter__add
> + 16.46% 1.85% perf [.] iter_add_next_cumulative_entry
> + 12.09% 11.98% libc-2.20.so [.] free
> + 10.68% 10.61% libc-2.20.so [.] __libc_calloc
> + 9.61% 0.09% perf [.] hists__decay_entries
> + 8.92% 8.85% libc-2.20.so [.] malloc_consolidate
> + 8.17% 6.33% perf [.] free_callchain_node
> + 7.94% 0.09% perf [.] hist_entry__delete
> + 6.22% 0.03% perf [.] callchain_append
> + 6.20% 6.11% perf [.] append_chain_children
> + 5.44% 1.50% perf [.] __hists__add_entry
> + 4.34% 0.14% [kernel] [k] entry_SYSCALL_64_fastpath
> + 3.69% 3.67% perf [.] sort__dso_cmp
> + 3.12% 0.20% perf [.] hists__output_resort
> + 2.88% 0.00% [unknown] [.] 0x6d86258d4c544155
> + 2.88% 0.00% libc-2.20.so [.] __libc_start_main
> + 2.88% 0.00% perf [.] main
> + 2.88% 0.00% perf [.] run_builtin
> + 2.66% 0.00% libpthread-2.20.so [.] start_thread
> + 2.66% 0.00% perf [.] display_thread_tui
> + 2.66% 0.00% perf [.] perf_evlist__tui_browse_hists
> + 2.66% 0.00% perf [.] perf_evsel__hists_browse
> + 2.49% 0.07% [kernel] [k] sys_futex
> + 2.42% 0.06% [kernel] [k] do_futex
> 2.24% 0.00% perf [.] perf_top__sort_new_samples
> + 1.92% 0.51% perf [.] hists__collapse_resort
> + 1.87% 1.86% perf [.] hpp__sort_overhead_acc
> + 1.69% 0.09% libc-2.20.so [.] __lll_unlock_wake_private
> + 1.45% 1.44% perf [.] hpp__nop_cmp
> 1.45% 1.43% perf [.] rb_erase
> + 1.44% 1.42% perf [.] __sort__hpp_cmp
> 1.31% 0.16% libc-2.20.so [.] __lll_lock_wait_private
> 1.18% 0.19% [kernel] [k] futex_wake
> + 1.13% 1.13% perf [.] sort__sym_cmp
> 1.11% 0.02% [kernel] [k] schedule
> 1.09% 0.22% [kernel] [k] __schedule
> 0.99% 0.08% [kernel] [k] futex_wait
>
> So its quite a lot of mallocs, i.e. just do a 'perf top -g' and wait a
> bit, malloc goes on bubbling up to the top, about the same time it
> starts showing that popup screen telling that we're losing events.
>
> If I use --no-children, to see if there is a difference, using either
> --call-graph caller or callee, it doesn't get more than about 1%.
>
> Ok, now I tried with "perf top --call-graph caller" i.e. with
> --children, and looked at the _int_malloc callchains I get really long,
> bogus callchains, see below. That explains why I don't lose events when
> I use --max-stack.
>
> I'll have to stop now, and I put the full perf.hist.1 at
> http://vger.kernel.org/~acme/perf/perf.hist.1.xz
>
> - Arnaldo
>
> [root@zoo ~]# head -60 perf.hist.1
> - 17.92% 17.10% libc-2.20.so [.] _int_malloc
> + 112.80% _int_malloc
> 11.14% 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068
> 0x41bf5118
> 0x41bf5068

Hmm.. we should cut off the loop in the broken callchains. And it'd
be good to apply --hide-unresolved to callchains as well. I'll take a
look at it.

Thanks,
Namhyung