[PATCH 08/19] perf tools: Introduce perf hooks

2016-12-01 Thread Arnaldo Carvalho de Melo
From: Wang Nan 

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 
Cc: Alexei Starovoitov 
Cc: He Kuang 
Cc: Jiri Olsa 
Cc: Joe Stringer 
Cc: Zefan Li 
Cc: pi3or...@163.com
Link: http://lkml.kernel.org/r/20161126070354.141764-5-wangn...@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo 
---
 tools/perf/builtin-record.c   | 11 +
 tools/perf/tests/Build|  1 +
 tools/perf/tests/builtin-test.c   |  4 ++
 tools/perf/tests/perf-hooks.c | 44 
 tools/perf/tests/tests.h  |  1 +
 tools/perf/util/Build |  2 +
 tools/perf/util/perf-hooks-list.h |  3 ++
 tools/perf/util/perf-hooks.c  | 84 +++
 tools/perf/util/perf-hooks.h  | 37 +
 9 files changed, 187 insertions(+)
 create mode 100644 tools/perf/tests/perf-hooks.c
 create mode 100644 tools/perf/util/perf-hooks-list.h
 create mode 100644 tools/perf/util/perf-hooks.c
 create mode 100644 tools/perf/util/perf-hooks.h

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 @@
 #include "util/llvm-utils.h"
 #include "util/bpf-loader.h"
 #include "util/trigger.h"
+#include "util/perf-hooks.h"
 #include "asm/bug.h"
 
 #include 
@@ -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)
@@ -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);
@@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
 
trigger_ready(_snapshot_trigger);
trigger_ready(_output_trigger);
+   perf_hooks__invoke_record_start();
for (;;) {
unsigned long long hits = rec->samples;
 
@@ -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 ?
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
 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)
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[] = {
.func = test__bitmap_print,
},
{
+   .desc = "Test perf hooks",
+   .func = test__perf_hooks,
+   },
+   {
.func = NULL,
},
 };
diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c
new file mode 100644
index ..9338cb2c25ab
--- /dev/null
+++ b/tools/perf/tests/perf-hooks.c
@@ -0,0 +1,44 @@
+#include 
+#include 
+
+#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 

[PATCH 08/19] perf tools: Introduce perf hooks

2016-12-01 Thread Arnaldo Carvalho de Melo
From: Wang Nan 

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 
Cc: Alexei Starovoitov 
Cc: He Kuang 
Cc: Jiri Olsa 
Cc: Joe Stringer 
Cc: Zefan Li 
Cc: pi3or...@163.com
Link: http://lkml.kernel.org/r/20161126070354.141764-5-wangn...@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo 
---
 tools/perf/builtin-record.c   | 11 +
 tools/perf/tests/Build|  1 +
 tools/perf/tests/builtin-test.c   |  4 ++
 tools/perf/tests/perf-hooks.c | 44 
 tools/perf/tests/tests.h  |  1 +
 tools/perf/util/Build |  2 +
 tools/perf/util/perf-hooks-list.h |  3 ++
 tools/perf/util/perf-hooks.c  | 84 +++
 tools/perf/util/perf-hooks.h  | 37 +
 9 files changed, 187 insertions(+)
 create mode 100644 tools/perf/tests/perf-hooks.c
 create mode 100644 tools/perf/util/perf-hooks-list.h
 create mode 100644 tools/perf/util/perf-hooks.c
 create mode 100644 tools/perf/util/perf-hooks.h

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 @@
 #include "util/llvm-utils.h"
 #include "util/bpf-loader.h"
 #include "util/trigger.h"
+#include "util/perf-hooks.h"
 #include "asm/bug.h"
 
 #include 
@@ -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)
@@ -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);
@@ -970,6 +978,7 @@ static int __cmd_record(struct record *rec, int argc, const 
char **argv)
 
trigger_ready(_snapshot_trigger);
trigger_ready(_output_trigger);
+   perf_hooks__invoke_record_start();
for (;;) {
unsigned long long hits = rec->samples;
 
@@ -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 ?
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
 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)
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[] = {
.func = test__bitmap_print,
},
{
+   .desc = "Test perf hooks",
+   .func = test__perf_hooks,
+   },
+   {
.func = NULL,
},
 };
diff --git a/tools/perf/tests/perf-hooks.c b/tools/perf/tests/perf-hooks.c
new file mode 100644
index ..9338cb2c25ab
--- /dev/null
+++ b/tools/perf/tests/perf-hooks.c
@@ -0,0 +1,44 @@
+#include 
+#include 
+
+#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,