In combination with the structleak gcc plugin, kunit can lead to excessive stack usage when each assertion adds another structure to the stack from of the calling function:
base/test/property-entry-test.c:99:1: error: the frame size of 3032 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
As most assertions are binary, change those over to a direct function call that does not have this problem. This can probably be improved further, I just went for a straightforward conversion, but a function call with 12 fixed arguments plus varargs it not great either.
Cc: Kees Cook keescook@chromium.org Cc: Ard Biesheuvel ard.biesheuvel@linaro.org Cc: Brendan Higgins brendanhiggins@google.com Cc: Stephen Boyd sboyd@kernel.org Cc: Dmitry Torokhov dmitry.torokhov@gmail.com Cc: "Rafael J. Wysocki" rjw@rjwysocki.net Cc: linux-kselftest@vger.kernel.org Cc: kunit-dev@googlegroups.com Signed-off-by: Arnd Bergmann arnd@arndb.de --- I think improving the compiler or the plugin to not force the allocation of these structs would be better, as it avoids the whack-a-mole game of fixing up each time it hits us, but this may not be possible using the current plugin infrastructure. --- include/kunit/test.h | 155 ++++++++++++------------------------------- lib/kunit/assert.c | 8 +-- lib/kunit/test.c | 42 ++++++++++++ 3 files changed, 90 insertions(+), 115 deletions(-)
diff --git a/include/kunit/test.h b/include/kunit/test.h index dba48304b3bd..76eadd4a8b77 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -391,6 +391,16 @@ void kunit_do_assertion(struct kunit *test, bool pass, const char *fmt, ...);
+void kunit_do_binary_assertion(struct kunit *test, bool pass, + enum kunit_assert_type type, + int line, const char *file, + const char *operation, + const char *left_text, long long left_value, + const char *right_text, long long right_value, + void (*format)(const struct kunit_assert *assert, + struct string_stream *stream), + const char *fmt, ...); + #define KUNIT_ASSERTION(test, pass, assert_class, INITIALIZER, fmt, ...) do { \ struct assert_class __assertion = INITIALIZER; \ kunit_do_assertion(test, \ @@ -491,19 +501,32 @@ do { \ typeof(left) __left = (left); \ typeof(right) __right = (right); \ ((void)__typecheck(__left, __right)); \ - \ - KUNIT_ASSERTION(test, \ - __left op __right, \ - assert_class, \ - ASSERT_CLASS_INIT(test, \ - assert_type, \ - #op, \ - #left, \ - __left, \ - #right, \ - __right), \ - fmt, \ - ##__VA_ARGS__); \ + kunit_do_binary_assertion(test, left op right, assert_type, \ + __LINE__, __FILE__, #op, #left, __left, \ + #right, __right, \ + kunit_binary_assert_format, \ + fmt, ##__VA_ARGS__); \ +} while (0) + +#define KUNIT_BASE_POINTER_ASSERTION(test, \ + assert_class, \ + ASSERT_CLASS_INIT, \ + assert_type, \ + left, \ + op, \ + right, \ + fmt, \ + ...) \ +do { \ + typeof(left) __left = (left); \ + typeof(right) __right = (right); \ + ((void)__typecheck(__left, __right)); \ + kunit_do_binary_assertion(test, left op right, assert_type, \ + __LINE__, __FILE__, #op, \ + #left, (uintptr_t)__left, \ + #right, (uintptr_t)__right, \ + kunit_binary_ptr_assert_format, \ + fmt, ##__VA_ARGS__); \ } while (0)
#define KUNIT_BASE_EQ_MSG_ASSERTION(test, \ @@ -625,12 +648,11 @@ do { \ right, \ fmt, \ ...) \ - KUNIT_BASE_EQ_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ + KUNIT_BASE_POINTER_ASSERTION(test, \ + assert_class, \ + ASSERT_CLASS_INIT, \ assert_type, \ - left, \ - right, \ + left, ==, right, \ fmt, \ ##__VA_ARGS__)
@@ -664,12 +686,11 @@ do { \ right, \ fmt, \ ...) \ - KUNIT_BASE_NE_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ + KUNIT_BASE_POINTER_ASSERTION(test, \ + assert_class, \ + ASSERT_CLASS_INIT, \ assert_type, \ - left, \ - right, \ + left, !=, right, \ fmt, \ ##__VA_ARGS__)
@@ -697,28 +718,6 @@ do { \ right, \ NULL)
-#define KUNIT_BINARY_PTR_LT_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ...) \ - KUNIT_BASE_LT_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ##__VA_ARGS__) - -#define KUNIT_BINARY_PTR_LT_ASSERTION(test, assert_type, left, right) \ - KUNIT_BINARY_PTR_LT_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - NULL) - #define KUNIT_BINARY_LE_MSG_ASSERTION(test, assert_type, left, right, fmt, ...)\ KUNIT_BASE_LE_MSG_ASSERTION(test, \ kunit_binary_assert, \ @@ -736,28 +735,6 @@ do { \ right, \ NULL)
-#define KUNIT_BINARY_PTR_LE_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ...) \ - KUNIT_BASE_LE_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ##__VA_ARGS__) - -#define KUNIT_BINARY_PTR_LE_ASSERTION(test, assert_type, left, right) \ - KUNIT_BINARY_PTR_LE_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - NULL) - #define KUNIT_BINARY_GT_MSG_ASSERTION(test, assert_type, left, right, fmt, ...)\ KUNIT_BASE_GT_MSG_ASSERTION(test, \ kunit_binary_assert, \ @@ -775,28 +752,6 @@ do { \ right, \ NULL)
-#define KUNIT_BINARY_PTR_GT_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ...) \ - KUNIT_BASE_GT_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ##__VA_ARGS__) - -#define KUNIT_BINARY_PTR_GT_ASSERTION(test, assert_type, left, right) \ - KUNIT_BINARY_PTR_GT_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - NULL) - #define KUNIT_BINARY_GE_MSG_ASSERTION(test, assert_type, left, right, fmt, ...)\ KUNIT_BASE_GE_MSG_ASSERTION(test, \ kunit_binary_assert, \ @@ -814,28 +769,6 @@ do { \ right, \ NULL)
-#define KUNIT_BINARY_PTR_GE_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ...) \ - KUNIT_BASE_GE_MSG_ASSERTION(test, \ - kunit_binary_ptr_assert, \ - KUNIT_INIT_BINARY_PTR_ASSERT_STRUCT, \ - assert_type, \ - left, \ - right, \ - fmt, \ - ##__VA_ARGS__) - -#define KUNIT_BINARY_PTR_GE_ASSERTION(test, assert_type, left, right) \ - KUNIT_BINARY_PTR_GE_MSG_ASSERTION(test, \ - assert_type, \ - left, \ - right, \ - NULL) - #define KUNIT_BINARY_STR_ASSERTION(test, \ assert_type, \ left, \ diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c index 86013d4cf891..abb6c70de925 100644 --- a/lib/kunit/assert.c +++ b/lib/kunit/assert.c @@ -101,8 +101,8 @@ void kunit_binary_assert_format(const struct kunit_assert *assert, void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, struct string_stream *stream) { - struct kunit_binary_ptr_assert *binary_assert = container_of( - assert, struct kunit_binary_ptr_assert, assert); + struct kunit_binary_assert *binary_assert = container_of( + assert, struct kunit_binary_assert, assert);
kunit_base_assert_format(assert, stream); string_stream_add(stream, @@ -112,10 +112,10 @@ void kunit_binary_ptr_assert_format(const struct kunit_assert *assert, binary_assert->right_text); string_stream_add(stream, "\t\t%s == %pK\n", binary_assert->left_text, - binary_assert->left_value); + (void *)(uintptr_t)binary_assert->left_value); string_stream_add(stream, "\t\t%s == %pK", binary_assert->right_text, - binary_assert->right_value); + (void *)(uintptr_t)binary_assert->right_value); kunit_assert_print_msg(assert, stream); }
diff --git a/lib/kunit/test.c b/lib/kunit/test.c index c83c0fa59cbd..937f3fbe5ecc 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -172,6 +172,48 @@ void kunit_do_assertion(struct kunit *test, kunit_abort(test); }
+void kunit_do_binary_assertion(struct kunit *test, bool pass, + enum kunit_assert_type type, + int line, const char *file, + const char *operation, + const char *left_text, long long left_value, + const char *right_text, long long right_value, + void (*format)(const struct kunit_assert *assert, + struct string_stream *stream), + const char *fmt, ...) +{ + va_list args; + struct kunit_binary_assert assert = { + .assert = { + .test = test, + .type = type, + .file = file, + .line = line, + .message.fmt = fmt, + .format = format, + }, + .operation = operation, + .left_text = left_text, + .left_value = left_value, + .right_text = right_text, + .right_value = right_value, + }; + + if (pass) + return; + + va_start(args, fmt); + + assert.assert.message.va = &args; + + kunit_fail(test, &assert.assert); + + va_end(args); + + if (type == KUNIT_ASSERTION) + kunit_abort(test); +} + void kunit_init_test(struct kunit *test, const char *name) { spin_lock_init(&test->lock);
On Fri, Jan 10, 2020 at 5:43 AM Arnd Bergmann arnd@arndb.de wrote:
In combination with the structleak gcc plugin, kunit can lead to excessive stack usage when each assertion adds another structure to the stack from of the calling function:
base/test/property-entry-test.c:99:1: error: the frame size of 3032 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
As most assertions are binary, change those over to a direct function call that does not have this problem. This can probably be improved further, I just went for a straightforward conversion, but a function call with 12 fixed arguments plus varargs it not great either.
Yeah, I am not exactly excited by maintaining such a set of functions.
I don't think anyone wants to go with the heap allocation route.
Along the lines of the union/single copy idea[1]. What if we just put a union of all the assertion types in the kunit struct? One is already allocated for every test case and we only need one assertion object for each test case at a time, so I imagine that sould work.
I will start messing around with the idea. Still, it sounds like we are down to either reducing the number of instances of this struct that get created per test case, or we need to remove it entirely (as you have done here).
Cheers
On Mon, Jan 13, 2020 at 6:12 PM Brendan Higgins brendanhiggins@google.com wrote:
On Fri, Jan 10, 2020 at 5:43 AM Arnd Bergmann arnd@arndb.de wrote:
In combination with the structleak gcc plugin, kunit can lead to excessive stack usage when each assertion adds another structure to the stack from of the calling function:
base/test/property-entry-test.c:99:1: error: the frame size of 3032 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
As most assertions are binary, change those over to a direct function call that does not have this problem. This can probably be improved further, I just went for a straightforward conversion, but a function call with 12 fixed arguments plus varargs it not great either.
Yeah, I am not exactly excited by maintaining such a set of functions.
I don't think anyone wants to go with the heap allocation route.
Along the lines of the union/single copy idea[1]. What if we just put a union of all the assertion types in the kunit struct? One is already allocated for every test case and we only need one assertion object for each test case at a time, so I imagine that sould work.
I will start messing around with the idea. Still, it sounds like we are down to either reducing the number of instances of this struct that get created per test case, or we need to remove it entirely (as you have done here).
Cheers
Woops forgot to link the original discussion.
On Tue, Jan 14, 2020 at 3:13 AM Brendan Higgins brendanhiggins@google.com wrote:
On Fri, Jan 10, 2020 at 5:43 AM Arnd Bergmann arnd@arndb.de wrote:
In combination with the structleak gcc plugin, kunit can lead to excessive stack usage when each assertion adds another structure to the stack from of the calling function:
base/test/property-entry-test.c:99:1: error: the frame size of 3032 bytes is larger than 2048 bytes [-Werror=frame-larger-than=]
As most assertions are binary, change those over to a direct function call that does not have this problem. This can probably be improved further, I just went for a straightforward conversion, but a function call with 12 fixed arguments plus varargs it not great either.
Yeah, I am not exactly excited by maintaining such a set of functions.
Ok.
I don't think anyone wants to go with the heap allocation route.
Along the lines of the union/single copy idea[1]. What if we just put a union of all the assertion types in the kunit struct? One is already allocated for every test case and we only need one assertion object for each test case at a time, so I imagine that sould work.
Ah right, that should work fine, and may also lead to better object code if the compiler can avoid repeated assignments of the same values, e.g. ".file = __FILE__".
Arnd
linux-kselftest-mirror@lists.linaro.org