18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * perf_hooks.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Huawei Inc.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <errno.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <setjmp.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include "util/debug.h"
168c2ecf20Sopenharmony_ci#include "util/perf-hooks.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic sigjmp_buf jmpbuf;
198c2ecf20Sopenharmony_cistatic const struct perf_hook_desc *current_perf_hook;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_civoid perf_hooks__invoke(const struct perf_hook_desc *desc)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	if (!(desc && desc->p_hook_func && *desc->p_hook_func))
248c2ecf20Sopenharmony_ci		return;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (sigsetjmp(jmpbuf, 1)) {
278c2ecf20Sopenharmony_ci		pr_warning("Fatal error (SEGFAULT) in perf hook '%s'\n",
288c2ecf20Sopenharmony_ci			   desc->hook_name);
298c2ecf20Sopenharmony_ci		*(current_perf_hook->p_hook_func) = NULL;
308c2ecf20Sopenharmony_ci	} else {
318c2ecf20Sopenharmony_ci		current_perf_hook = desc;
328c2ecf20Sopenharmony_ci		(**desc->p_hook_func)(desc->hook_ctx);
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci	current_perf_hook = NULL;
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_civoid perf_hooks__recover(void)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (current_perf_hook)
408c2ecf20Sopenharmony_ci		siglongjmp(jmpbuf, 1);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define PERF_HOOK(name)					\
448c2ecf20Sopenharmony_ciperf_hook_func_t __perf_hook_func_##name = NULL;	\
458c2ecf20Sopenharmony_cistruct perf_hook_desc __perf_hook_desc_##name =		\
468c2ecf20Sopenharmony_ci	{.hook_name = #name,				\
478c2ecf20Sopenharmony_ci	 .p_hook_func = &__perf_hook_func_##name,	\
488c2ecf20Sopenharmony_ci	 .hook_ctx = NULL};
498c2ecf20Sopenharmony_ci#include "perf-hooks-list.h"
508c2ecf20Sopenharmony_ci#undef PERF_HOOK
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define PERF_HOOK(name)		\
538c2ecf20Sopenharmony_ci	&__perf_hook_desc_##name,
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct perf_hook_desc *perf_hooks[] = {
568c2ecf20Sopenharmony_ci#include "perf-hooks-list.h"
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci#undef PERF_HOOK
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint perf_hooks__set_hook(const char *hook_name,
618c2ecf20Sopenharmony_ci			 perf_hook_func_t hook_func,
628c2ecf20Sopenharmony_ci			 void *hook_ctx)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned int i;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
678c2ecf20Sopenharmony_ci		if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
688c2ecf20Sopenharmony_ci			continue;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		if (*(perf_hooks[i]->p_hook_func))
718c2ecf20Sopenharmony_ci			pr_warning("Overwrite existing hook: %s\n", hook_name);
728c2ecf20Sopenharmony_ci		*(perf_hooks[i]->p_hook_func) = hook_func;
738c2ecf20Sopenharmony_ci		perf_hooks[i]->hook_ctx = hook_ctx;
748c2ecf20Sopenharmony_ci		return 0;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	return -ENOENT;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciperf_hook_func_t perf_hooks__get_hook(const char *hook_name)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	unsigned int i;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(perf_hooks); i++) {
848c2ecf20Sopenharmony_ci		if (strcmp(hook_name, perf_hooks[i]->hook_name) != 0)
858c2ecf20Sopenharmony_ci			continue;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		return *(perf_hooks[i]->p_hook_func);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOENT);
908c2ecf20Sopenharmony_ci}
91