aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWang Nan <wangnan0@huawei.com>2016-11-26 02:03:28 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2016-11-29 10:13:27 -0500
commita074865e60edd762b99ec5dacec69b406f702e66 (patch)
tree774e0abac8cf015dad77638c3c79aa56609dbb82
parent5a6acad17d2e81765dd4c2fce7346a6f045eab25 (diff)
perf tools: Introduce perf hooks
Perf hooks allow hooking user code at perf events. They can be used for manipulation of BPF maps, taking snapshot and reporting results. In this patch two perf hook points are introduced: record_start and record_end. To avoid buggy user actions, a SIGSEGV signal handler is introduced into 'perf record'. It turns off perf hook if it causes a segfault and report an error to help debugging. A test case for perf hook is introduced. Test result: $ ./buildperf/perf test -v hook 50: Test perf hooks : --- start --- test child forked, pid 10311 SIGSEGV is observed as expected, try to recover. Fatal error (SEGFAULT) in perf hook 'test' test child finished with 0 ---- end ---- Test perf hooks: Ok Signed-off-by: Wang Nan <wangnan0@huawei.com> Cc: Alexei Starovoitov <ast@fb.com> Cc: He Kuang <hekuang@huawei.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Joe Stringer <joe@ovn.org> Cc: Zefan Li <lizefan@huawei.com> Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/20161126070354.141764-5-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r--tools/perf/builtin-record.c11
-rw-r--r--tools/perf/tests/Build1
-rw-r--r--tools/perf/tests/builtin-test.c4
-rw-r--r--tools/perf/tests/perf-hooks.c44
-rw-r--r--tools/perf/tests/tests.h1
-rw-r--r--tools/perf/util/Build2
-rw-r--r--tools/perf/util/perf-hooks-list.h3
-rw-r--r--tools/perf/util/perf-hooks.c84
-rw-r--r--tools/perf/util/perf-hooks.h37
9 files changed, 187 insertions, 0 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 67d2a9003294..fa26865364b6 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -37,6 +37,7 @@
37#include "util/llvm-utils.h" 37#include "util/llvm-utils.h"
38#include "util/bpf-loader.h" 38#include "util/bpf-loader.h"
39#include "util/trigger.h" 39#include "util/trigger.h"
40#include "util/perf-hooks.h"
40#include "asm/bug.h" 41#include "asm/bug.h"
41 42
42#include <unistd.h> 43#include <unistd.h>
@@ -206,6 +207,12 @@ static void sig_handler(int sig)
206 done = 1; 207 done = 1;
207} 208}
208 209
210static void sigsegv_handler(int sig)
211{
212 perf_hooks__recover();
213 sighandler_dump_stack(sig);
214}
215
209static void record__sig_exit(void) 216static void record__sig_exit(void)
210{ 217{
211 if (signr == -1) 218 if (signr == -1)
@@ -833,6 +840,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
833 signal(SIGCHLD, sig_handler); 840 signal(SIGCHLD, sig_handler);
834 signal(SIGINT, sig_handler); 841 signal(SIGINT, sig_handler);
835 signal(SIGTERM, sig_handler); 842 signal(SIGTERM, sig_handler);
843 signal(SIGSEGV, sigsegv_handler);
836 844
837 if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) { 845 if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) {
838 signal(SIGUSR2, snapshot_sig_handler); 846 signal(SIGUSR2, snapshot_sig_handler);
@@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
970 978
971 trigger_ready(&auxtrace_snapshot_trigger); 979 trigger_ready(&auxtrace_snapshot_trigger);
972 trigger_ready(&switch_output_trigger); 980 trigger_ready(&switch_output_trigger);
981 perf_hooks__invoke_record_start();
973 for (;;) { 982 for (;;) {
974 unsigned long long hits = rec->samples; 983 unsigned long long hits = rec->samples;
975 984
@@ -1114,6 +1123,8 @@ out_child:
1114 } 1123 }
1115 } 1124 }
1116 1125
1126 perf_hooks__invoke_record_end();
1127
1117 if (!err && !quiet) { 1128 if (!err && !quiet) {
1118 char samples[128]; 1129 char samples[128];
1119 const char *postfix = rec->timestamp_filename ? 1130 const char *postfix = rec->timestamp_filename ?
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 8a4ce492f7b2..af3ec94869aa 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -42,6 +42,7 @@ perf-y += backward-ring-buffer.o
42perf-y += sdt.o 42perf-y += sdt.o
43perf-y += is_printable_array.o 43perf-y += is_printable_array.o
44perf-y += bitmap.o 44perf-y += bitmap.o
45perf-y += perf-hooks.o
45 46
46$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build 47$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
47 $(call rule_mkdir) 48 $(call rule_mkdir)
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 778668a2a966..dab83f7042fa 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -230,6 +230,10 @@ static struct test generic_tests[] = {
230 .func = test__bitmap_print, 230 .func = test__bitmap_print,
231 }, 231 },
232 { 232 {
233 .desc = "Test perf hooks",
234 .func = test__perf_hooks,
235 },
236 {
233 .func = NULL, 237 .func = NULL,
234 }, 238 },
235}; 239};
diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c
new file mode 100644
index 000000000000..9338cb2c25ab
--- /dev/null
+++ b/tools/perf/tests/perf-hooks.c
@@ -0,0 +1,44 @@
1#include <signal.h>
2#include <stdlib.h>
3
4#include "tests.h"
5#include "debug.h"
6#include "util.h"
7#include "perf-hooks.h"
8
9static void sigsegv_handler(int sig __maybe_unused)
10{
11 pr_debug("SIGSEGV is observed as expected, try to recover.\n");
12 perf_hooks__recover();
13 signal(SIGSEGV, SIG_DFL);
14 raise(SIGSEGV);
15 exit(-1);
16}
17
18static int hook_flags;
19
20static void the_hook(void)
21{
22 int *p = NULL;
23
24 hook_flags = 1234;
25
26 /* Generate a segfault, test perf_hooks__recover */
27 *p = 0;
28}
29
30int test__perf_hooks(int subtest __maybe_unused)
31{
32 signal(SIGSEGV, sigsegv_handler);
33 perf_hooks__set_hook("test", the_hook);
34 perf_hooks__invoke_test();
35
36 /* hook is triggered? */
37 if (hook_flags != 1234)
38 return TEST_FAIL;
39
40 /* the buggy hook is removed? */
41 if (perf_hooks__get_hook("test"))
42 return TEST_FAIL;
43 return TEST_OK;
44}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 7c196c585472..3a1f98f291ba 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -91,6 +91,7 @@ int test__cpu_map_print(int subtest);
91int test__sdt_event(int subtest); 91int test__sdt_event(int subtest);
92int test__is_printable_array(int subtest); 92int test__is_printable_array(int subtest);
93int test__bitmap_print(int subtest); 93int test__bitmap_print(int subtest);
94int test__perf_hooks(int subtest);
94 95
95#if defined(__arm__) || defined(__aarch64__) 96#if defined(__arm__) || defined(__aarch64__)
96#ifdef HAVE_DWARF_UNWIND_SUPPORT 97#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 1dc67efad634..b2a47aac8d1c 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -123,6 +123,8 @@ libperf-$(CONFIG_LIBELF) += genelf.o
123libperf-$(CONFIG_DWARF) += genelf_debug.o 123libperf-$(CONFIG_DWARF) += genelf_debug.o
124endif 124endif
125 125
126libperf-y += perf-hooks.o
127
126CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" 128CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
127# avoid compiler warnings in 32-bit mode 129# avoid compiler warnings in 32-bit mode
128CFLAGS_genelf_debug.o += -Wno-packed 130CFLAGS_genelf_debug.o += -Wno-packed
diff --git a/tools/perf/util/perf-hooks-list.h b/tools/perf/util/perf-hooks-list.h
new file mode 100644
index 000000000000..2867c07ee84e
--- /dev/null
+++ b/tools/perf/util/perf-hooks-list.h
@@ -0,0 +1,3 @@
1PERF_HOOK(record_start)
2PERF_HOOK(record_end)
3PERF_HOOK(test)
diff --git a/tools/perf/util/perf-hooks.c b/tools/perf/util/perf-hooks.c
new file mode 100644
index 000000000000..4ce88e37dd63
--- /dev/null
+++ b/tools/perf/util/perf-hooks.c
@@ -0,0 +1,84 @@
1/*
2 * perf_hooks.c
3 *
4 * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
5 * Copyright (C) 2016 Huawei Inc.
6 */
7
8#include <errno.h>
9#include <stdlib.h>
10#include <setjmp.h>
11#include <linux/err.h>
12#include "util/util.h"
13#include "util/debug.h"
14#include "util/perf-hooks.h"
15
16static sigjmp_buf jmpbuf;
17static const struct perf_hook_desc *current_perf_hook;
18
19void perf_hooks__invoke(const struct perf_hook_desc *desc)
20{
21 if (!(desc && desc->p_hook_func && *desc->p_hook_func))
22 return;
23
24 if (sigsetjmp(jmpbuf, 1)) {
25 pr_warning("Fatal error (SEGFAULT) in perf hook '%s'\n",
26 desc->hook_name);
27 *(current_perf_hook->p_hook_func) = NULL;
28 } else {
29 current_perf_hook = desc;
30 (**desc->p_hook_func)();
31 }
32 current_perf_hook = NULL;
33}
34
35void perf_hooks__recover(void)
36{
37 if (current_perf_hook)
38 siglongjmp(jmpbuf, 1);
39}
40
41#define PERF_HOOK(name) \
42perf_hook_func_t __perf_hook_func_##name = NULL; \
43struct perf_hook_desc __perf_hook_desc_##name = \
44 {.hook_name = #name, .p_hook_func = &__perf_hook_func_##name};
45#include "perf-hooks-list.h"
46#undef PERF_HOOK
47
48#define PERF_HOOK(name) \
49 &__perf_hook_desc_##name,
50
51static struct perf_hook_desc *perf_hooks[] = {
52#include "perf-hooks-list.h"
53};
54#undef PERF_HOOK
55
56int perf_hooks__set_hook(const char *hook_name,
57 perf_hook_func_t hook_func)
58{
59 unsigned int i;
60
61 for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
62 if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
63 continue;
64
65 if (*(perf_hooks[i]->p_hook_func))
66 pr_warning("Overwrite existing hook: %s\n", hook_name);
67 *(perf_hooks[i]->p_hook_func) = hook_func;
68 return 0;
69 }
70 return -ENOENT;
71}
72
73perf_hook_func_t perf_hooks__get_hook(const char *hook_name)
74{
75 unsigned int i;
76
77 for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
78 if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
79 continue;
80
81 return *(perf_hooks[i]->p_hook_func);
82 }
83 return ERR_PTR(-ENOENT);
84}
diff --git a/tools/perf/util/perf-hooks.h b/tools/perf/util/perf-hooks.h
new file mode 100644
index 000000000000..1d482b26b4b9
--- /dev/null
+++ b/tools/perf/util/perf-hooks.h
@@ -0,0 +1,37 @@
1#ifndef PERF_UTIL_PERF_HOOKS_H
2#define PERF_UTIL_PERF_HOOKS_H
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8typedef void (*perf_hook_func_t)(void);
9struct perf_hook_desc {
10 const char * const hook_name;
11 perf_hook_func_t * const p_hook_func;
12};
13
14extern void perf_hooks__invoke(const struct perf_hook_desc *);
15extern void perf_hooks__recover(void);
16
17#define PERF_HOOK(name) \
18extern struct perf_hook_desc __perf_hook_desc_##name; \
19static inline void perf_hooks__invoke_##name(void) \
20{ \
21 perf_hooks__invoke(&__perf_hook_desc_##name); \
22}
23
24#include "perf-hooks-list.h"
25#undef PERF_HOOK
26
27extern int
28perf_hooks__set_hook(const char *hook_name,
29 perf_hook_func_t hook_func);
30
31extern perf_hook_func_t
32perf_hooks__get_hook(const char *hook_name);
33
34#ifdef __cplusplus
35}
36#endif
37#endif