Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Wang Nan and committed by
Arnaldo Carvalho de Melo
a074865e 5a6acad1

+187
+11
tools/perf/builtin-record.c
··· 37 37 #include "util/llvm-utils.h" 38 38 #include "util/bpf-loader.h" 39 39 #include "util/trigger.h" 40 + #include "util/perf-hooks.h" 40 41 #include "asm/bug.h" 41 42 42 43 #include <unistd.h> ··· 205 204 signr = sig; 206 205 207 206 done = 1; 207 + } 208 + 209 + static void sigsegv_handler(int sig) 210 + { 211 + perf_hooks__recover(); 212 + sighandler_dump_stack(sig); 208 213 } 209 214 210 215 static void record__sig_exit(void) ··· 840 833 signal(SIGCHLD, sig_handler); 841 834 signal(SIGINT, sig_handler); 842 835 signal(SIGTERM, sig_handler); 836 + signal(SIGSEGV, sigsegv_handler); 843 837 844 838 if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) { 845 839 signal(SIGUSR2, snapshot_sig_handler); ··· 978 970 979 971 trigger_ready(&auxtrace_snapshot_trigger); 980 972 trigger_ready(&switch_output_trigger); 973 + perf_hooks__invoke_record_start(); 981 974 for (;;) { 982 975 unsigned long long hits = rec->samples; 983 976 ··· 1122 1113 } 1123 1114 } 1124 1115 } 1116 + 1117 + perf_hooks__invoke_record_end(); 1125 1118 1126 1119 if (!err && !quiet) { 1127 1120 char samples[128];
+1
tools/perf/tests/Build
··· 42 42 perf-y += sdt.o 43 43 perf-y += is_printable_array.o 44 44 perf-y += bitmap.o 45 + perf-y += perf-hooks.o 45 46 46 47 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build 47 48 $(call rule_mkdir)
+4
tools/perf/tests/builtin-test.c
··· 230 230 .func = test__bitmap_print, 231 231 }, 232 232 { 233 + .desc = "Test perf hooks", 234 + .func = test__perf_hooks, 235 + }, 236 + { 233 237 .func = NULL, 234 238 }, 235 239 };
+44
tools/perf/tests/perf-hooks.c
··· 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 + }
+1
tools/perf/tests/tests.h
··· 91 91 int test__sdt_event(int subtest); 92 92 int test__is_printable_array(int subtest); 93 93 int test__bitmap_print(int subtest); 94 + int test__perf_hooks(int subtest); 94 95 95 96 #if defined(__arm__) || defined(__aarch64__) 96 97 #ifdef HAVE_DWARF_UNWIND_SUPPORT
+2
tools/perf/util/Build
··· 123 123 libperf-$(CONFIG_DWARF) += genelf_debug.o 124 124 endif 125 125 126 + libperf-y += perf-hooks.o 127 + 126 128 CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" 127 129 # avoid compiler warnings in 32-bit mode 128 130 CFLAGS_genelf_debug.o += -Wno-packed
+3
tools/perf/util/perf-hooks-list.h
··· 1 + PERF_HOOK(record_start) 2 + PERF_HOOK(record_end) 3 + PERF_HOOK(test)
+84
tools/perf/util/perf-hooks.c
··· 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 + }
+37
tools/perf/util/perf-hooks.h
··· 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