Files
linux-stable-mirror/lib/kunit/backtrace-suppression-test.c
Guenter Roeck bbc960d009 kunit: Add backtrace suppression self-tests
Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
  and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
  sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Link: https://lore.kernel.org/r/20260514-kunit_add_support-v11-2-b36a530a6d8f@redhat.com
Tested-by: Linux Kernel Functional Testing <lkft@linaro.org>
Acked-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Alessandro Carminati <acarmina@redhat.com>
Reviewed-by: David Gow <david@davidgow.net>
Signed-off-by: Albert Esteve <aesteve@redhat.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
2026-05-14 10:50:00 -06:00

199 lines
5.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* KUnit test for suppressing warning tracebacks.
*
* Copyright (C) 2024, Guenter Roeck
* Author: Guenter Roeck <linux@roeck-us.net>
*/
#include <kunit/test.h>
#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/kthread.h>
static void backtrace_suppression_test_warn_direct(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
WARN(1, "This backtrace should be suppressed");
/*
* Count must be checked inside the scope; the handle
* is not accessible after the block exits.
*/
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
}
static noinline void trigger_backtrace_warn(void)
{
WARN(1, "This backtrace should be suppressed");
}
static void backtrace_suppression_test_warn_indirect(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
trigger_backtrace_warn();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static void backtrace_suppression_test_warn_multi(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
WARN(1, "This backtrace should be suppressed");
trigger_backtrace_warn();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
}
}
static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
kunit_warning_suppress(test) {
WARN_ON(1);
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static noinline void trigger_backtrace_warn_on(void)
{
WARN_ON(1);
}
static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
kunit_warning_suppress(test) {
trigger_backtrace_warn_on();
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
}
}
static void backtrace_suppression_test_count(struct kunit *test)
{
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
kunit_warning_suppress(test) {
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
WARN(1, "suppressed");
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
WARN(1, "suppressed again");
KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
}
}
static void backtrace_suppression_test_active_state(struct kunit *test)
{
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
kunit_warning_suppress(test) {
KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
kunit_warning_suppress(test) {
KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
}
KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
}
static void backtrace_suppression_test_multi_scope(struct kunit *test)
{
struct kunit_suppressed_warning *sw1, *sw2;
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
sw1 = kunit_start_suppress_warning(test);
trigger_backtrace_warn_on();
WARN(1, "suppressed by sw1");
kunit_end_suppress_warning(test, sw1);
sw2 = kunit_start_suppress_warning(test);
WARN(1, "suppressed by sw2");
kunit_end_suppress_warning(test, sw2);
KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw1), 2);
KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw2), 1);
}
struct cross_kthread_data {
bool was_active;
struct completion done;
};
static int cross_kthread_fn(void *data)
{
struct cross_kthread_data *d = data;
d->was_active = kunit_has_active_suppress_warning();
complete(&d->done);
while (!kthread_should_stop())
schedule();
return 0;
}
static void backtrace_suppression_test_cross_kthread(struct kunit *test)
{
struct cross_kthread_data data;
struct task_struct *task;
data.was_active = false;
init_completion(&data.done);
kunit_warning_suppress(test) {
task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test");
KUNIT_ASSERT_FALSE(test, IS_ERR(task));
wait_for_completion(&data.done);
kthread_stop(task);
}
KUNIT_EXPECT_FALSE(test, data.was_active);
}
static struct kunit_case backtrace_suppression_test_cases[] = {
KUNIT_CASE(backtrace_suppression_test_warn_direct),
KUNIT_CASE(backtrace_suppression_test_warn_indirect),
KUNIT_CASE(backtrace_suppression_test_warn_multi),
KUNIT_CASE(backtrace_suppression_test_warn_on_direct),
KUNIT_CASE(backtrace_suppression_test_warn_on_indirect),
KUNIT_CASE(backtrace_suppression_test_count),
KUNIT_CASE(backtrace_suppression_test_active_state),
KUNIT_CASE(backtrace_suppression_test_multi_scope),
KUNIT_CASE(backtrace_suppression_test_cross_kthread),
{}
};
static struct kunit_suite backtrace_suppression_test_suite = {
.name = "backtrace-suppression-test",
.test_cases = backtrace_suppression_test_cases,
};
kunit_test_suites(&backtrace_suppression_test_suite);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KUnit test to verify warning backtrace suppression");