kunit: Expose 'static stub' API to redirect functions
Add a simple way of redirecting calls to functions by including a
special prologue in the "real" function which checks to see if the
replacement function should be called (and, if so, calls it).
To redirect calls to a function, make the first (non-declaration) line
of the function:
KUNIT_STATIC_STUB_REDIRECT(function_name, [function arguments]);
(This will compile away to nothing if KUnit is not enabled, otherwise it
will check if a redirection is active, call the replacement function,
and return. This check is protected by a static branch, so has very
little overhead when there are no KUnit tests running.)
Calls to the real function can be redirected to a replacement using:
kunit_activate_static_stub(test, real_fn, replacement_fn);
The redirection will only affect calls made from within the kthread of
the current test, and will be automatically disabled when the test
completes. It can also be manually disabled with
kunit_deactivate_static_stub().
The 'example' KUnit test suite has a more complete example.
Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2023-01-31 14:46:40 +08:00
|
|
|
/* 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. */
|
2023-07-26 01:20:51 +08:00
|
|
|
#define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) do {} while (0)
|
kunit: Expose 'static stub' API to redirect functions
Add a simple way of redirecting calls to functions by including a
special prologue in the "real" function which checks to see if the
replacement function should be called (and, if so, calls it).
To redirect calls to a function, make the first (non-declaration) line
of the function:
KUNIT_STATIC_STUB_REDIRECT(function_name, [function arguments]);
(This will compile away to nothing if KUnit is not enabled, otherwise it
will check if a redirection is active, call the replacement function,
and return. This check is protected by a static branch, so has very
little overhead when there are no KUnit tests running.)
Calls to the real function can be redirected to a replacement using:
kunit_activate_static_stub(test, real_fn, replacement_fn);
The redirection will only affect calls made from within the kthread of
the current test, and will be automatically disabled when the test
completes. It can also be manually disabled with
kunit_deactivate_static_stub().
The 'example' KUnit test suite has a more complete example.
Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2023-01-31 14:46:40 +08:00
|
|
|
|
|
|
|
#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
|
2023-07-26 01:20:51 +08:00
|
|
|
* replacement function will be called by KUNIT_STATIC_STUB_REDIRECT(), which
|
kunit: Expose 'static stub' API to redirect functions
Add a simple way of redirecting calls to functions by including a
special prologue in the "real" function which checks to see if the
replacement function should be called (and, if so, calls it).
To redirect calls to a function, make the first (non-declaration) line
of the function:
KUNIT_STATIC_STUB_REDIRECT(function_name, [function arguments]);
(This will compile away to nothing if KUnit is not enabled, otherwise it
will check if a redirection is active, call the replacement function,
and return. This check is protected by a static branch, so has very
little overhead when there are no KUnit tests running.)
Calls to the real function can be redirected to a replacement using:
kunit_activate_static_stub(test, real_fn, replacement_fn);
The redirection will only affect calls made from within the kthread of
the current test, and will be automatically disabled when the test
completes. It can also be manually disabled with
kunit_deactivate_static_stub().
The 'example' KUnit test suite has a more complete example.
Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2023-01-31 14:46:40 +08:00
|
|
|
* 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
|
2023-07-26 01:20:51 +08:00
|
|
|
* KUNIT_STATIC_STUB_REDIRECT() for this to work. real_fn_addr and
|
kunit: Expose 'static stub' API to redirect functions
Add a simple way of redirecting calls to functions by including a
special prologue in the "real" function which checks to see if the
replacement function should be called (and, if so, calls it).
To redirect calls to a function, make the first (non-declaration) line
of the function:
KUNIT_STATIC_STUB_REDIRECT(function_name, [function arguments]);
(This will compile away to nothing if KUnit is not enabled, otherwise it
will check if a redirection is active, call the replacement function,
and return. This check is protected by a static branch, so has very
little overhead when there are no KUnit tests running.)
Calls to the real function can be redirected to a replacement using:
kunit_activate_static_stub(test, real_fn, replacement_fn);
The redirection will only affect calls made from within the kthread of
the current test, and will be automatically disabled when the test
completes. It can also be manually disabled with
kunit_deactivate_static_stub().
The 'example' KUnit test suite has a more complete example.
Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2023-01-31 14:46:40 +08:00
|
|
|
* 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 { \
|
kunit: Allow passing function pointer to kunit_activate_static_stub()
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>
2023-12-21 18:38:56 +08:00
|
|
|
typecheck_fn(typeof(&replacement_addr), real_fn_addr); \
|
kunit: Expose 'static stub' API to redirect functions
Add a simple way of redirecting calls to functions by including a
special prologue in the "real" function which checks to see if the
replacement function should be called (and, if so, calls it).
To redirect calls to a function, make the first (non-declaration) line
of the function:
KUNIT_STATIC_STUB_REDIRECT(function_name, [function arguments]);
(This will compile away to nothing if KUnit is not enabled, otherwise it
will check if a redirection is active, call the replacement function,
and return. This check is protected by a static branch, so has very
little overhead when there are no KUnit tests running.)
Calls to the real function can be redirected to a replacement using:
kunit_activate_static_stub(test, real_fn, replacement_fn);
The redirection will only affect calls made from within the kthread of
the current test, and will be automatically disabled when the test
completes. It can also be manually disabled with
kunit_deactivate_static_stub().
The 'example' KUnit test suite has a more complete example.
Co-developed-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: Daniel Latypov <dlatypov@google.com>
Signed-off-by: David Gow <davidgow@google.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2023-01-31 14:46:40 +08:00
|
|
|
__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
|