diff options
-rw-r--r-- | tools/perf/builtin-record.c | 11 | ||||
-rw-r--r-- | tools/perf/tests/Build | 1 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
-rw-r--r-- | tools/perf/tests/perf-hooks.c | 44 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
-rw-r--r-- | tools/perf/util/Build | 2 | ||||
-rw-r--r-- | tools/perf/util/perf-hooks-list.h | 3 | ||||
-rw-r--r-- | tools/perf/util/perf-hooks.c | 84 | ||||
-rw-r--r-- | tools/perf/util/perf-hooks.h | 37 |
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 | ||
210 | static void sigsegv_handler(int sig) | ||
211 | { | ||
212 | perf_hooks__recover(); | ||
213 | sighandler_dump_stack(sig); | ||
214 | } | ||
215 | |||
209 | static void record__sig_exit(void) | 216 | static 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 | |||
42 | perf-y += sdt.o | 42 | perf-y += sdt.o |
43 | perf-y += is_printable_array.o | 43 | perf-y += is_printable_array.o |
44 | perf-y += bitmap.o | 44 | perf-y += bitmap.o |
45 | perf-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 | |||
9 | static 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 | |||
18 | static int hook_flags; | ||
19 | |||
20 | static 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 | |||
30 | int 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); | |||
91 | int test__sdt_event(int subtest); | 91 | int test__sdt_event(int subtest); |
92 | int test__is_printable_array(int subtest); | 92 | int test__is_printable_array(int subtest); |
93 | int test__bitmap_print(int subtest); | 93 | int test__bitmap_print(int subtest); |
94 | int 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 | |||
123 | libperf-$(CONFIG_DWARF) += genelf_debug.o | 123 | libperf-$(CONFIG_DWARF) += genelf_debug.o |
124 | endif | 124 | endif |
125 | 125 | ||
126 | libperf-y += perf-hooks.o | ||
127 | |||
126 | CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" | 128 | CFLAGS_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 |
128 | CFLAGS_genelf_debug.o += -Wno-packed | 130 | CFLAGS_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 @@ | |||
1 | PERF_HOOK(record_start) | ||
2 | PERF_HOOK(record_end) | ||
3 | PERF_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 | |||
16 | static sigjmp_buf jmpbuf; | ||
17 | static const struct perf_hook_desc *current_perf_hook; | ||
18 | |||
19 | void 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 | |||
35 | void perf_hooks__recover(void) | ||
36 | { | ||
37 | if (current_perf_hook) | ||
38 | siglongjmp(jmpbuf, 1); | ||
39 | } | ||
40 | |||
41 | #define PERF_HOOK(name) \ | ||
42 | perf_hook_func_t __perf_hook_func_##name = NULL; \ | ||
43 | struct 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 | |||
51 | static struct perf_hook_desc *perf_hooks[] = { | ||
52 | #include "perf-hooks-list.h" | ||
53 | }; | ||
54 | #undef PERF_HOOK | ||
55 | |||
56 | int 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 | |||
73 | perf_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 | ||
5 | extern "C" { | ||
6 | #endif | ||
7 | |||
8 | typedef void (*perf_hook_func_t)(void); | ||
9 | struct perf_hook_desc { | ||
10 | const char * const hook_name; | ||
11 | perf_hook_func_t * const p_hook_func; | ||
12 | }; | ||
13 | |||
14 | extern void perf_hooks__invoke(const struct perf_hook_desc *); | ||
15 | extern void perf_hooks__recover(void); | ||
16 | |||
17 | #define PERF_HOOK(name) \ | ||
18 | extern struct perf_hook_desc __perf_hook_desc_##name; \ | ||
19 | static 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 | |||
27 | extern int | ||
28 | perf_hooks__set_hook(const char *hook_name, | ||
29 | perf_hook_func_t hook_func); | ||
30 | |||
31 | extern perf_hook_func_t | ||
32 | perf_hooks__get_hook(const char *hook_name); | ||
33 | |||
34 | #ifdef __cplusplus | ||
35 | } | ||
36 | #endif | ||
37 | #endif | ||