Skip to content

Commit

Permalink
perf tools: Introduce perf hooks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Wang Nan authored and Arnaldo Carvalho de Melo committed Nov 29, 2016
1 parent 5a6acad commit a074865
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 0 deletions.
11 changes: 11 additions & 0 deletions tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "util/llvm-utils.h"
#include "util/bpf-loader.h"
#include "util/trigger.h"
#include "util/perf-hooks.h"
#include "asm/bug.h"

#include <unistd.h>
Expand Down Expand Up @@ -206,6 +207,12 @@ static void sig_handler(int sig)
done = 1;
}

static void sigsegv_handler(int sig)
{
perf_hooks__recover();
sighandler_dump_stack(sig);
}

static void record__sig_exit(void)
{
if (signr == -1)
Expand Down Expand Up @@ -833,6 +840,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
signal(SIGSEGV, sigsegv_handler);

if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) {
signal(SIGUSR2, snapshot_sig_handler);
Expand Down Expand Up @@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)

trigger_ready(&auxtrace_snapshot_trigger);
trigger_ready(&switch_output_trigger);
perf_hooks__invoke_record_start();
for (;;) {
unsigned long long hits = rec->samples;

Expand Down Expand Up @@ -1114,6 +1123,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
}
}

perf_hooks__invoke_record_end();

if (!err && !quiet) {
char samples[128];
const char *postfix = rec->timestamp_filename ?
Expand Down
1 change: 1 addition & 0 deletions tools/perf/tests/Build
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ perf-y += backward-ring-buffer.o
perf-y += sdt.o
perf-y += is_printable_array.o
perf-y += bitmap.o
perf-y += perf-hooks.o

$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir)
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/tests/builtin-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ static struct test generic_tests[] = {
.desc = "Test bitmap print",
.func = test__bitmap_print,
},
{
.desc = "Test perf hooks",
.func = test__perf_hooks,
},
{
.func = NULL,
},
Expand Down
44 changes: 44 additions & 0 deletions tools/perf/tests/perf-hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <signal.h>
#include <stdlib.h>

#include "tests.h"
#include "debug.h"
#include "util.h"
#include "perf-hooks.h"

static void sigsegv_handler(int sig __maybe_unused)
{
pr_debug("SIGSEGV is observed as expected, try to recover.\n");
perf_hooks__recover();
signal(SIGSEGV, SIG_DFL);
raise(SIGSEGV);
exit(-1);
}

static int hook_flags;

static void the_hook(void)
{
int *p = NULL;

hook_flags = 1234;

/* Generate a segfault, test perf_hooks__recover */
*p = 0;
}

int test__perf_hooks(int subtest __maybe_unused)
{
signal(SIGSEGV, sigsegv_handler);
perf_hooks__set_hook("test", the_hook);
perf_hooks__invoke_test();

/* hook is triggered? */
if (hook_flags != 1234)
return TEST_FAIL;

/* the buggy hook is removed? */
if (perf_hooks__get_hook("test"))
return TEST_FAIL;
return TEST_OK;
}
1 change: 1 addition & 0 deletions tools/perf/tests/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ int test__cpu_map_print(int subtest);
int test__sdt_event(int subtest);
int test__is_printable_array(int subtest);
int test__bitmap_print(int subtest);
int test__perf_hooks(int subtest);

#if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
Expand Down
2 changes: 2 additions & 0 deletions tools/perf/util/Build
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ libperf-$(CONFIG_LIBELF) += genelf.o
libperf-$(CONFIG_DWARF) += genelf_debug.o
endif

libperf-y += perf-hooks.o

CFLAGS_config.o += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
# avoid compiler warnings in 32-bit mode
CFLAGS_genelf_debug.o += -Wno-packed
Expand Down
3 changes: 3 additions & 0 deletions tools/perf/util/perf-hooks-list.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PERF_HOOK(record_start)
PERF_HOOK(record_end)
PERF_HOOK(test)
84 changes: 84 additions & 0 deletions tools/perf/util/perf-hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* perf_hooks.c
*
* Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2016 Huawei Inc.
*/

#include <errno.h>
#include <stdlib.h>
#include <setjmp.h>
#include <linux/err.h>
#include "util/util.h"
#include "util/debug.h"
#include "util/perf-hooks.h"

static sigjmp_buf jmpbuf;
static const struct perf_hook_desc *current_perf_hook;

void perf_hooks__invoke(const struct perf_hook_desc *desc)
{
if (!(desc && desc->p_hook_func && *desc->p_hook_func))
return;

if (sigsetjmp(jmpbuf, 1)) {
pr_warning("Fatal error (SEGFAULT) in perf hook '%s'\n",
desc->hook_name);
*(current_perf_hook->p_hook_func) = NULL;
} else {
current_perf_hook = desc;
(**desc->p_hook_func)();
}
current_perf_hook = NULL;
}

void perf_hooks__recover(void)
{
if (current_perf_hook)
siglongjmp(jmpbuf, 1);
}

#define PERF_HOOK(name) \
perf_hook_func_t __perf_hook_func_##name = NULL; \
struct perf_hook_desc __perf_hook_desc_##name = \
{.hook_name = #name, .p_hook_func = &__perf_hook_func_##name};
#include "perf-hooks-list.h"
#undef PERF_HOOK

#define PERF_HOOK(name) \
&__perf_hook_desc_##name,

static struct perf_hook_desc *perf_hooks[] = {
#include "perf-hooks-list.h"
};
#undef PERF_HOOK

int perf_hooks__set_hook(const char *hook_name,
perf_hook_func_t hook_func)
{
unsigned int i;

for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
continue;

if (*(perf_hooks[i]->p_hook_func))
pr_warning("Overwrite existing hook: %s\n", hook_name);
*(perf_hooks[i]->p_hook_func) = hook_func;
return 0;
}
return -ENOENT;
}

perf_hook_func_t perf_hooks__get_hook(const char *hook_name)
{
unsigned int i;

for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
continue;

return *(perf_hooks[i]->p_hook_func);
}
return ERR_PTR(-ENOENT);
}
37 changes: 37 additions & 0 deletions tools/perf/util/perf-hooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef PERF_UTIL_PERF_HOOKS_H
#define PERF_UTIL_PERF_HOOKS_H

#ifdef __cplusplus
extern "C" {
#endif

typedef void (*perf_hook_func_t)(void);
struct perf_hook_desc {
const char * const hook_name;
perf_hook_func_t * const p_hook_func;
};

extern void perf_hooks__invoke(const struct perf_hook_desc *);
extern void perf_hooks__recover(void);

#define PERF_HOOK(name) \
extern struct perf_hook_desc __perf_hook_desc_##name; \
static inline void perf_hooks__invoke_##name(void) \
{ \
perf_hooks__invoke(&__perf_hook_desc_##name); \
}

#include "perf-hooks-list.h"
#undef PERF_HOOK

extern int
perf_hooks__set_hook(const char *hook_name,
perf_hook_func_t hook_func);

extern perf_hook_func_t
perf_hooks__get_hook(const char *hook_name);

#ifdef __cplusplus
}
#endif
#endif

0 comments on commit a074865

Please sign in to comment.