Skip to contentSkip to conversation

perf/core improvements and fixes

GIT PULL In review Arnaldo Carvalho de Melo 6 participants 51 messages Nov 19, 2015 – Nov 25, 2015

Subsystems (3)
lore
Share & tools
lore

Arnaldo Carvalho de Melo

author maintainer
#0 lore raw

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)

----------------------------------------------------------------
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
author maintainer
#1 lore raw

PATCH 1/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 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'")
---
 tools/perf/tests/bpf.c  | 14 +++++++-------
 tools/perf/tests/llvm.c |  8 ++++----
 2 files changed, 11 insertions(+), 11 deletions(-)
tools/perf/tests/bpf.c +7 -7
index ec16f7812c8b..6ebfdee3e2c6 100644
@@ -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;
tools/perf/tests/llvm.c +4 -4
index bc4cf507cde5..366e38ba8b49 100644
@@ -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);
 
Signature · 1 line
2.1.0
author maintainer
#2 lore raw

PATCH 2/37 tools: Fix selftests_install Makefile rule

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/Makefile +1 -1
index 7dc820a8c1f1..0ba0df3b516f 100644
@@ -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)
Signature · 1 line
2.1.0
author maintainer
#3 lore raw

PATCH 3/37 tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c

In reply to Arnaldo Carvalho de Melo

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

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

---
 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
tools/include/linux/string.h new file +9 -0
new file mode 100644
index 000000000000..f3a6db6ad732
@@ -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_ */
tools/lib/string.c new file +19 -0
new file mode 100644
index 000000000000..ecfd43a9b24e
@@ -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;
+}
tools/perf/MANIFEST +2 -0
index 39c38cb45b00..2562eac6451d 100644
@@ -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
tools/perf/util/Build +6 -0
index 591b3fe3ed49..e2316900f96f 100644
@@ -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)
tools/perf/util/include/linux/string.h deleted +0 -3
deleted file mode 100644
index 6f19c548ecc0..000000000000
@@ -1,3 +0,0 @@
-#include <string.h>
-
-void *memdup(const void *src, size_t len);
tools/perf/util/string.c +0 -16
index fc8781de62db..7f7e072be746 100644
@@ -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)
 {
 	/*
Signature · 1 line
2.1.0
author maintainer
#4 lore raw

PATCH 4/37 tools: Clone the kernel's strtobool function

In reply to Arnaldo Carvalho de Melo

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'.

Cc Alexei Starovoitov <[email protected]>
Cc Jonathan Cameron <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ]
---
 tools/include/linux/string.h |  2 ++
 tools/lib/string.c           | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)
tools/include/linux/string.h +2 -0
index f3a6db6ad732..2e2f736c039c 100644
@@ -6,4 +6,6 @@
 
 void *memdup(const void *src, size_t len);
 
+int strtobool(const char *s, bool *res);
+
 #endif /* _LINUX_STRING_H_ */
tools/lib/string.c +43 -0

51 lines not on this page · raw message

Signature · 1 line
2.1.0
author maintainer
#5 lore raw

PATCH 5/37 bpf tools: Load a program with different instances using preprocessor

In reply to Arnaldo Carvalho de Melo

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.

Cc Alexei Starovoitov <[email protected]>
Cc He Kuang <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Enclosed multi-line if/else blocks with {}, (*func_ptr)() -> func_ptr() ]
---
 tools/lib/bpf/libbpf.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++---
 tools/lib/bpf/libbpf.h |  64 ++++++++++++++++++++++
 2 files changed, 201 insertions(+), 9 deletions(-)
tools/lib/bpf/libbpf.c +137 -9
index e176bad19bcb..e3f4c3379f14 100644
@@ -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;
 }
tools/lib/bpf/libbpf.h +64 -0

70 lines not on this page · raw message

Signature · 1 line
2.1.0
author maintainer
#6 lore raw

PATCH 6/37 perf bpf: Add BPF_PROLOGUE config options for further patches

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/config/Makefile | 12 ++++++++++++
 1 file changed, 12 insertions(+)
tools/perf/config/Makefile +12 -0
index de89ec574361..6eb9a956a408 100644
@@ -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
 
Signature · 1 line
2.1.0
author maintainer
#7 lore raw

PATCH 7/37 perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/arch/x86/util/Build | 1 +
 1 file changed, 1 insertion(+)
tools/perf/arch/x86/util/Build +1 -0
index ff63649fa9ac..465970370f3e 100644
@@ -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
Signature · 1 line
2.1.0
author maintainer
#8 lore raw

PATCH 8/37 perf bpf: Allow BPF program attach to uprobe events

In reply to Arnaldo Carvalho de Melo

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.

Cc Alexei Starovoitov <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Changed the separator from \n to ; ]
---
 tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++---
 tools/perf/util/bpf-loader.h |   1 +
 2 files changed, 115 insertions(+), 6 deletions(-)
tools/perf/util/bpf-loader.c +114 -6
index 4c50411371db..84169d6f2585 100644
@@ -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");
tools/perf/util/bpf-loader.h +1 -0
index 9caf3ae4acf3..d19f5c5d6d74 100644
@@ -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,
 };
 
Signature · 1 line
2.1.0
author maintainer
#9 lore raw

PATCH 9/37 perf bpf: Allow attaching BPF programs to modules symbols

In reply to Arnaldo Carvalho de Melo

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.

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]>
---
 tools/perf/util/bpf-loader.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
tools/perf/util/bpf-loader.c +16 -0
index 84169d6f2585..d0f02ed93804 100644
@@ -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
Signature · 1 line
2.1.0
author maintainer
#10 lore raw

PATCH 10/37 perf bpf: Allow BPF program config probing options

In reply to Arnaldo Carvalho de Melo

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
  #
---
 tools/perf/util/bpf-loader.c  | 53 +++++++++++++++++++++++++++++++++++++++++--
 tools/perf/util/probe-event.c |  7 ++++--
 2 files changed, 56 insertions(+), 4 deletions(-)
tools/perf/util/bpf-loader.c +51 -2
index d0f02ed93804..98f2e5d1a5be 100644
@@ -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");
tools/perf/util/probe-event.c +5 -2
index 03875f9154e7..93996ec4bbe3 100644
@@ -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;
 	}
Signature · 1 line
2.1.0
author maintainer
#11 lore raw

PATCH 11/37 perf bpf: Add prologue for BPF programs for fetching arguments

In reply to Arnaldo Carvalho de Melo

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
  #
---
 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
tools/perf/util/Build +1 -0
index e2316900f96f..0513dd525d87 100644
@@ -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
tools/perf/util/bpf-loader.c +3 -0
index 98f2e5d1a5be..bd14be438cda 100644
@@ -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
tools/perf/util/bpf-loader.h +3 -0
index d19f5c5d6d74..a58740b0f31e 100644
@@ -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,
 };
 
tools/perf/util/bpf-prologue.c new file +455 -0

455 lines not on this page · raw message

tools/perf/util/bpf-prologue.h new file +34 -0
new file mode 100644
index 000000000000..d94cbea12899
@@ -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 */
Signature · 1 line
2.1.0
author maintainer
#12 lore raw

PATCH 12/37 perf bpf: Generate prologue for BPF programs

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 119 insertions(+), 1 deletion(-)
tools/perf/util/bpf-loader.c +119 -1
index bd14be438cda..190a1c7f0649 100644
@@ -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;
Signature · 1 line
2.1.0
author maintainer
#13 lore raw

PATCH 13/37 perf test: Test the BPF prologue adding infrastructure

In reply to Arnaldo Carvalho de Melo

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
#

Cc Alexei Starovoitov <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Added tools/perf/tests/llvm-src-prologue.c to .gitignore ]
---
 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
tools/perf/tests/.gitignore +1 -0
index 489fc9ffbcb0..bf016c439fbd 100644
@@ -1,2 +1,3 @@
 llvm-src-base.c
 llvm-src-kbuild.c
+llvm-src-prologue.c
tools/perf/tests/Build +8 -1
index f41ebf8849fe..0ff8a973b81c 100644
@@ -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
tools/perf/tests/bpf-script-test-prologue.c new file +35 -0
new file mode 100644
index 000000000000..7230e62c70fc
@@ -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;
tools/perf/tests/bpf.c +34 -0
index 6ebfdee3e2c6..d58442294e9e 100644
@@ -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),
tools/perf/tests/llvm.c +4 -0
index 366e38ba8b49..b4147634fb44 100644
@@ -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",
+	},
 };
 
 
tools/perf/tests/llvm.h +2 -0
index d91d8f44efee..5150b4d6ef50 100644
@@ -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,
 };
 
Signature · 1 line
2.1.0
author maintainer
#14 lore raw

PATCH 14/37 perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux

In reply to Arnaldo Carvalho de Melo

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
---
 tools/perf/tests/bpf.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
tools/perf/tests/bpf.c +4 -3
index d58442294e9e..232043cc232a 100644
@@ -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;
 
Signature · 1 line
2.1.0
author maintainer
#15 lore raw

PATCH 15/37 perf bpf: Use same BPF program if arguments are identical

In reply to Arnaldo Carvalho de Melo

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
  ...
---
 tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 131 insertions(+), 7 deletions(-)
tools/perf/util/bpf-loader.c +131 -7
index 190a1c7f0649..36544e5ece43 100644
@@ -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");
Signature · 1 line
2.1.0
author maintainer
#16 lore raw

PATCH 16/37 perf tests: Pass the subtest index to each test routine

In reply to Arnaldo Carvalho de Melo

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.

---
 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(-)
tools/perf/arch/x86/include/arch-tests.h +4 -4
index 7ed00f4b0908..b48de2f5813c 100644
@@ -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;
tools/perf/arch/x86/tests/insn-x86.c +1 -1
index b6115dfd28f0..08d9b2bc185c 100644
@@ -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;
 
tools/perf/arch/x86/tests/intel-cqm.c +1 -1
index d28c1b6a3b54..94e0cb7462f9 100644
@@ -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;
tools/perf/arch/x86/tests/perf-time-to-tsc.c +1 -1
index 658cd200af74..a289aa8a083a 100644
@@ -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,
tools/perf/arch/x86/tests/rdpmc.c +1 -1
index e7688214c7cf..7bb0d13c235f 100644
@@ -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;
tools/perf/tests/attr.c +1 -1
index 638875a0960a..b66730eb94e3 100644
@@ -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];
tools/perf/tests/bp_signal.c +1 -1
index a02b035fd5aa..fb80c9eb6a95 100644
@@ -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;
tools/perf/tests/bp_signal_overflow.c +1 -1
index e76537724491..89f92fa67cc4 100644
@@ -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;
tools/perf/tests/bpf.c +1 -1
index 232043cc232a..4efdc1607754 100644
@@ -215,7 +215,7 @@ out:
 	return ret;
 }
 
-int test__bpf(void)
+int test__bpf(int subtest __maybe_unused)
 {
 	unsigned int i;
 	int err;
tools/perf/tests/builtin-test.c +3 -3
index 80c442eab767..9cf4892c061d 100644
@@ -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) {
tools/perf/tests/code-reading.c +1 -1
index a767a6400c5c..4417b6a079f0 100644
@@ -601,7 +601,7 @@ out_err:
 	return err;
 }
 
-int test__code_reading(void)
+int test__code_reading(int subtest __maybe_unused)
 {
 	int ret;
 
tools/perf/tests/dso-data.c +3 -3
index a218aeaf56a0..dc673ff7c437 100644
@@ -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();
tools/perf/tests/dwarf-unwind.c +1 -1
index 07221793a3ac..01f0b61de53d 100644
@@ -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;
tools/perf/tests/evsel-roundtrip-name.c +1 -1
index 3fa715987a5e..1da92e1159ee 100644
@@ -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;
 
tools/perf/tests/evsel-tp-sched.c +1 -1
index 790e413d9a1f..1984b3bbfe15 100644
@@ -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;
tools/perf/tests/fdarray.c +2 -2
index d24b837951d4..c809463edbe5 100644
@@ -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);
tools/perf/tests/hists_cumulate.c +1 -1
index 7ed737019de7..8292948bc5f9 100644
@@ -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;
tools/perf/tests/hists_filter.c +1 -1
index 818acf875dd0..ccb5b4921f25 100644
@@ -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;
tools/perf/tests/hists_link.c +1 -1
index 8c102b011424..6243e2b2a245 100644
@@ -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;
tools/perf/tests/hists_output.c +1 -1
index adbebc852cc8..248beec1d917 100644
@@ -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;
tools/perf/tests/keep-tracking.c +1 -1
index a2e2269aa093..a337a6da1f39 100644
@@ -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,
tools/perf/tests/kmod-path.c +1 -1
index 08c433b4bf4f..d2af78193153 100644
@@ -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);
tools/perf/tests/llvm.c +1 -1
index b4147634fb44..4350c455d06c 100644
@@ -131,7 +131,7 @@ out:
 	return ret;
 }
 
-int test__llvm(void)
+int test__llvm(int subtest __maybe_unused)
 {
 	enum test_llvm__testcase i;
 
tools/perf/tests/mmap-basic.c +1 -1
index 4495493c9431..359e98fcd94c 100644
@@ -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;
tools/perf/tests/mmap-thread-lookup.c +1 -1
index 145050e2e544..6cdb97579c45 100644
@@ -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",
tools/perf/tests/openat-syscall-all-cpus.c +1 -1
index 2006485a2859..53c2273e8859 100644
@@ -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;
tools/perf/tests/openat-syscall-tp-fields.c +1 -1
index 5e811cd8f1c3..eb99a105f31c 100644
@@ -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 = {
tools/perf/tests/openat-syscall.c +1 -1
index 033b54797b8a..1184f9ba6499 100644
@@ -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;
tools/perf/tests/parse-events.c +1 -1
index 636d7b42d844..abe8849d1d70 100644
@@ -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;
 
tools/perf/tests/parse-no-sample-id-all.c +1 -1
index 2c63ea658541..294c76b01b41 100644
@@ -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;
 
tools/perf/tests/perf-record.c +1 -1
index 7a228a2a070b..9d5f0b57c4c1 100644
@@ -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 = {
tools/perf/tests/pmu.c +1 -1
index faa04e9d5d5f..1e2ba2602930 100644
@@ -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);
tools/perf/tests/python-use.c +2 -1
index 7760277c6def..7a52834ee0d0 100644
@@ -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;
tools/perf/tests/sample-parsing.c +1 -1
index 30c02181e78b..5f23710b9fee 100644
@@ -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;
tools/perf/tests/sw-clock.c +1 -1
index 5b83f56a3b6f..36e8ce1550e3 100644
@@ -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;
 
tools/perf/tests/switch-tracking.c +1 -1
index a02af503100c..dfbd8d69ce89 100644
@@ -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, };
tools/perf/tests/task-exit.c +1 -1
index add16385f13e..2dfff7ac8ef3 100644
@@ -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;
tools/perf/tests/tests.h +39 -39

87 lines not on this page · raw message

tools/perf/tests/thread-map.c +1 -1
index 138a0e3431fa..2be02d303e82 100644
@@ -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;
 
tools/perf/tests/thread-mg-share.c +1 -1
index 01fabb19d746..188b63140fc8 100644
@@ -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;
tools/perf/tests/topology.c +1 -1
index f5bb096c3bd9..98fe69ac553c 100644
@@ -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;
tools/perf/tests/vmlinux-kallsyms.c +1 -1
index d677e018e504..f0bfc9e8fd9f 100644
@@ -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;
Signature · 1 line
2.1.0
author maintainer
#17 lore raw

PATCH 17/37 perf test: Print result for each LLVM subtest

In reply to Arnaldo Carvalho de Melo

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)
  ...
Cc Alexei Starovoitov <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Changed so that func is not on an anonymous union ]
---
 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(-)
tools/perf/tests/builtin-test.c +76 -15
index 9cf4892c061d..813660976217 100644
@@ -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;
+			}
 		}
 	}
 
tools/perf/tests/llvm.c +30 -35

81 lines not on this page · raw message

tools/perf/tests/tests.h +9 -0
index 204e4eeadea2..f92af527f080 100644
@@ -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);
 
Signature · 1 line
2.1.0
author maintainer
#18 lore raw

PATCH 18/37 perf test: Print result for each BPF subtest

In reply to Arnaldo Carvalho de Melo

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
Cc Alexei Starovoitov <[email protected]>
Cc Masami Hiramatsu <[email protected]>
Cc Zefan Li <[email protected]>
[ Fixed up not to use .func in an anonymous union ]
---
 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(-)
tools/perf/tests/bpf.c +28 -10
index 4efdc1607754..33689a0cf821 100644
@@ -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;
tools/perf/tests/builtin-test.c +5 -0
index 813660976217..146ae9821c00 100644
@@ -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,
tools/perf/tests/tests.h +2 -0
index f92af527f080..a0733aaad081 100644
@@ -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__)
Signature · 1 line
2.1.0
author maintainer
#19 lore raw

PATCH 19/37 perf test: Mute test cases error messages if verbose == 0

In reply to Arnaldo Carvalho de Melo

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!
---
 tools/perf/tests/builtin-test.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
tools/perf/tests/builtin-test.c +12 -0
index 146ae9821c00..2b1ade1aafc3 100644
@@ -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);
 	}
Signature · 1 line
2.1.0
author maintainer
#20 lore raw

PATCH 20/37 perf probe: Fix to free temporal Dwarf_Frame

In reply to Arnaldo Carvalho de Melo

From: Masami Hiramatsu <[email protected]>

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

---
 tools/perf/util/probe-finder.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
tools/perf/util/probe-finder.c +6 -3
index 05012bb178d7..1cab05a3831e 100644
@@ -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;
Signature · 1 line
2.1.0
author maintainer
#21 lore raw

PATCH 21/37 perf machine: Fix machine__findnew_module_map to put registered map

In reply to Arnaldo Carvalho de Melo

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)

---
 tools/perf/util/machine.c | 2 ++
 1 file changed, 2 insertions(+)
tools/perf/util/machine.c +2 -0
index 8b303ff20289..0487d7795f13 100644
@@ -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;
Signature · 1 line
2.1.0
author maintainer
#22 lore raw

PATCH 22/37 perf machine: Fix machine__destroy_kernel_maps to drop vmlinux_maps references

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/util/machine.c | 1 +
 1 file changed, 1 insertion(+)
tools/perf/util/machine.c +1 -0
index 0487d7795f13..e9e09bee221c 100644
@@ -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;
 	}
 }
Signature · 1 line
2.1.0
author maintainer
#23 lore raw

PATCH 23/37 perf machine: Fix to destroy kernel maps when machine exits

In reply to Arnaldo Carvalho de Melo

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

---
 tools/perf/util/machine.c | 1 +
 1 file changed, 1 insertion(+)
tools/perf/util/machine.c +1 -0
index e9e09bee221c..a358771fe9e3 100644
@@ -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);
Signature · 1 line
2.1.0
author maintainer
#24 lore raw

PATCH 24/37 perf tools: Make perf_exec_path() always return malloc'd string

In reply to Arnaldo Carvalho de Melo

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.

---
 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(-)
tools/perf/util/exec_cmd.c +11 -10
index 7adf4ad15d8f..1099e92f5ee1 100644
@@ -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);
tools/perf/util/exec_cmd.h +3 -2
index bc4b915963f5..48b4175f1e11 100644
@@ -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 */
tools/perf/util/help.c +4 -2
index 86c37c472263..fa1fc4acb8a4 100644
@@ -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) {
Signature · 1 line
2.1.0
author maintainer
#25 lore raw

PATCH 25/37 perf tools: Fix to put new map after inserting to map_groups in dso__load_sym

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/util/symbol-elf.c | 2 ++
 1 file changed, 2 insertions(+)
tools/perf/util/symbol-elf.c +2 -0
index 475d88d0a1c9..53f19968bfa2 100644
@@ -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
Signature · 1 line
2.1.0
author maintainer
#26 lore raw

PATCH 26/37 perf tools: Fix __dsos__addnew to put dso after adding it to the list

In reply to Arnaldo Carvalho de Melo

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]

---
 tools/perf/util/dso.c | 2 ++
 1 file changed, 2 insertions(+)
tools/perf/util/dso.c +2 -0
index 425df5c86c9c..e8e9a9dbf5e3 100644
@@ -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;
 }
Signature · 1 line
2.1.0
author maintainer
#27 lore raw

PATCH 27/37 perf tools: Fix machine__create_kernel_maps to put kernel dso refcount

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/util/machine.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
tools/perf/util/machine.c +6 -3
index a358771fe9e3..0b4a05c14204 100644
@@ -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) {
Signature · 1 line
2.1.0
author maintainer
#28 lore raw

PATCH 28/37 perf machine: Fix machine__findnew_module_map to put dso

In reply to Arnaldo Carvalho de Melo

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().

---
 tools/perf/util/machine.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
tools/perf/util/machine.c +3 -1
index 0b4a05c14204..7f5071a4d9aa 100644
@@ -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;
 }
Signature · 1 line
2.1.0
author maintainer
#29 lore raw

PATCH 29/37 perf report: Support folded callchain mode on --stdio

In reply to Arnaldo Carvalho de Melo

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

---
 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(-)
tools/perf/Documentation/perf-report.txt +1 -0
index 5ce8da1e1256..f7d81aac9188 100644
@@ -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
tools/perf/ui/stdio/hist.c +55 -0

67 lines not on this page · raw message

tools/perf/util/callchain.c +6 -0
index 735ad48e1858..08cb220ba5ea 100644
@@ -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:
tools/perf/util/callchain.h +3 -2
index fce8161e54db..544d99ac169c 100644
@@ -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 {
Signature · 1 line
2.1.0
author maintainer
#30 lore raw

PATCH 30/37 perf callchain: Abstract callchain print function

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

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

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]>
[ renamed new _sprintf_ operation to _scnprintf_ ]
---
 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(-)
tools/perf/ui/browsers/hists.c +5 -3
index fa9eb92c9e24..0b18857a36e8 100644
@@ -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;
tools/perf/ui/gtk/hists.c +2 -6
index 4b3585eed1e8..cff7bb9d9632 100644
@@ -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);
tools/perf/ui/stdio/hist.c +17 -18
index ea7984932d9a..f4de055cab9b 100644
@@ -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)
tools/perf/util/callchain.c +29 -0
index 08cb220ba5ea..b948bd068966 100644
@@ -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;
tools/perf/util/callchain.h +4 -0
index 544d99ac169c..060e636e33ab 100644
@@ -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);
 
Signature · 1 line
2.1.0
author maintainer
#31 lore raw

PATCH 31/37 perf callchain: Add count fields to struct callchain_node

In reply to Arnaldo Carvalho de Melo

From: Namhyung Kim <[email protected]>

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

---
 tools/perf/util/callchain.c | 10 ++++++++++
 tools/perf/util/callchain.h |  7 +++++++
 2 files changed, 17 insertions(+)
tools/perf/util/callchain.c +10 -0
index b948bd068966..e390edd31504 100644
@@ -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;
 	}
 
tools/perf/util/callchain.h +7 -0
index 060e636e33ab..cdb386d9ba02 100644
@@ -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,
Signature · 1 line
2.1.0
#32 lore raw

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

Namhyung,

Quoted 3 lines · Arnaldo Carvalho de Melo

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.

maintainer
#33 lore raw

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

Hi Frederic,

Quoted 11 lines · Frederic Weisbecker

Namhyung,

Quoted 3 lines · Arnaldo Carvalho de Melo

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

author maintainer
#34 lore raw

PATCH 32/37 perf report: Add callchain value option

In reply to Arnaldo Carvalho de Melo

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
---
 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(-)
tools/perf/Documentation/perf-report.txt +9 -4
index f7d81aac9188..dab99ed2b339 100644
@@ -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
tools/perf/builtin-report.c +2 -2
index f256fac1e722..14428342b47b 100644
@@ -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,
tools/perf/ui/stdio/hist.c +9 -1
index f4de055cab9b..7ebc661be267 100644
@@ -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,
tools/perf/util/callchain.c +53 -9

92 lines not on this page · raw message

tools/perf/util/callchain.h +9 -1
index cdb386d9ba02..47bc0c57f764 100644
@@ -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;
tools/perf/util/util.c +2 -1
index 47b1e36c7ea0..75759aebc7b8 100644
@@ -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,
 };
 
 /*
Signature · 1 line
2.1.0
author maintainer
#35 lore raw

PATCH 33/37 perf hists browser: Factor out hist_browser__show_callchain_list()

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/ui/browsers/hists.c | 72 ++++++++++++++++++++++++++----------------
 1 file changed, 45 insertions(+), 27 deletions(-)
tools/perf/ui/browsers/hists.c +45 -27
index 0b18857a36e8..0746d41d9efe 100644
@@ -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;
 		}
Signature · 1 line
2.1.0
author maintainer
#36 lore raw

PATCH 34/37 perf hists browser: Support flat callchains

In reply to Arnaldo Carvalho de Melo

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
---
 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(-)
tools/perf/ui/browsers/hists.c +120 -2
index 0746d41d9efe..c44af461a68f 100644
@@ -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;
tools/perf/util/callchain.c +44 -0

59 lines not on this page · raw message

tools/perf/util/callchain.h +2 -0
index 47bc0c57f764..6e9b5f2099e1 100644
@@ -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 */
Signature · 1 line
2.1.0
#37 lore raw

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

Quoted 64 lines · Arnaldo Carvalho de Melo

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

---

[...]

Quoted 17 lines
tools/perf/util/callchain.c +17 -0
+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;
+	}

tools/perf/util/callchain.c void free_callchain(struct callchain_root *root) · line 953

Quoted 10 lines
tools/perf/util/callchain.c +10 -0
+
+	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.

maintainer
#38 lore raw

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

Quoted 90 lines · Frederic Weisbecker
Quoted 64 lines · Arnaldo Carvalho de Melo

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

---

[...]

Quoted 22 lines
tools/perf/util/callchain.c +22 -0
+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)) {
Quoted 8 lines

tools/perf/util/callchain.c void free_callchain(struct callchain_root *root) · line 953

Quoted 5 lines
tools/perf/util/callchain.c +5 -0
+		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

#39 lore raw

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

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:

Quoted 39 lines · Frederic Weisbecker
Quoted 26 lines · Arnaldo Carvalho de Melo
Quoted 1 line

From: Namhyung Kim <[email protected]>

[...]

Quoted 22 lines
tools/perf/util/callchain.c +22 -0
+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)) {
Quoted 8 lines

tools/perf/util/callchain.c void free_callchain(struct callchain_root *root) · line 953

Quoted 5 lines
tools/perf/util/callchain.c +5 -0
+		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.

Quoted 8 lines

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

maintainer
#40 lore raw

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

Hi Arnaldo,

Quoted 46 lines · Arnaldo Carvalho de Melo

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:

Quoted 45 lines · Frederic Weisbecker
Quoted 26 lines · Arnaldo Carvalho de Melo
Quoted 1 line

From: Namhyung Kim <[email protected]>

[...]

Quoted 22 lines
tools/perf/util/callchain.c +22 -0
+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)) {
Quoted 8 lines

tools/perf/util/callchain.c void free_callchain(struct callchain_root *root) · line 953

Quoted 5 lines
tools/perf/util/callchain.c +5 -0
+		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.

Quoted 7 lines
Quoted 2 lines

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.

Quoted 7 lines

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

#41 lore raw

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

Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:

Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 8 lines

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Frederic Weisbecker
Quoted 3 lines · Arnaldo Carvalho de Melo
Quoted 1 line

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.

Quoted 7 lines
Quoted 4 lines

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.

Quoted 10 lines
Quoted 6 lines

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

#42 lore raw

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:

Quoted 26 lines

Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:

Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 8 lines

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Frederic Weisbecker
Quoted 3 lines · Arnaldo Carvalho de Melo
Quoted 1 line

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.

Quoted 7 lines
Quoted 4 lines

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.

Quoted 4 lines
Quoted 4 lines

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.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

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
maintainer
#43 lore raw

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

Hi Arnaldo,

Quoted 174 lines · Arnaldo Carvalho de Melo

Em Tue, Nov 24, 2015 at 10:34:18PM -0300, Arnaldo Carvalho de Melo escreveu:

Quoted 26 lines

Em Wed, Nov 25, 2015 at 10:26:08AM +0900, Namhyung Kim escreveu:

Quoted 9 lines · Arnaldo Carvalho de Melo
Quoted 8 lines

Em Tue, Nov 24, 2015 at 02:27:08PM +0900, Namhyung Kim escreveu:

Quoted 6 lines · Frederic Weisbecker
Quoted 3 lines · Arnaldo Carvalho de Melo
Quoted 1 line

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.

Quoted 7 lines
Quoted 4 lines

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.

Quoted 4 lines
Quoted 4 lines

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.

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

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:

(quoted diff) +8 -0
+    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
(quoted diff) +33 -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
Quoted 8 lines
        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

author maintainer
#44 lore raw

PATCH 35/37 perf hists browser: Support folded callchains

In reply to Arnaldo Carvalho de Melo

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; ...
---
 tools/perf/ui/browsers/hists.c | 125 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 124 insertions(+), 1 deletion(-)
tools/perf/ui/browsers/hists.c +124 -1
index c44af461a68f..a211b7b6a81e 100644
@@ -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,
Signature · 1 line
2.1.0
author maintainer
#45 lore raw

PATCH 36/37 perf ui/gtk: Support flat callchains

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/ui/gtk/hists.c | 80 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 76 insertions(+), 4 deletions(-)
tools/perf/ui/gtk/hists.c +76 -4
index cff7bb9d9632..0b24cd6d38a4 100644
@@ -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)
Signature · 1 line
2.1.0
author maintainer
#46 lore raw

PATCH 37/37 perf ui/gtk: Support folded callchains

In reply to Arnaldo Carvalho de Melo

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.

---
 tools/perf/ui/gtk/hists.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)
tools/perf/ui/gtk/hists.c +62 -0
index 0b24cd6d38a4..467717276ab6 100644
@@ -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);
 }
Signature · 1 line
2.1.0
#47 lore raw
In reply to Arnaldo Carvalho de Melo

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!

Quoted 285 lines

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)

----------------------------------------------------------------
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(-)
Quoted 6 lines

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�

author maintainer
#48 lore raw

Em Fri, Nov 20, 2015 at 10:01:30AM +0000, 平松雅巳 / HIRAMATU,MASAMI escreveu:

Quoted 5 lines

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.

Quoted 3 lines

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

#49 lore raw
Quoted 12 lines

From: 'Arnaldo Carvalho de Melo' [mailto:[email protected]]

Em Fri, Nov 20, 2015 at 10:01:30AM +0000, 平松雅巳 / HIRAMATU,MASAMI escreveu:

Quoted 5 lines

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.

Quoted 6 lines
Quoted 3 lines

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!

Signature · 6 lines
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�
#50 lore raw
In reply to Arnaldo Carvalho de Melo
Quoted 279 lines · Arnaldo Carvalho de Melo

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)

----------------------------------------------------------------
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 +
Quoted 8 lines
 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