// SPDX-License-Identifier: GPL-2.0 /* * KUnit test for suppressing warning tracebacks. * * Copyright (C) 2024, Guenter Roeck * Author: Guenter Roeck */ #include #include #include #include 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");