mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
fcbac39b7d
Swap the arguments to typecheck_fn() in kunit_activate_static_stub() so that real_fn_addr can be either the function itself or a pointer to that function. This is useful to simplify redirecting static functions in a module. Having to pass the actual function meant that it must be exported from the module. Either making the 'static' and EXPORT_SYMBOL*() conditional (which makes the code messy), or change it to always exported (which increases the export namespace and prevents the compiler inlining a trivial stub function in non-test builds). With the original definition of kunit_activate_static_stub() the address of real_fn_addr was passed to typecheck_fn() as the type to be passed. This meant that if real_fn_addr was a pointer-to-function it would resolve to a ** instead of a *, giving an error like this: error: initialization of ‘int (**)(int)’ from incompatible pointer type ‘int (*)(int)’ [-Werror=incompatible-pointer-types] kunit_activate_static_stub(test, add_one_fn_ptr, subtract_one); | ^~~~~~~~~~~~ ./include/linux/typecheck.h:21:25: note: in definition of macro ‘typecheck_fn’ 21 | ({ typeof(type) __tmp = function; \ Swapping the arguments to typecheck_fn makes it take the type of a pointer to the replacement function. Either a function or a pointer to function can be assigned to that. For example: static int some_function(int x) { /* whatever */ } int (* some_function_ptr)(int) = some_function; static int replacement(int x) { /* whatever */ } Then: kunit_activate_static_stub(test, some_function, replacement); yields: typecheck_fn(typeof(&replacement), some_function); and: kunit_activate_static_stub(test, some_function_ptr, replacement); yields: typecheck_fn(typeof(&replacement), some_function_ptr); The two typecheck_fn() then resolve to: int (*__tmp)(int) = some_function; and int (*__tmp)(int) = some_function_ptr; Both of these are valid. In the first case the compiler inserts an implicit '&' to take the address of the supplied function, and in the second case the RHS is already a pointer to the same type. Signed-off-by: Richard Fitzgerald <rf@opensource.cirrus.com> Reviewed-by: Rae Moar <rmoar@google.com> Reviewed-by: David Gow <davidgow@google.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
114 lines
3.6 KiB
C
114 lines
3.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* KUnit function redirection (static stubbing) API.
|
|
*
|
|
* Copyright (C) 2022, Google LLC.
|
|
* Author: David Gow <davidgow@google.com>
|
|
*/
|
|
#ifndef _KUNIT_STATIC_STUB_H
|
|
#define _KUNIT_STATIC_STUB_H
|
|
|
|
#if !IS_ENABLED(CONFIG_KUNIT)
|
|
|
|
/* If CONFIG_KUNIT is not enabled, these stubs quietly disappear. */
|
|
#define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) do {} while (0)
|
|
|
|
#else
|
|
|
|
#include <kunit/test.h>
|
|
#include <kunit/test-bug.h>
|
|
|
|
#include <linux/compiler.h> /* for {un,}likely() */
|
|
#include <linux/sched.h> /* for task_struct */
|
|
|
|
|
|
/**
|
|
* KUNIT_STATIC_STUB_REDIRECT() - call a replacement 'static stub' if one exists
|
|
* @real_fn_name: The name of this function (as an identifier, not a string)
|
|
* @args: All of the arguments passed to this function
|
|
*
|
|
* This is a function prologue which is used to allow calls to the current
|
|
* function to be redirected by a KUnit test. KUnit tests can call
|
|
* kunit_activate_static_stub() to pass a replacement function in. The
|
|
* replacement function will be called by KUNIT_STATIC_STUB_REDIRECT(), which
|
|
* will then return from the function. If the caller is not in a KUnit context,
|
|
* the function will continue execution as normal.
|
|
*
|
|
* Example:
|
|
*
|
|
* .. code-block:: c
|
|
*
|
|
* int real_func(int n)
|
|
* {
|
|
* KUNIT_STATIC_STUB_REDIRECT(real_func, n);
|
|
* return 0;
|
|
* }
|
|
*
|
|
* int replacement_func(int n)
|
|
* {
|
|
* return 42;
|
|
* }
|
|
*
|
|
* void example_test(struct kunit *test)
|
|
* {
|
|
* kunit_activate_static_stub(test, real_func, replacement_func);
|
|
* KUNIT_EXPECT_EQ(test, real_func(1), 42);
|
|
* }
|
|
*
|
|
*/
|
|
#define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) \
|
|
do { \
|
|
typeof(&real_fn_name) replacement; \
|
|
struct kunit *current_test = kunit_get_current_test(); \
|
|
\
|
|
if (likely(!current_test)) \
|
|
break; \
|
|
\
|
|
replacement = kunit_hooks.get_static_stub_address(current_test, \
|
|
&real_fn_name); \
|
|
\
|
|
if (unlikely(replacement)) \
|
|
return replacement(args); \
|
|
} while (0)
|
|
|
|
/* Helper function for kunit_activate_static_stub(). The macro does
|
|
* typechecking, so use it instead.
|
|
*/
|
|
void __kunit_activate_static_stub(struct kunit *test,
|
|
void *real_fn_addr,
|
|
void *replacement_addr);
|
|
|
|
/**
|
|
* kunit_activate_static_stub() - replace a function using static stubs.
|
|
* @test: A pointer to the 'struct kunit' test context for the current test.
|
|
* @real_fn_addr: The address of the function to replace.
|
|
* @replacement_addr: The address of the function to replace it with.
|
|
*
|
|
* When activated, calls to real_fn_addr from within this test (even if called
|
|
* indirectly) will instead call replacement_addr. The function pointed to by
|
|
* real_fn_addr must begin with the static stub prologue in
|
|
* KUNIT_STATIC_STUB_REDIRECT() for this to work. real_fn_addr and
|
|
* replacement_addr must have the same type.
|
|
*
|
|
* The redirection can be disabled again with kunit_deactivate_static_stub().
|
|
*/
|
|
#define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do { \
|
|
typecheck_fn(typeof(&replacement_addr), real_fn_addr); \
|
|
__kunit_activate_static_stub(test, real_fn_addr, replacement_addr); \
|
|
} while (0)
|
|
|
|
|
|
/**
|
|
* kunit_deactivate_static_stub() - disable a function redirection
|
|
* @test: A pointer to the 'struct kunit' test context for the current test.
|
|
* @real_fn_addr: The address of the function to no-longer redirect
|
|
*
|
|
* Deactivates a redirection configured with kunit_activate_static_stub(). After
|
|
* this function returns, calls to real_fn_addr() will execute the original
|
|
* real_fn, not any previously-configured replacement.
|
|
*/
|
|
void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr);
|
|
|
|
#endif
|
|
#endif
|