Every KUNIT_ASSERT/EXPECT() invocation puts a `kunit_assert` object onto the stack. The most common one is `kunit_binary_assert` which is 88 bytes on UML. So in the cases where the compiler doesn't optimize this away, we can very quickly blow up the stack size.
This series implements Linus' suggestion in [1]. Namely, we split out the file, line number, and assert_type (EXPECT/ASSERT) out of kunit_assert.
We can also drop the entirely unused `struct kunit *test` field, saving a bit more space as well.
All together, sizeof(struct kunit_assert) went from 48 to 24 on UML. Note: the other assert types are bigger, see [2].
This series also adds in an example test that uses all the base KUNIT_EXPECT macros to both advertise their existence to new users and serve as a smoketest for all these changes here.
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ [2] e.g. consider the most commonly used assert (also the biggest) struct kunit_binary_assert { struct kunit_assert assert; const char *operation; const char *left_text; long long left_value; const char *right_text; long long right_value; }; So sizeof(struct kunit_binary_assert) = went from 88 to 64. I.e. only a 27% reduction instead of 50% in the most common case.
All 3 of the `const char*` could be split out into a `static` var as well, but that's a bit trickier to do with how all the macros are written.
=== Changelog === v1 -> v2: * made the new example test more focused on documenting the macros rather than using them all as a smoketest * s/kunit_failed_assertion()/kunit_do_failed_assertion() * added `unlikely()` to `if(!(pass))` check in KUNIT_ASSERTION()
Daniel Latypov (6): kunit: add example test case showing off all the expect macros kunit: move check if assertion passed into the macros kunit: drop unused kunit* field in kunit_assert kunit: factor out kunit_base_assert_format() call into kunit_fail() kunit: split out part of kunit_assert into a static const kunit: drop unused assert_type from kunit_assert and clean up macros
include/kunit/assert.h | 88 +++++++++++----------------------- include/kunit/test.h | 53 ++++++++++---------- lib/kunit/assert.c | 15 ++---- lib/kunit/kunit-example-test.c | 42 ++++++++++++++++ lib/kunit/test.c | 27 +++++------ 5 files changed, 117 insertions(+), 108 deletions(-)
base-commit: ad659ccb5412874c6a89d3588cb18857c00e9d0f
Currently, these macros are only really documented near the bottom of https://www.kernel.org/doc/html/latest/dev-tools/kunit/api/test.html#c.KUNIT....
E.g. it's likely someone might just not realize that KUNIT_EXPECT_STREQ() exists and instead use KUNIT_EXPECT_FALSE(strcmp()) or similar.
This can also serve as a basic smoketest that the KUnit assert machinery still works for all the macros.
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: Brendan Higgins brendanhiggins@google.com Reviewed-by: David Gow davidgow@google.com --- lib/kunit/kunit-example-test.c | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+)
diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 51099b0ca29c..4bbf37c04eba 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -69,6 +69,47 @@ static void example_mark_skipped_test(struct kunit *test) /* This line should run */ kunit_info(test, "You should see this line."); } + +/* + * This test shows off all the types of KUNIT_EXPECT macros. + */ +static void example_all_expect_macros_test(struct kunit *test) +{ + /* Boolean assertions */ + KUNIT_EXPECT_TRUE(test, true); + KUNIT_EXPECT_FALSE(test, false); + + /* Integer assertions */ + KUNIT_EXPECT_EQ(test, 1, 1); /* check == */ + KUNIT_EXPECT_GE(test, 1, 1); /* check >= */ + KUNIT_EXPECT_LE(test, 1, 1); /* check <= */ + KUNIT_EXPECT_NE(test, 1, 0); /* check != */ + KUNIT_EXPECT_GT(test, 1, 0); /* check > */ + KUNIT_EXPECT_LT(test, 0, 1); /* check < */ + + /* Pointer assertions */ + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, test); + KUNIT_EXPECT_PTR_EQ(test, NULL, NULL); + KUNIT_EXPECT_PTR_NE(test, test, NULL); + + /* String assertions */ + KUNIT_EXPECT_STREQ(test, "hi", "hi"); + KUNIT_EXPECT_STRNEQ(test, "hi", "bye"); + + /* + * There are also ASSERT variants of all of the above that abort test + * execution if they fail. Useful for memory allocations, etc. + */ + KUNIT_ASSERT_GT(test, sizeof(char), 0); + + /* + * There are also _MSG variants of all of the above that let you include + * additional text on failure. + */ + KUNIT_EXPECT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!"); + KUNIT_ASSERT_GT_MSG(test, sizeof(int), 0, "Your ints are 0-bit?!"); +} + /* * Here we make a list of all the test cases we want to add to the test suite * below. @@ -83,6 +124,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_simple_test), KUNIT_CASE(example_skip_test), KUNIT_CASE(example_mark_skipped_test), + KUNIT_CASE(example_all_expect_macros_test), {} };
Currently the code always calls kunit_do_assertion() even though it does nothing when `pass` is true.
This change moves the `if(!(pass))` check into the macro instead and renames the function to kunit_do_failed_assertion(). I feel this a bit easier to read and understand.
This has the potential upside of avoiding a function call that does nothing most of the time (assuming your tests are passing) but comes with the downside of generating a bit more code and branches. We try to mitigate the branches by tagging them with `unlikely()`.
This also means we don't have to initialize structs that we don't need, which will become a tiny bit more expensive if we switch over to using static variables to try and reduce stack usage. (There's runtime code to check if the variable has been initialized yet or not).
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: Brendan Higgins brendanhiggins@google.com --- include/kunit/test.h | 21 +++++++++++---------- lib/kunit/test.c | 13 ++++--------- 2 files changed, 15 insertions(+), 19 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h index b26400731c02..12cabd15449a 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -12,6 +12,7 @@ #include <kunit/assert.h> #include <kunit/try-catch.h>
+#include <linux/compiler.h> #include <linux/container_of.h> #include <linux/err.h> #include <linux/init.h> @@ -770,18 +771,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...); */ #define KUNIT_SUCCEED(test) do {} while (0)
-void kunit_do_assertion(struct kunit *test, - struct kunit_assert *assert, - bool pass, - const char *fmt, ...); +void kunit_do_failed_assertion(struct kunit *test, + struct kunit_assert *assert, + const char *fmt, ...);
#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \ - struct assert_class __assertion = INITIALIZER; \ - kunit_do_assertion(test, \ - &__assertion.assert, \ - pass, \ - fmt, \ - ##__VA_ARGS__); \ + if (unlikely(!(pass))) { \ + struct assert_class __assertion = INITIALIZER; \ + kunit_do_failed_assertion(test, \ + &__assertion.assert, \ + fmt, \ + ##__VA_ARGS__); \ + } \ } while (0)
diff --git a/lib/kunit/test.c b/lib/kunit/test.c index c7ed4aabec04..3a52c321c280 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test) WARN_ONCE(true, "Throw could not abort from test!\n"); }
-void kunit_do_assertion(struct kunit *test, - struct kunit_assert *assert, - bool pass, - const char *fmt, ...) +void kunit_do_failed_assertion(struct kunit *test, + struct kunit_assert *assert, + const char *fmt, ...) { va_list args; - - if (pass) - return; - va_start(args, fmt);
assert->message.fmt = fmt; @@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test, if (assert->type == KUNIT_ASSERTION) kunit_abort(test); } -EXPORT_SYMBOL_GPL(kunit_do_assertion); +EXPORT_SYMBOL_GPL(kunit_do_failed_assertion);
void kunit_init_test(struct kunit *test, const char *name, char *log) {
On Wed, Jan 12, 2022 at 3:42 AM Daniel Latypov dlatypov@google.com wrote:
Currently the code always calls kunit_do_assertion() even though it does nothing when `pass` is true.
This change moves the `if(!(pass))` check into the macro instead and renames the function to kunit_do_failed_assertion(). I feel this a bit easier to read and understand.
This has the potential upside of avoiding a function call that does nothing most of the time (assuming your tests are passing) but comes with the downside of generating a bit more code and branches. We try to mitigate the branches by tagging them with `unlikely()`.
This also means we don't have to initialize structs that we don't need, which will become a tiny bit more expensive if we switch over to using static variables to try and reduce stack usage. (There's runtime code to check if the variable has been initialized yet or not).
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: Brendan Higgins brendanhiggins@google.com
This looks good. I'm still not 100% sold that putting the if() outside the function is significantly easier to read, but I don't think it's harder to read either, and getting rid of the function call is probably worth it.
Reviewed-by: David Gow davidgow@google.com
-- David
include/kunit/test.h | 21 +++++++++++---------- lib/kunit/test.c | 13 ++++--------- 2 files changed, 15 insertions(+), 19 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h index b26400731c02..12cabd15449a 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -12,6 +12,7 @@ #include <kunit/assert.h> #include <kunit/try-catch.h>
+#include <linux/compiler.h> #include <linux/container_of.h> #include <linux/err.h> #include <linux/init.h> @@ -770,18 +771,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...); */ #define KUNIT_SUCCEED(test) do {} while (0)
-void kunit_do_assertion(struct kunit *test,
struct kunit_assert *assert,
bool pass,
const char *fmt, ...);
+void kunit_do_failed_assertion(struct kunit *test,
struct kunit_assert *assert,
const char *fmt, ...);
#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \
struct assert_class __assertion = INITIALIZER; \
kunit_do_assertion(test, \
&__assertion.assert, \
pass, \
fmt, \
##__VA_ARGS__); \
if (unlikely(!(pass))) { \
struct assert_class __assertion = INITIALIZER; \
kunit_do_failed_assertion(test, \
&__assertion.assert, \
fmt, \
##__VA_ARGS__); \
} \
} while (0)
diff --git a/lib/kunit/test.c b/lib/kunit/test.c index c7ed4aabec04..3a52c321c280 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -275,16 +275,11 @@ static void __noreturn kunit_abort(struct kunit *test) WARN_ONCE(true, "Throw could not abort from test!\n"); }
-void kunit_do_assertion(struct kunit *test,
struct kunit_assert *assert,
bool pass,
const char *fmt, ...)
+void kunit_do_failed_assertion(struct kunit *test,
struct kunit_assert *assert,
const char *fmt, ...)
{ va_list args;
if (pass)
return;
va_start(args, fmt); assert->message.fmt = fmt;
@@ -297,7 +292,7 @@ void kunit_do_assertion(struct kunit *test, if (assert->type == KUNIT_ASSERTION) kunit_abort(test); } -EXPORT_SYMBOL_GPL(kunit_do_assertion); +EXPORT_SYMBOL_GPL(kunit_do_failed_assertion);
void kunit_init_test(struct kunit *test, const char *name, char *log) { -- 2.34.1.575.g55b058a8bb-goog
The `struct kunit* test` field in kunit_assert is unused. Note: string_stream needs it, but it has its own `test` field. I assume `test` in `kunit_assert` predates this and was leftover after some refactoring.
This patch removes the field and cleans up the macros to avoid needlessly passing around `test`.
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: Brendan Higgins brendanhiggins@google.com Reviewed-by: David Gow davidgow@google.com --- include/kunit/assert.h | 45 ++++++++++++------------------------------ include/kunit/test.h | 14 +++++-------- 2 files changed, 18 insertions(+), 41 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index ad889b539ab3..3da6c792496c 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -30,7 +30,6 @@ enum kunit_assert_type {
/** * struct kunit_assert - Data for printing a failed assertion or expectation. - * @test: the test case this expectation/assertion is associated with. * @type: the type (either an expectation or an assertion) of this kunit_assert. * @line: the source code line number that the expectation/assertion is at. * @file: the file path of the source file that the expectation/assertion is in. @@ -41,7 +40,6 @@ enum kunit_assert_type { * format a string to a user reporting the failure. */ struct kunit_assert { - struct kunit *test; enum kunit_assert_type type; int line; const char *file; @@ -60,14 +58,12 @@ struct kunit_assert {
/** * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert. - * @kunit: The test case that this expectation/assertion is associated with. * @assert_type: The type (assertion or expectation) of this kunit_assert. * @fmt: The formatting function which builds a string out of this kunit_assert. * * The base initializer for a &struct kunit_assert. */ -#define KUNIT_INIT_ASSERT_STRUCT(kunit, assert_type, fmt) { \ - .test = kunit, \ +#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \ .file = __FILE__, \ .line = __LINE__, \ @@ -96,15 +92,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert,
/** * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * * Initializes a &struct kunit_fail_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(test, type) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_fail_assert_format) \ }
@@ -129,7 +123,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
/** * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * @cond: A string representation of the expression asserted true or false. * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise. @@ -137,9 +130,8 @@ void kunit_unary_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_unary_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(test, type, cond, expect_true) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_unary_assert_format), \ .condition = cond, \ .expected_true = expect_true \ @@ -167,7 +159,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a * &struct kunit_ptr_not_err_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * @txt: A string representation of the expression passed to the expectation. * @val: The actual evaluated pointer value of the expression. @@ -175,9 +166,8 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, type, txt, val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_ptr_not_err_assert_format), \ .text = txt, \ .value = val \ @@ -212,7 +202,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a * &struct kunit_binary_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * @op_str: A string representation of the comparison operator (e.g. "=="). * @left_str: A string representation of the expression in the left slot. @@ -223,15 +212,13 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \ op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_binary_assert_format), \ .operation = op_str, \ .left_text = left_str, \ @@ -269,7 +256,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT() - Initializes a * &struct kunit_binary_ptr_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * @op_str: A string representation of the comparison operator (e.g. "=="). * @left_str: A string representation of the expression in the left slot. @@ -280,15 +266,13 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \ op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_binary_ptr_assert_format), \ .operation = op_str, \ .left_text = left_str, \ @@ -326,7 +310,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a * &struct kunit_binary_str_assert. - * @test: The test case that this expectation/assertion is associated with. * @type: The type (assertion or expectation) of this kunit_assert. * @op_str: A string representation of the comparison operator (e.g. "=="). * @left_str: A string representation of the expression in the left slot. @@ -337,15 +320,13 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_str_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \ - type, \ +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \ op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(test, \ - type, \ + .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ kunit_binary_str_assert_format), \ .operation = op_str, \ .left_text = left_str, \ diff --git a/include/kunit/test.h b/include/kunit/test.h index 12cabd15449a..25ea3bce6663 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -790,7 +790,7 @@ void kunit_do_failed_assertion(struct kunit *test, KUNIT_ASSERTION(test, \ false, \ kunit_fail_assert, \ - KUNIT_INIT_FAIL_ASSERT_STRUCT(test, assert_type), \ + KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \ fmt, \ ##__VA_ARGS__)
@@ -820,8 +820,7 @@ void kunit_do_failed_assertion(struct kunit *test, KUNIT_ASSERTION(test, \ !!(condition) == !!expected_true, \ kunit_unary_assert, \ - KUNIT_INIT_UNARY_ASSERT_STRUCT(test, \ - assert_type, \ + KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \ #condition, \ expected_true), \ fmt, \ @@ -879,8 +878,7 @@ do { \ KUNIT_ASSERTION(test, \ __left op __right, \ assert_class, \ - ASSERT_CLASS_INIT(test, \ - assert_type, \ + ASSERT_CLASS_INIT(assert_type, \ #op, \ #left, \ __left, \ @@ -1234,8 +1232,7 @@ do { \ KUNIT_ASSERTION(test, \ strcmp(__left, __right) op 0, \ kunit_binary_str_assert, \ - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(test, \ - assert_type, \ + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \ #op, \ #left, \ __left, \ @@ -1294,8 +1291,7 @@ do { \ KUNIT_ASSERTION(test, \ !IS_ERR_OR_NULL(__ptr), \ kunit_ptr_not_err_assert, \ - KUNIT_INIT_PTR_NOT_ERR_STRUCT(test, \ - assert_type, \ + KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \ #ptr, \ __ptr), \ fmt, \
We call this function first thing for all the assertion `format()` functions. This is the part that prints the file and line number and assertion type (EXPECTATION, ASSERTION).
Having it as part of the format functions lets us have the flexibility to not print that information (or print it differently) for new assertion types, but I think this we don't need that.
And in the future, we'd like to consider factoring that data (file, line#, type) out of the kunit_assert struct and into a `static` variable, as Linus suggested [1], so we'd need to extract it anyways.
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: David Gow davidgow@google.com --- lib/kunit/assert.c | 6 ------ lib/kunit/test.c | 1 + 2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c index b972bda61c0c..4d9a1295efc7 100644 --- a/lib/kunit/assert.c +++ b/lib/kunit/assert.c @@ -40,7 +40,6 @@ EXPORT_SYMBOL_GPL(kunit_assert_print_msg); void kunit_fail_assert_format(const struct kunit_assert *assert, struct string_stream *stream) { - kunit_base_assert_format(assert, stream); string_stream_add(stream, "%pV", &assert->message); } EXPORT_SYMBOL_GPL(kunit_fail_assert_format); @@ -52,7 +51,6 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
unary_assert = container_of(assert, struct kunit_unary_assert, assert);
- kunit_base_assert_format(assert, stream); if (unary_assert->expected_true) string_stream_add(stream, KUNIT_SUBTEST_INDENT "Expected %s to be true, but is false\n", @@ -73,7 +71,6 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, ptr_assert = container_of(assert, struct kunit_ptr_not_err_assert, assert);
- kunit_base_assert_format(assert, stream); if (!ptr_assert->value) { string_stream_add(stream, KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n", @@ -119,7 +116,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, binary_assert = container_of(assert, struct kunit_binary_assert, assert);
- kunit_base_assert_format(assert, stream); string_stream_add(stream, KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n", binary_assert->left_text, @@ -147,7 +143,6 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, binary_assert = container_of(assert, struct kunit_binary_ptr_assert, assert);
- kunit_base_assert_format(assert, stream); string_stream_add(stream, KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n", binary_assert->left_text, @@ -187,7 +182,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert, binary_assert = container_of(assert, struct kunit_binary_str_assert, assert);
- kunit_base_assert_format(assert, stream); string_stream_add(stream, KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n", binary_assert->left_text, diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 3a52c321c280..345a9dd88c27 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -255,6 +255,7 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert) return; }
+ kunit_base_assert_format(assert, stream); assert->format(assert, stream);
kunit_print_string_stream(test, stream);
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com --- include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/** + * struct kunit_loc - Identifies the source location of a line of code. + * @line: the line number in the file. + * @file: the file name. + */ +struct kunit_loc { + int line; + const char *file; +}; + +#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ } + /** * struct kunit_assert - Data for printing a failed assertion or expectation. * @type: the type (either an expectation or an assertion) of this kunit_assert. - * @line: the source code line number that the expectation/assertion is at. - * @file: the file path of the source file that the expectation/assertion is in. * @message: an optional message to provide additional context. * @format: a function which formats the data in this kunit_assert to a string. * @@ -40,9 +50,7 @@ enum kunit_assert_type { * format a string to a user reporting the failure. */ struct kunit_assert { - enum kunit_assert_type type; - int line; - const char *file; + enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream); @@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \ - .file = __FILE__, \ - .line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \ }
-void kunit_base_assert_format(const struct kunit_assert *assert, - struct string_stream *stream); +void kunit_assert_prologue(const struct kunit_loc *loc, + enum kunit_assert_type type, + struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -772,13 +772,18 @@ void __printf(2, 3) kunit_log_append(char *log, const char *fmt, ...); #define KUNIT_SUCCEED(test) do {} while (0)
void kunit_do_failed_assertion(struct kunit *test, + const struct kunit_loc *loc, + enum kunit_assert_type type, struct kunit_assert *assert, const char *fmt, ...);
-#define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \ +#define KUNIT_ASSERTION(test, assert_type, pass, assert_class, INITIALIZER, fmt, ...) do { \ if (unlikely(!(pass))) { \ + static const struct kunit_loc loc = KUNIT_CURRENT_LOC; \ struct assert_class __assertion = INITIALIZER; \ kunit_do_failed_assertion(test, \ + &loc, \ + assert_type, \ &__assertion.assert, \ fmt, \ ##__VA_ARGS__); \ @@ -788,6 +793,7 @@ void kunit_do_failed_assertion(struct kunit *test,
#define KUNIT_FAIL_ASSERTION(test, assert_type, fmt, ...) \ KUNIT_ASSERTION(test, \ + assert_type, \ false, \ kunit_fail_assert, \ KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \ @@ -818,6 +824,7 @@ void kunit_do_failed_assertion(struct kunit *test, fmt, \ ...) \ KUNIT_ASSERTION(test, \ + assert_type, \ !!(condition) == !!expected_true, \ kunit_unary_assert, \ KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \ @@ -876,6 +883,7 @@ do { \ typeof(right) __right = (right); \ \ KUNIT_ASSERTION(test, \ + assert_type, \ __left op __right, \ assert_class, \ ASSERT_CLASS_INIT(assert_type, \ @@ -1230,6 +1238,7 @@ do { \ const char *__right = (right); \ \ KUNIT_ASSERTION(test, \ + assert_type, \ strcmp(__left, __right) op 0, \ kunit_binary_str_assert, \ KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \ @@ -1289,6 +1298,7 @@ do { \ typeof(ptr) __ptr = (ptr); \ \ KUNIT_ASSERTION(test, \ + assert_type, \ !IS_ERR_OR_NULL(__ptr), \ kunit_ptr_not_err_assert, \ KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \ diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c index 4d9a1295efc7..9f4492a8e24e 100644 --- a/lib/kunit/assert.c +++ b/lib/kunit/assert.c @@ -10,12 +10,13 @@
#include "string-stream.h"
-void kunit_base_assert_format(const struct kunit_assert *assert, +void kunit_assert_prologue(const struct kunit_loc *loc, + enum kunit_assert_type type, struct string_stream *stream) { const char *expect_or_assert = NULL;
- switch (assert->type) { + switch (type) { case KUNIT_EXPECTATION: expect_or_assert = "EXPECTATION"; break; @@ -25,9 +26,9 @@ void kunit_base_assert_format(const struct kunit_assert *assert, }
string_stream_add(stream, "%s FAILED at %s:%d\n", - expect_or_assert, assert->file, assert->line); + expect_or_assert, loc->file, loc->line); } -EXPORT_SYMBOL_GPL(kunit_base_assert_format); +EXPORT_SYMBOL_GPL(kunit_assert_prologue);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 345a9dd88c27..7dec3248562f 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -240,7 +240,8 @@ static void kunit_print_string_stream(struct kunit *test, } }
-static void kunit_fail(struct kunit *test, struct kunit_assert *assert) +static void kunit_fail(struct kunit *test, const struct kunit_loc *loc, + enum kunit_assert_type type, struct kunit_assert *assert) { struct string_stream *stream;
@@ -250,12 +251,12 @@ static void kunit_fail(struct kunit *test, struct kunit_assert *assert) if (!stream) { WARN(true, "Could not allocate stream to print failed assertion in %s:%d\n", - assert->file, - assert->line); + loc->file, + loc->line); return; }
- kunit_base_assert_format(assert, stream); + kunit_assert_prologue(loc, type, stream); assert->format(assert, stream);
kunit_print_string_stream(test, stream); @@ -277,6 +278,8 @@ static void __noreturn kunit_abort(struct kunit *test) }
void kunit_do_failed_assertion(struct kunit *test, + const struct kunit_loc *loc, + enum kunit_assert_type type, struct kunit_assert *assert, const char *fmt, ...) { @@ -286,11 +289,11 @@ void kunit_do_failed_assertion(struct kunit *test, assert->message.fmt = fmt; assert->message.va = &args;
- kunit_fail(test, assert); + kunit_fail(test, loc, type, assert);
va_end(args);
- if (assert->type == KUNIT_ASSERTION) + if (type == KUNIT_ASSERTION) kunit_abort(test); } EXPORT_SYMBOL_GPL(kunit_do_failed_assertion);
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com
One question below, but other than that,
Reviewed-by: Brendan Higgins brendanhiggins@google.com
include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/**
- struct kunit_loc - Identifies the source location of a line of code.
- @line: the line number in the file.
- @file: the file name.
- */
+struct kunit_loc {
int line;
const char *file;
+};
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
/**
- struct kunit_assert - Data for printing a failed assertion or expectation.
- @type: the type (either an expectation or an assertion) of this kunit_assert.
- @line: the source code line number that the expectation/assertion is at.
- @file: the file path of the source file that the expectation/assertion is in.
- @message: an optional message to provide additional context.
- @format: a function which formats the data in this kunit_assert to a string.
@@ -40,9 +50,7 @@ enum kunit_assert_type {
- format a string to a user reporting the failure.
*/ struct kunit_assert {
enum kunit_assert_type type;
int line;
const char *file;
enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this
Can you provide some context for this?
struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \
}
-void kunit_base_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h
On Tue, Jan 11, 2022 at 1:35 PM Brendan Higgins brendanhiggins@google.com wrote:
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com
One question below, but other than that,
Reviewed-by: Brendan Higgins brendanhiggins@google.com
include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/**
- struct kunit_loc - Identifies the source location of a line of code.
- @line: the line number in the file.
- @file: the file name.
- */
+struct kunit_loc {
int line;
const char *file;
+};
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
/**
- struct kunit_assert - Data for printing a failed assertion or expectation.
- @type: the type (either an expectation or an assertion) of this kunit_assert.
- @line: the source code line number that the expectation/assertion is at.
- @file: the file path of the source file that the expectation/assertion is in.
- @message: an optional message to provide additional context.
- @format: a function which formats the data in this kunit_assert to a string.
@@ -40,9 +50,7 @@ enum kunit_assert_type {
- format a string to a user reporting the failure.
*/ struct kunit_assert {
enum kunit_assert_type type;
int line;
const char *file;
enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this
Can you provide some context for this?
This is what the commit desc is referring to. We leave in the type field in this change so we can clean that up and all the macros all at once.
This TODO is addressed and removed in the next commit, so I was being a bit lazy with the TODO. I was hoping people could check `git blame` and find the context they need, if people do somehow find their way to this intermediate commit.
If you want, I can update the TODO message to be more fleshed out. Something like
TODO(...): delete this unused field when we've updated all the related KUNIT_INIT_ASSERT macros.
?
struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \
}
-void kunit_base_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h
On Tue, Jan 11, 2022 at 4:41 PM Daniel Latypov dlatypov@google.com wrote:
On Tue, Jan 11, 2022 at 1:35 PM Brendan Higgins brendanhiggins@google.com wrote:
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com
One question below, but other than that,
Reviewed-by: Brendan Higgins brendanhiggins@google.com
include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/**
- struct kunit_loc - Identifies the source location of a line of code.
- @line: the line number in the file.
- @file: the file name.
- */
+struct kunit_loc {
int line;
const char *file;
+};
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
/**
- struct kunit_assert - Data for printing a failed assertion or expectation.
- @type: the type (either an expectation or an assertion) of this kunit_assert.
- @line: the source code line number that the expectation/assertion is at.
- @file: the file path of the source file that the expectation/assertion is in.
- @message: an optional message to provide additional context.
- @format: a function which formats the data in this kunit_assert to a string.
@@ -40,9 +50,7 @@ enum kunit_assert_type {
- format a string to a user reporting the failure.
*/ struct kunit_assert {
enum kunit_assert_type type;
int line;
const char *file;
enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this
Can you provide some context for this?
This is what the commit desc is referring to. We leave in the type field in this change so we can clean that up and all the macros all at once.
This TODO is addressed and removed in the next commit, so I was being a bit lazy with the TODO. I was hoping people could check `git blame` and find the context they need, if people do somehow find their way to this intermediate commit.
If you want, I can update the TODO message to be more fleshed out. Something like
TODO(...): delete this unused field when we've updated all the related KUNIT_INIT_ASSERT macros.
?
Yeah, I like that better.
struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \
}
-void kunit_base_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h
On Tue, Jan 11, 2022 at 1:43 PM Brendan Higgins brendanhiggins@google.com wrote:
On Tue, Jan 11, 2022 at 4:41 PM Daniel Latypov dlatypov@google.com wrote:
On Tue, Jan 11, 2022 at 1:35 PM Brendan Higgins brendanhiggins@google.com wrote:
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com
One question below, but other than that,
Reviewed-by: Brendan Higgins brendanhiggins@google.com
include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/**
- struct kunit_loc - Identifies the source location of a line of code.
- @line: the line number in the file.
- @file: the file name.
- */
+struct kunit_loc {
int line;
const char *file;
+};
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
/**
- struct kunit_assert - Data for printing a failed assertion or expectation.
- @type: the type (either an expectation or an assertion) of this kunit_assert.
- @line: the source code line number that the expectation/assertion is at.
- @file: the file path of the source file that the expectation/assertion is in.
- @message: an optional message to provide additional context.
- @format: a function which formats the data in this kunit_assert to a string.
@@ -40,9 +50,7 @@ enum kunit_assert_type {
- format a string to a user reporting the failure.
*/ struct kunit_assert {
enum kunit_assert_type type;
int line;
const char *file;
enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this
Can you provide some context for this?
This is what the commit desc is referring to. We leave in the type field in this change so we can clean that up and all the macros all at once.
This TODO is addressed and removed in the next commit, so I was being a bit lazy with the TODO. I was hoping people could check `git blame` and find the context they need, if people do somehow find their way to this intermediate commit.
If you want, I can update the TODO message to be more fleshed out. Something like
TODO(...): delete this unused field when we've updated all the related KUNIT_INIT_ASSERT macros.
?
Yeah, I like that better.
Ack, applied this locally so now it looks like
struct kunit_assert { + // TODO(dlatypov@google.com): delete this unused field when we've + // updated all the related KUNIT_INIT_ASSERT* macros. enum kunit_assert_type type; - int line; - const char *file; struct va_format message;
I'll wait to see if there's any other tweaks we want before sending out a v3.
struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \
}
-void kunit_base_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h
On Tue, Jan 11, 2022 at 4:35 PM Brendan Higgins brendanhiggins@google.com wrote:
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This is per Linus's suggestion in [1].
The issue there is that every KUNIT_EXPECT/KUNIT_ASSERT puts a kunit_assert object onto the stack. Normally we rely on compilers to elide this, but when that doesn't work out, this blows up the stack usage of kunit test functions.
We can move some data off the stack by making it static. This change introduces a new `struct kunit_loc` to hold the file and line number and then just passing assert_type (EXPECT or ASSERT) as an argument.
In [1], it was suggested to also move out the format string as well, but users could theoretically craft a format string at runtime, so we can't.
This change leaves a copy of `assert_type` in kunit_assert for now because cleaning up all the macros to not pass it around is a bit more involved.
Here's an example of the expanded code for KUNIT_FAIL(): if (__builtin_expect(!!(!(false)), 0)) { static const struct kunit_loc loc = { .file = ... }; struct kunit_fail_assert __assertion = { .assert = { .type ... }; kunit_do_failed_assertion(test, &loc, KUNIT_EXPECTATION, &__assertion.assert, ...); };
[1] https://groups.google.com/g/kunit-dev/c/i3fZXgvBrfA/m/VULQg1z6BAAJ
Signed-off-by: Daniel Latypov dlatypov@google.com Suggested-by: Linus Torvalds torvalds@linux-foundation.org Reviewed-by: David Gow davidgow@google.com
One question below, but other than that,
Reviewed-by: Brendan Higgins brendanhiggins@google.com
include/kunit/assert.h | 25 ++++++++++++++++--------- include/kunit/test.h | 12 +++++++++++- lib/kunit/assert.c | 9 +++++---- lib/kunit/test.c | 15 +++++++++------ 4 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 3da6c792496c..4f91dbdb886a 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -28,11 +28,21 @@ enum kunit_assert_type { KUNIT_EXPECTATION, };
+/**
- struct kunit_loc - Identifies the source location of a line of code.
- @line: the line number in the file.
- @file: the file name.
- */
+struct kunit_loc {
int line;
const char *file;
+};
+#define KUNIT_CURRENT_LOC { .file = __FILE__, .line = __LINE__ }
/**
- struct kunit_assert - Data for printing a failed assertion or expectation.
- @type: the type (either an expectation or an assertion) of this kunit_assert.
- @line: the source code line number that the expectation/assertion is at.
- @file: the file path of the source file that the expectation/assertion is in.
- @message: an optional message to provide additional context.
- @format: a function which formats the data in this kunit_assert to a string.
@@ -40,9 +50,7 @@ enum kunit_assert_type {
- format a string to a user reporting the failure.
*/ struct kunit_assert {
enum kunit_assert_type type;
int line;
const char *file;
enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this
Can you provide some context for this?
NVM, I saw the context in the next patch. Could you maybe mention that it is removed in a later patch in the comment?
struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream);
@@ -65,14 +73,13 @@ struct kunit_assert { */ #define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ .type = assert_type, \
.file = __FILE__, \
.line = __LINE__, \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \
}
-void kunit_base_assert_format(const struct kunit_assert *assert,
struct string_stream *stream);
+void kunit_assert_prologue(const struct kunit_loc *loc,
enum kunit_assert_type type,
struct string_stream *stream);
void kunit_assert_print_msg(const struct kunit_assert *assert, struct string_stream *stream); diff --git a/include/kunit/test.h b/include/kunit/test.h index 25ea3bce6663..7b752175e614 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h
This field has been split out from kunit_assert to make the struct less heavy along with the filename and line number.
This change drops the assert_type field and cleans up all the macros that were plumbing assert_type into kunit_assert.
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: David Gow davidgow@google.com --- include/kunit/assert.h | 46 +++++++++++++----------------------------- include/kunit/test.h | 14 +++++-------- 2 files changed, 19 insertions(+), 41 deletions(-)
diff --git a/include/kunit/assert.h b/include/kunit/assert.h index 4f91dbdb886a..21299232c120 100644 --- a/include/kunit/assert.h +++ b/include/kunit/assert.h @@ -42,7 +42,6 @@ struct kunit_loc {
/** * struct kunit_assert - Data for printing a failed assertion or expectation. - * @type: the type (either an expectation or an assertion) of this kunit_assert. * @message: an optional message to provide additional context. * @format: a function which formats the data in this kunit_assert to a string. * @@ -50,7 +49,6 @@ struct kunit_loc { * format a string to a user reporting the failure. */ struct kunit_assert { - enum kunit_assert_type type; // TODO(dlatypov@google.com): delete this struct va_format message; void (*format)(const struct kunit_assert *assert, struct string_stream *stream); @@ -66,13 +64,11 @@ struct kunit_assert {
/** * KUNIT_INIT_ASSERT_STRUCT() - Initializer for a &struct kunit_assert. - * @assert_type: The type (assertion or expectation) of this kunit_assert. * @fmt: The formatting function which builds a string out of this kunit_assert. * * The base initializer for a &struct kunit_assert. */ -#define KUNIT_INIT_ASSERT_STRUCT(assert_type, fmt) { \ - .type = assert_type, \ +#define KUNIT_INIT_ASSERT_STRUCT(fmt) { \ .message = KUNIT_INIT_VA_FMT_NULL, \ .format = fmt \ } @@ -98,15 +94,13 @@ void kunit_fail_assert_format(const struct kunit_assert *assert, struct string_stream *stream);
/** - * KUNIT_INIT_FAIL_ASSERT_STRUCT() - Initializer for &struct kunit_fail_assert. - * @type: The type (assertion or expectation) of this kunit_assert. + * KUNIT_INIT_FAIL_ASSERT_STRUCT - Initializer for &struct kunit_fail_assert. * * Initializes a &struct kunit_fail_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_FAIL_ASSERT_STRUCT(type) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_fail_assert_format) \ +#define KUNIT_INIT_FAIL_ASSERT_STRUCT { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_fail_assert_format) \ }
/** @@ -130,16 +124,14 @@ void kunit_unary_assert_format(const struct kunit_assert *assert,
/** * KUNIT_INIT_UNARY_ASSERT_STRUCT() - Initializes &struct kunit_unary_assert. - * @type: The type (assertion or expectation) of this kunit_assert. * @cond: A string representation of the expression asserted true or false. * @expect_true: True if of type KUNIT_{EXPECT|ASSERT}_TRUE, false otherwise. * * Initializes a &struct kunit_unary_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_UNARY_ASSERT_STRUCT(type, cond, expect_true) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_unary_assert_format), \ +#define KUNIT_INIT_UNARY_ASSERT_STRUCT(cond, expect_true) { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_unary_assert_format), \ .condition = cond, \ .expected_true = expect_true \ } @@ -166,16 +158,14 @@ void kunit_ptr_not_err_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_PTR_NOT_ERR_ASSERT_STRUCT() - Initializes a * &struct kunit_ptr_not_err_assert. - * @type: The type (assertion or expectation) of this kunit_assert. * @txt: A string representation of the expression passed to the expectation. * @val: The actual evaluated pointer value of the expression. * * Initializes a &struct kunit_ptr_not_err_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(type, txt, val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_ptr_not_err_assert_format), \ +#define KUNIT_INIT_PTR_NOT_ERR_STRUCT(txt, val) { \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_ptr_not_err_assert_format), \ .text = txt, \ .value = val \ } @@ -209,7 +199,6 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_BINARY_ASSERT_STRUCT() - Initializes a * &struct kunit_binary_assert. - * @type: The type (assertion or expectation) of this kunit_assert. * @op_str: A string representation of the comparison operator (e.g. "=="). * @left_str: A string representation of the expression in the left slot. * @left_val: The actual evaluated value of the expression in the left slot. @@ -219,14 +208,12 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_ASSERT_STRUCT(type, \ - op_str, \ +#define KUNIT_INIT_BINARY_ASSERT_STRUCT(op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_binary_assert_format), \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_assert_format), \ .operation = op_str, \ .left_text = left_str, \ .left_value = left_val, \ @@ -273,14 +260,12 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_ptr_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(type, \ - op_str, \ +#define KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT(op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_binary_ptr_assert_format), \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_ptr_assert_format), \ .operation = op_str, \ .left_text = left_str, \ .left_value = left_val, \ @@ -317,7 +302,6 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert, /** * KUNIT_INIT_BINARY_STR_ASSERT_STRUCT() - Initializes a * &struct kunit_binary_str_assert. - * @type: The type (assertion or expectation) of this kunit_assert. * @op_str: A string representation of the comparison operator (e.g. "=="). * @left_str: A string representation of the expression in the left slot. * @left_val: The actual evaluated value of the expression in the left slot. @@ -327,14 +311,12 @@ void kunit_binary_str_assert_format(const struct kunit_assert *assert, * Initializes a &struct kunit_binary_str_assert. Intended to be used in * KUNIT_EXPECT_* and KUNIT_ASSERT_* macros. */ -#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(type, \ - op_str, \ +#define KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(op_str, \ left_str, \ left_val, \ right_str, \ right_val) { \ - .assert = KUNIT_INIT_ASSERT_STRUCT(type, \ - kunit_binary_str_assert_format), \ + .assert = KUNIT_INIT_ASSERT_STRUCT(kunit_binary_str_assert_format), \ .operation = op_str, \ .left_text = left_str, \ .left_value = left_val, \ diff --git a/include/kunit/test.h b/include/kunit/test.h index 7b752175e614..5964af750d93 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -796,7 +796,7 @@ void kunit_do_failed_assertion(struct kunit *test, assert_type, \ false, \ kunit_fail_assert, \ - KUNIT_INIT_FAIL_ASSERT_STRUCT(assert_type), \ + KUNIT_INIT_FAIL_ASSERT_STRUCT, \ fmt, \ ##__VA_ARGS__)
@@ -827,8 +827,7 @@ void kunit_do_failed_assertion(struct kunit *test, assert_type, \ !!(condition) == !!expected_true, \ kunit_unary_assert, \ - KUNIT_INIT_UNARY_ASSERT_STRUCT(assert_type, \ - #condition, \ + KUNIT_INIT_UNARY_ASSERT_STRUCT(#condition, \ expected_true), \ fmt, \ ##__VA_ARGS__) @@ -886,8 +885,7 @@ do { \ assert_type, \ __left op __right, \ assert_class, \ - ASSERT_CLASS_INIT(assert_type, \ - #op, \ + ASSERT_CLASS_INIT(#op, \ #left, \ __left, \ #right, \ @@ -1241,8 +1239,7 @@ do { \ assert_type, \ strcmp(__left, __right) op 0, \ kunit_binary_str_assert, \ - KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(assert_type, \ - #op, \ + KUNIT_INIT_BINARY_STR_ASSERT_STRUCT(#op, \ #left, \ __left, \ #right, \ @@ -1301,8 +1298,7 @@ do { \ assert_type, \ !IS_ERR_OR_NULL(__ptr), \ kunit_ptr_not_err_assert, \ - KUNIT_INIT_PTR_NOT_ERR_STRUCT(assert_type, \ - #ptr, \ + KUNIT_INIT_PTR_NOT_ERR_STRUCT(#ptr, \ __ptr), \ fmt, \ ##__VA_ARGS__); \
On Tue, Jan 11, 2022 at 2:42 PM Daniel Latypov dlatypov@google.com wrote:
This field has been split out from kunit_assert to make the struct less heavy along with the filename and line number.
This change drops the assert_type field and cleans up all the macros that were plumbing assert_type into kunit_assert.
Signed-off-by: Daniel Latypov dlatypov@google.com Reviewed-by: David Gow davidgow@google.com
Reviewed-by: Brendan Higgins brendanhiggins@google.com
linux-kselftest-mirror@lists.linaro.org