blob: 81696fa0000aa3bd437007478f6477fc089f6e6c [file]
// SPDX-License-Identifier: GPL-2.0
/*
* KUnit tests for kallsyms lineinfo (CONFIG_KALLSYMS_LINEINFO).
*
* Copyright (c) 2026 Sasha Levin <sashal@kernel.org>
*
* Verifies that sprint_symbol() and related APIs append correct
* " (file.c:NNN)" annotations to kernel symbol lookups.
*
* Build with: CONFIG_LINEINFO_KUNIT_TEST=m (or =y)
* Run with: ./tools/testing/kunit/kunit.py run lineinfo
*/
#include <kunit/test.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/mod_lineinfo.h>
/* --------------- helpers --------------- */
static char *alloc_sym_buf(struct kunit *test)
{
return kunit_kzalloc(test, KSYM_SYMBOL_LEN, GFP_KERNEL);
}
/*
* Return true if @buf contains a lineinfo annotation matching
* the pattern " (<path>:<digits>)".
*
* The path may be a full path like "lib/tests/lineinfo_kunit.c" or
* a shortened form from module lineinfo (e.g., just a directory name).
*/
static bool has_lineinfo(const char *buf)
{
const char *p, *colon, *end;
p = strstr(buf, " (");
if (!p)
return false;
p += 2; /* skip " (" */
colon = strchr(p, ':');
if (!colon || colon == p)
return false;
/* After colon: one or more digits then ')' */
end = colon + 1;
if (*end < '0' || *end > '9')
return false;
while (*end >= '0' && *end <= '9')
end++;
return *end == ')';
}
/*
* Extract line number from a lineinfo annotation.
* Returns 0 if not found.
*/
static unsigned int extract_line(const char *buf)
{
const char *p, *colon;
unsigned int line = 0;
p = strstr(buf, " (");
if (!p)
return 0;
colon = strchr(p + 2, ':');
if (!colon)
return 0;
colon++;
while (*colon >= '0' && *colon <= '9') {
line = line * 10 + (*colon - '0');
colon++;
}
return line;
}
/*
* Check if the lineinfo annotation contains the given filename substring.
*/
static bool lineinfo_contains_file(const char *buf, const char *name)
{
const char *p, *colon;
p = strstr(buf, " (");
if (!p)
return false;
colon = strchr(p + 2, ':');
if (!colon)
return false;
/* Search for @name between '(' and ':' */
return strnstr(p + 1, name, colon - p - 1) != NULL;
}
/* --------------- target functions --------------- */
static noinline int lineinfo_target_normal(void)
{
barrier();
return 42;
}
static noinline int lineinfo_target_short(void)
{
barrier();
return 1;
}
static noinline int lineinfo_target_with_arg(int x)
{
barrier();
return x + 1;
}
static noinline int lineinfo_target_many_lines(void)
{
int a = 0;
barrier();
a += 1;
a += 2;
a += 3;
a += 4;
a += 5;
a += 6;
a += 7;
a += 8;
a += 9;
a += 10;
barrier();
return a;
}
static __always_inline int lineinfo_inline_helper(void)
{
return 99;
}
static noinline int lineinfo_inline_caller(void)
{
barrier();
return lineinfo_inline_helper();
}
/* 10-deep call chain */
static noinline int lineinfo_chain_10(void) { barrier(); return 10; }
static noinline int lineinfo_chain_9(void) { barrier(); return lineinfo_chain_10(); }
static noinline int lineinfo_chain_8(void) { barrier(); return lineinfo_chain_9(); }
static noinline int lineinfo_chain_7(void) { barrier(); return lineinfo_chain_8(); }
static noinline int lineinfo_chain_6(void) { barrier(); return lineinfo_chain_7(); }
static noinline int lineinfo_chain_5(void) { barrier(); return lineinfo_chain_6(); }
static noinline int lineinfo_chain_4(void) { barrier(); return lineinfo_chain_5(); }
static noinline int lineinfo_chain_3(void) { barrier(); return lineinfo_chain_4(); }
static noinline int lineinfo_chain_2(void) { barrier(); return lineinfo_chain_3(); }
static noinline int lineinfo_chain_1(void) { barrier(); return lineinfo_chain_2(); }
/* --------------- Group A: Basic lineinfo presence --------------- */
static void test_normal_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test,
lineinfo_contains_file(buf, "lineinfo_kunit.c"),
"Wrong file in: %s", buf);
}
static void test_static_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_short;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in: %s", buf);
}
static void test_noinline_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_with_arg;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in: %s", buf);
}
static void test_inline_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_inline_caller;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo for inline caller in: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test,
lineinfo_contains_file(buf, "lineinfo_kunit.c"),
"Wrong file in: %s", buf);
}
static void test_short_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_short;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo for short function in: %s", buf);
}
static void test_many_lines_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_many_lines;
unsigned int line;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in: %s", buf);
line = extract_line(buf);
KUNIT_EXPECT_GT_MSG(test, line, (unsigned int)0,
"Line number should be > 0 in: %s", buf);
}
/* --------------- Group B: Deep call chain --------------- */
typedef int (*chain_fn_t)(void);
static void test_deep_call_chain(struct kunit *test)
{
static const chain_fn_t chain_fns[] = {
lineinfo_chain_1, lineinfo_chain_2,
lineinfo_chain_3, lineinfo_chain_4,
lineinfo_chain_5, lineinfo_chain_6,
lineinfo_chain_7, lineinfo_chain_8,
lineinfo_chain_9, lineinfo_chain_10,
};
char *buf = alloc_sym_buf(test);
int i, found = 0;
/* Call chain to prevent dead-code elimination */
KUNIT_ASSERT_EQ(test, lineinfo_chain_1(), 10);
for (i = 0; i < ARRAY_SIZE(chain_fns); i++) {
unsigned long addr = (unsigned long)chain_fns[i];
sprint_symbol(buf, addr);
if (has_lineinfo(buf))
found++;
}
/*
* Not every tiny function gets DWARF line info (compiler may
* omit it for very small stubs), but at least some should.
*/
KUNIT_EXPECT_GT_MSG(test, found, 0,
"None of the 10 chain functions had lineinfo");
}
/* --------------- Group C: sprint_symbol API variants --------------- */
static void test_sprint_symbol_format(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_symbol(buf, addr);
/* Should contain +0x and /0x for offset/size */
KUNIT_EXPECT_NOT_NULL_MSG(test, strstr(buf, "+0x"),
"Missing offset in: %s", buf);
KUNIT_EXPECT_NOT_NULL_MSG(test, strstr(buf, "/0x"),
"Missing size in: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in: %s", buf);
}
static void test_sprint_backtrace(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
/* sprint_backtrace subtracts 1 internally to handle tail calls */
sprint_backtrace(buf, addr + 1);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in backtrace: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test,
lineinfo_contains_file(buf, "lineinfo_kunit.c"),
"Wrong file in backtrace: %s", buf);
}
static void test_sprint_backtrace_build_id(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_backtrace_build_id(buf, addr + 1);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in backtrace_build_id: %s", buf);
}
static void test_sprint_symbol_no_offset(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_symbol_no_offset(buf, addr);
/* No "+0x" in output */
KUNIT_EXPECT_NULL_MSG(test, strstr(buf, "+0x"),
"Unexpected offset in no_offset: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in no_offset: %s", buf);
}
/* --------------- Group D: printk format specifiers --------------- */
static void test_pS_format(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
void *addr = lineinfo_target_normal;
snprintf(buf, KSYM_SYMBOL_LEN, "%pS", addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in %%pS: %s", buf);
}
static void test_pBb_format(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
/*
* %pBb uses sprint_backtrace_build_id which subtracts 1 from the
* address, so pass addr+1 to resolve back to the function.
*/
void *addr = (void *)((unsigned long)lineinfo_target_normal + 1);
snprintf(buf, KSYM_SYMBOL_LEN, "%pBb", addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in %%pBb: %s", buf);
}
static void test_pSR_format(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
void *addr = lineinfo_target_normal;
snprintf(buf, KSYM_SYMBOL_LEN, "%pSR", addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in %%pSR: %s", buf);
}
/* --------------- Group E: Address edge cases --------------- */
static void test_symbol_start_addr(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_symbol(buf, addr);
KUNIT_EXPECT_NOT_NULL_MSG(test, strstr(buf, "+0x0/"),
"Expected +0x0/ at function start: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo at function start: %s", buf);
}
static void test_symbol_nonzero_offset(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
/*
* sprint_backtrace subtracts 1 internally.
* Passing addr+2 resolves to addr+1 which is inside the function
* at a non-zero offset.
*/
sprint_backtrace(buf, addr + 2);
KUNIT_EXPECT_TRUE_MSG(test,
strnstr(buf, "lineinfo_target_normal",
KSYM_SYMBOL_LEN) != NULL,
"Didn't resolve to expected function: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo at non-zero offset: %s", buf);
}
static void test_unknown_address(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
sprint_symbol(buf, 1UL);
/* Should be "0x1" with no lineinfo */
KUNIT_EXPECT_NOT_NULL_MSG(test, strstr(buf, "0x1"),
"Expected hex address for bogus addr: %s", buf);
KUNIT_EXPECT_FALSE_MSG(test, has_lineinfo(buf),
"Unexpected lineinfo for bogus addr: %s", buf);
}
static void test_kernel_function_lineinfo(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)sprint_symbol;
sprint_symbol(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo for sprint_symbol: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test,
lineinfo_contains_file(buf, "kallsyms.c"),
"Expected kallsyms.c in: %s", buf);
}
static void test_assembly_no_lineinfo(struct kunit *test)
{
#if IS_BUILTIN(CONFIG_LINEINFO_KUNIT_TEST)
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)_text;
sprint_symbol(buf, addr);
/*
* _text is typically an asm entry point with no DWARF line info.
* If it has lineinfo, it's a C-based entry — skip in that case.
*/
if (has_lineinfo(buf))
kunit_skip(test, "_text has lineinfo (C entry?): %s", buf);
KUNIT_EXPECT_FALSE_MSG(test, has_lineinfo(buf),
"Unexpected lineinfo for asm symbol: %s", buf);
#else
kunit_skip(test, "_text not accessible from modules");
#endif
}
/* --------------- Group F: Module path --------------- */
static void test_module_function_lineinfo(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
if (!IS_MODULE(CONFIG_LINEINFO_KUNIT_TEST)) {
kunit_skip(test, "Test only meaningful when built as module");
return;
}
sprint_symbol(buf, addr);
KUNIT_EXPECT_NOT_NULL_MSG(test,
strstr(buf, "[lineinfo_kunit"),
"Missing module name in: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo for module function: %s", buf);
KUNIT_EXPECT_TRUE_MSG(test,
lineinfo_contains_file(buf, "lineinfo_kunit.c"),
"Wrong file for module function: %s", buf);
}
/* --------------- Group G: Stress --------------- */
struct lineinfo_stress_data {
unsigned long addr;
atomic_t failures;
};
static void lineinfo_stress_fn(void *info)
{
struct lineinfo_stress_data *data = info;
char buf[KSYM_SYMBOL_LEN];
int i;
for (i = 0; i < 100; i++) {
sprint_symbol(buf, data->addr);
if (!has_lineinfo(buf))
atomic_inc(&data->failures);
}
}
static void test_concurrent_sprint_symbol(struct kunit *test)
{
struct lineinfo_stress_data data;
data.addr = (unsigned long)lineinfo_target_normal;
atomic_set(&data.failures, 0);
on_each_cpu(lineinfo_stress_fn, &data, 1);
KUNIT_EXPECT_EQ_MSG(test, atomic_read(&data.failures), 0,
"Concurrent lineinfo failures detected");
}
static void test_rapid_sprint_symbol(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
int i, failures = 0;
for (i = 0; i < 1000; i++) {
sprint_symbol(buf, addr);
if (!has_lineinfo(buf))
failures++;
}
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Rapid sprint_symbol failures: %d/1000", failures);
}
/* --------------- Group H: Safety and plausibility --------------- */
static void test_line_number_plausible(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
unsigned int line;
sprint_symbol(buf, addr);
KUNIT_ASSERT_TRUE(test, has_lineinfo(buf));
line = extract_line(buf);
KUNIT_EXPECT_GT_MSG(test, line, (unsigned int)0,
"Line number should be > 0");
KUNIT_EXPECT_LT_MSG(test, line, (unsigned int)10000,
"Line number %u implausibly large for this file",
line);
}
static void test_buffer_no_overflow(struct kunit *test)
{
const size_t canary_size = 16;
char *buf;
int i;
buf = kunit_kzalloc(test, KSYM_SYMBOL_LEN + canary_size, GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, buf);
/* Fill canary area past KSYM_SYMBOL_LEN with 0xAA */
memset(buf + KSYM_SYMBOL_LEN, 0xAA, canary_size);
sprint_symbol(buf, (unsigned long)lineinfo_target_normal);
/* Verify canary bytes are untouched */
for (i = 0; i < canary_size; i++) {
KUNIT_EXPECT_EQ_MSG(test,
(unsigned char)buf[KSYM_SYMBOL_LEN + i],
(unsigned char)0xAA,
"Buffer overflow at offset %d past KSYM_SYMBOL_LEN",
i);
}
}
static void test_dump_stack_no_crash(struct kunit *test)
{
/* Just verify dump_stack() completes without panic */
dump_stack();
KUNIT_SUCCEED(test);
}
static void test_sprint_symbol_build_id(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
sprint_symbol_build_id(buf, addr);
KUNIT_EXPECT_TRUE_MSG(test, has_lineinfo(buf),
"No lineinfo in sprint_symbol_build_id: %s", buf);
}
static void test_sleb128_edge_cases(struct kunit *test)
{
u32 pos;
int32_t result;
/* Value 0: single byte 0x00 */
{
static const u8 data[] = { 0x00 };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)0);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value -1: single byte 0x7F */
{
static const u8 data[] = { 0x7f };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)-1);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value 1: single byte 0x01 */
{
static const u8 data[] = { 0x01 };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)1);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value -64: single byte 0x40 */
{
static const u8 data[] = { 0x40 };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)-64);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value 63: single byte 0x3F */
{
static const u8 data[] = { 0x3f };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)63);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value -128: two bytes 0x80 0x7F */
{
static const u8 data[] = { 0x80, 0x7f };
pos = 0;
result = lineinfo_read_sleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (int32_t)-128);
KUNIT_EXPECT_EQ(test, pos, (u32)2);
}
}
static void test_uleb128_edge_cases(struct kunit *test)
{
u32 pos, result;
/* Value 0: single byte 0x00 */
{
static const u8 data[] = { 0x00 };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (u32)0);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value 127: single byte 0x7F */
{
static const u8 data[] = { 0x7F };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (u32)127);
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
/* Value 128: two bytes 0x80 0x01 */
{
static const u8 data[] = { 0x80, 0x01 };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (u32)128);
KUNIT_EXPECT_EQ(test, pos, (u32)2);
}
/* Max u32 0xFFFFFFFF: 5 bytes */
{
static const u8 data[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0x0F };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, sizeof(data));
KUNIT_EXPECT_EQ(test, result, (u32)0xFFFFFFFF);
KUNIT_EXPECT_EQ(test, pos, (u32)5);
}
/* Truncated input: pos >= end returns 0 */
{
static const u8 data[] = { 0x80 };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, 0);
KUNIT_EXPECT_EQ_MSG(test, result, (u32)0,
"Expected 0 for empty input");
}
/* Truncated mid-varint: continuation byte but end reached */
{
static const u8 data[] = { 0x80 };
pos = 0;
result = lineinfo_read_uleb128(data, &pos, 1);
KUNIT_EXPECT_EQ_MSG(test, result, (u32)0,
"Expected 0 for truncated varint");
KUNIT_EXPECT_EQ(test, pos, (u32)1);
}
}
static void test_line_number_accuracy(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_normal;
unsigned int line;
sprint_symbol(buf, addr);
KUNIT_ASSERT_TRUE(test, has_lineinfo(buf));
line = extract_line(buf);
/*
* lineinfo_target_normal is defined around line 103-107.
* Allow wide range: KASAN instrumentation and module lineinfo
* address mapping can shift the reported line significantly.
*/
KUNIT_EXPECT_GE_MSG(test, line, (unsigned int)50,
"Line %u too low for lineinfo_target_normal", line);
KUNIT_EXPECT_LE_MSG(test, line, (unsigned int)300,
"Line %u too high for lineinfo_target_normal", line);
}
static void test_many_lines_mid_function(struct kunit *test)
{
char *buf = alloc_sym_buf(test);
unsigned long addr = (unsigned long)lineinfo_target_many_lines;
unsigned int line;
unsigned long mid_addr;
/* Get function size from sprint_symbol output */
sprint_symbol(buf, addr);
KUNIT_ASSERT_TRUE(test, has_lineinfo(buf));
/* Try an address 8 bytes into the function (past prologue) */
mid_addr = addr + 8;
sprint_symbol(buf, mid_addr);
/*
* Should still resolve to lineinfo_target_many_lines.
* Lineinfo should be present with a plausible line number.
*/
KUNIT_EXPECT_TRUE_MSG(test,
strnstr(buf, "lineinfo_target_many_lines",
KSYM_SYMBOL_LEN) != NULL,
"Mid-function addr resolved to wrong symbol: %s",
buf);
if (has_lineinfo(buf)) {
line = extract_line(buf);
KUNIT_EXPECT_GE_MSG(test, line, (unsigned int)50,
"Line %u too low for mid-function", line);
KUNIT_EXPECT_LE_MSG(test, line, (unsigned int)700,
"Line %u too high for mid-function", line);
}
}
/* --------------- Suite registration --------------- */
static struct kunit_case lineinfo_test_cases[] = {
/* Group A: Basic lineinfo presence */
KUNIT_CASE(test_normal_function),
KUNIT_CASE(test_static_function),
KUNIT_CASE(test_noinline_function),
KUNIT_CASE(test_inline_function),
KUNIT_CASE(test_short_function),
KUNIT_CASE(test_many_lines_function),
/* Group B: Deep call chain */
KUNIT_CASE(test_deep_call_chain),
/* Group C: sprint_symbol API variants */
KUNIT_CASE(test_sprint_symbol_format),
KUNIT_CASE(test_sprint_backtrace),
KUNIT_CASE(test_sprint_backtrace_build_id),
KUNIT_CASE(test_sprint_symbol_no_offset),
/* Group D: printk format specifiers */
KUNIT_CASE(test_pS_format),
KUNIT_CASE(test_pBb_format),
KUNIT_CASE(test_pSR_format),
/* Group E: Address edge cases */
KUNIT_CASE(test_symbol_start_addr),
KUNIT_CASE(test_symbol_nonzero_offset),
KUNIT_CASE(test_unknown_address),
KUNIT_CASE(test_kernel_function_lineinfo),
KUNIT_CASE(test_assembly_no_lineinfo),
/* Group F: Module path */
KUNIT_CASE(test_module_function_lineinfo),
/* Group G: Stress */
KUNIT_CASE_SLOW(test_concurrent_sprint_symbol),
KUNIT_CASE_SLOW(test_rapid_sprint_symbol),
/* Group H: Safety and plausibility */
KUNIT_CASE(test_line_number_plausible),
KUNIT_CASE(test_buffer_no_overflow),
KUNIT_CASE(test_dump_stack_no_crash),
KUNIT_CASE(test_sprint_symbol_build_id),
/* Group I: Encoding/decoding and accuracy */
KUNIT_CASE(test_sleb128_edge_cases),
KUNIT_CASE(test_uleb128_edge_cases),
KUNIT_CASE(test_line_number_accuracy),
KUNIT_CASE(test_many_lines_mid_function),
{}
};
static struct kunit_suite lineinfo_test_suite = {
.name = "lineinfo",
.test_cases = lineinfo_test_cases,
};
kunit_test_suites(&lineinfo_test_suite);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KUnit tests for kallsyms lineinfo");
MODULE_AUTHOR("Sasha Levin");