Skip to content

Commit

Permalink
kunit: split out part of kunit_assert into a static const
Browse files Browse the repository at this point in the history
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 <[email protected]>
Suggested-by: Linus Torvalds <[email protected]>
Reviewed-by: David Gow <[email protected]>
Reviewed-by: Brendan Higgins <[email protected]>
Signed-off-by: Shuah Khan <[email protected]>
  • Loading branch information
dlatypov authored and shuahkh committed Jan 25, 2022
1 parent dd640d7 commit 21957f9
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 19 deletions.
25 changes: 17 additions & 8 deletions include/kunit/assert.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,31 @@ 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.
*
* Represents a failed expectation/assertion. Contains all the data necessary to
* format a string to a user reporting the failure.
*/
struct kunit_assert {
// TODO([email protected]): 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;
void (*format)(const struct kunit_assert *assert,
struct string_stream *stream);
Expand All @@ -65,14 +75,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);
Expand Down
12 changes: 11 additions & 1 deletion include/kunit/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__); \
Expand All @@ -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), \
Expand Down Expand Up @@ -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, \
Expand Down Expand Up @@ -876,6 +883,7 @@ do { \
typeof(right) __right = (right); \
\
KUNIT_ASSERTION(test, \
assert_type, \
__left op __right, \
assert_class, \
ASSERT_CLASS_INIT(assert_type, \
Expand Down Expand Up @@ -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, \
Expand Down Expand Up @@ -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, \
Expand Down
9 changes: 5 additions & 4 deletions lib/kunit/assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down
15 changes: 9 additions & 6 deletions lib/kunit/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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, ...)
{
Expand All @@ -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);
Expand Down

0 comments on commit 21957f9

Please sign in to comment.