|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * perf_hooks.c | 
|  | * | 
|  | * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> | 
|  | * Copyright (C) 2016 Huawei Inc. | 
|  | */ | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <setjmp.h> | 
|  | #include <linux/err.h> | 
|  | #include <linux/kernel.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)(desc->hook_ctx); | 
|  | } | 
|  | 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,	\ | 
|  | .hook_ctx = NULL}; | 
|  | #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, | 
|  | void *hook_ctx) | 
|  | { | 
|  | 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; | 
|  | perf_hooks[i]->hook_ctx = hook_ctx; | 
|  | 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); | 
|  | } |