130f3bb09SZhen Lei // SPDX-License-Identifier: GPL-2.0-or-later 230f3bb09SZhen Lei /* 330f3bb09SZhen Lei * Test the function and performance of kallsyms 430f3bb09SZhen Lei * 530f3bb09SZhen Lei * Copyright (C) Huawei Technologies Co., Ltd., 2022 630f3bb09SZhen Lei * 730f3bb09SZhen Lei * Authors: Zhen Lei <thunder.leizhen@huawei.com> Huawei 830f3bb09SZhen Lei */ 930f3bb09SZhen Lei 1030f3bb09SZhen Lei #define pr_fmt(fmt) "kallsyms_selftest: " fmt 1130f3bb09SZhen Lei 1230f3bb09SZhen Lei #include <linux/init.h> 1330f3bb09SZhen Lei #include <linux/module.h> 1430f3bb09SZhen Lei #include <linux/kallsyms.h> 1530f3bb09SZhen Lei #include <linux/random.h> 1630f3bb09SZhen Lei #include <linux/sched/clock.h> 1730f3bb09SZhen Lei #include <linux/kthread.h> 1830f3bb09SZhen Lei #include <linux/vmalloc.h> 1930f3bb09SZhen Lei 2030f3bb09SZhen Lei #include "kallsyms_internal.h" 2130f3bb09SZhen Lei #include "kallsyms_selftest.h" 2230f3bb09SZhen Lei 2330f3bb09SZhen Lei 2430f3bb09SZhen Lei #define MAX_NUM_OF_RECORDS 64 2530f3bb09SZhen Lei 2630f3bb09SZhen Lei struct test_stat { 2730f3bb09SZhen Lei int min; 2830f3bb09SZhen Lei int max; 2930f3bb09SZhen Lei int save_cnt; 3030f3bb09SZhen Lei int real_cnt; 3130f3bb09SZhen Lei int perf; 3230f3bb09SZhen Lei u64 sum; 3330f3bb09SZhen Lei char *name; 3430f3bb09SZhen Lei unsigned long addr; 3530f3bb09SZhen Lei unsigned long addrs[MAX_NUM_OF_RECORDS]; 3630f3bb09SZhen Lei }; 3730f3bb09SZhen Lei 3830f3bb09SZhen Lei struct test_item { 3930f3bb09SZhen Lei char *name; 4030f3bb09SZhen Lei unsigned long addr; 4130f3bb09SZhen Lei }; 4230f3bb09SZhen Lei 4330f3bb09SZhen Lei #define ITEM_FUNC(s) \ 4430f3bb09SZhen Lei { \ 4530f3bb09SZhen Lei .name = #s, \ 4630f3bb09SZhen Lei .addr = (unsigned long)s, \ 4730f3bb09SZhen Lei } 4830f3bb09SZhen Lei 4930f3bb09SZhen Lei #define ITEM_DATA(s) \ 5030f3bb09SZhen Lei { \ 5130f3bb09SZhen Lei .name = #s, \ 5230f3bb09SZhen Lei .addr = (unsigned long)&s, \ 5330f3bb09SZhen Lei } 5430f3bb09SZhen Lei 5530f3bb09SZhen Lei 5630f3bb09SZhen Lei static int kallsyms_test_var_bss_static; 5730f3bb09SZhen Lei static int kallsyms_test_var_data_static = 1; 5830f3bb09SZhen Lei int kallsyms_test_var_bss; 5930f3bb09SZhen Lei int kallsyms_test_var_data = 1; 6030f3bb09SZhen Lei 6130f3bb09SZhen Lei static int kallsyms_test_func_static(void) 6230f3bb09SZhen Lei { 6330f3bb09SZhen Lei kallsyms_test_var_bss_static++; 6430f3bb09SZhen Lei kallsyms_test_var_data_static++; 6530f3bb09SZhen Lei 6630f3bb09SZhen Lei return 0; 6730f3bb09SZhen Lei } 6830f3bb09SZhen Lei 6930f3bb09SZhen Lei int kallsyms_test_func(void) 7030f3bb09SZhen Lei { 7130f3bb09SZhen Lei return kallsyms_test_func_static(); 7230f3bb09SZhen Lei } 7330f3bb09SZhen Lei 7430f3bb09SZhen Lei __weak int kallsyms_test_func_weak(void) 7530f3bb09SZhen Lei { 7630f3bb09SZhen Lei kallsyms_test_var_bss++; 7730f3bb09SZhen Lei kallsyms_test_var_data++; 7830f3bb09SZhen Lei return 0; 7930f3bb09SZhen Lei } 8030f3bb09SZhen Lei 8130f3bb09SZhen Lei static struct test_item test_items[] = { 8230f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func_static), 8330f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func), 8430f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func_weak), 8530f3bb09SZhen Lei ITEM_FUNC(vmalloc), 8630f3bb09SZhen Lei ITEM_FUNC(vfree), 8730f3bb09SZhen Lei #ifdef CONFIG_KALLSYMS_ALL 8830f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_bss_static), 8930f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_data_static), 9030f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_bss), 9130f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_data), 9230f3bb09SZhen Lei ITEM_DATA(vmap_area_list), 9330f3bb09SZhen Lei #endif 9430f3bb09SZhen Lei }; 9530f3bb09SZhen Lei 9630f3bb09SZhen Lei static char stub_name[KSYM_NAME_LEN]; 9730f3bb09SZhen Lei 9830f3bb09SZhen Lei static int stat_symbol_len(void *data, const char *name, struct module *mod, unsigned long addr) 9930f3bb09SZhen Lei { 10030f3bb09SZhen Lei *(u32 *)data += strlen(name); 10130f3bb09SZhen Lei 10230f3bb09SZhen Lei return 0; 10330f3bb09SZhen Lei } 10430f3bb09SZhen Lei 10530f3bb09SZhen Lei static void test_kallsyms_compression_ratio(void) 10630f3bb09SZhen Lei { 10730f3bb09SZhen Lei u32 pos, off, len, num; 10830f3bb09SZhen Lei u32 ratio, total_size, total_len = 0; 10930f3bb09SZhen Lei 11030f3bb09SZhen Lei kallsyms_on_each_symbol(stat_symbol_len, &total_len); 11130f3bb09SZhen Lei 11230f3bb09SZhen Lei /* 11330f3bb09SZhen Lei * A symbol name cannot start with a number. This stub name helps us 11430f3bb09SZhen Lei * traverse the entire symbol table without finding a match. It's used 11530f3bb09SZhen Lei * for subsequent performance tests, and its length is the average 11630f3bb09SZhen Lei * length of all symbol names. 11730f3bb09SZhen Lei */ 11830f3bb09SZhen Lei memset(stub_name, '4', sizeof(stub_name)); 11930f3bb09SZhen Lei pos = total_len / kallsyms_num_syms; 12030f3bb09SZhen Lei stub_name[pos] = 0; 12130f3bb09SZhen Lei 12230f3bb09SZhen Lei pos = 0; 12330f3bb09SZhen Lei num = 0; 12430f3bb09SZhen Lei off = 0; 12530f3bb09SZhen Lei while (pos < kallsyms_num_syms) { 12630f3bb09SZhen Lei len = kallsyms_names[off]; 12730f3bb09SZhen Lei num++; 12830f3bb09SZhen Lei off++; 12930f3bb09SZhen Lei pos++; 13030f3bb09SZhen Lei if ((len & 0x80) != 0) { 13130f3bb09SZhen Lei len = (len & 0x7f) | (kallsyms_names[off] << 7); 13230f3bb09SZhen Lei num++; 13330f3bb09SZhen Lei off++; 13430f3bb09SZhen Lei } 13530f3bb09SZhen Lei off += len; 136*c86a514fSYang Li } 13730f3bb09SZhen Lei 13830f3bb09SZhen Lei /* 13930f3bb09SZhen Lei * 1. The length fields is not counted 14030f3bb09SZhen Lei * 2. The memory occupied by array kallsyms_token_table[] and 14130f3bb09SZhen Lei * kallsyms_token_index[] needs to be counted. 14230f3bb09SZhen Lei */ 14330f3bb09SZhen Lei total_size = off - num; 14430f3bb09SZhen Lei pos = kallsyms_token_index[0xff]; 14530f3bb09SZhen Lei total_size += pos + strlen(&kallsyms_token_table[pos]) + 1; 14630f3bb09SZhen Lei total_size += 0x100 * sizeof(u16); 14730f3bb09SZhen Lei 14830f3bb09SZhen Lei pr_info(" ---------------------------------------------------------\n"); 14930f3bb09SZhen Lei pr_info("| nr_symbols | compressed size | original size | ratio(%%) |\n"); 15030f3bb09SZhen Lei pr_info("|---------------------------------------------------------|\n"); 15130f3bb09SZhen Lei ratio = (u32)div_u64(10000ULL * total_size, total_len); 15230f3bb09SZhen Lei pr_info("| %10d | %10d | %10d | %2d.%-2d |\n", 15330f3bb09SZhen Lei kallsyms_num_syms, total_size, total_len, ratio / 100, ratio % 100); 15430f3bb09SZhen Lei pr_info(" ---------------------------------------------------------\n"); 15530f3bb09SZhen Lei } 15630f3bb09SZhen Lei 15730f3bb09SZhen Lei static int lookup_name(void *data, const char *name, struct module *mod, unsigned long addr) 15830f3bb09SZhen Lei { 15930f3bb09SZhen Lei u64 t0, t1, t; 16030f3bb09SZhen Lei unsigned long flags; 16130f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 16230f3bb09SZhen Lei 16330f3bb09SZhen Lei local_irq_save(flags); 16430f3bb09SZhen Lei t0 = sched_clock(); 16530f3bb09SZhen Lei (void)kallsyms_lookup_name(name); 16630f3bb09SZhen Lei t1 = sched_clock(); 16730f3bb09SZhen Lei local_irq_restore(flags); 16830f3bb09SZhen Lei 16930f3bb09SZhen Lei t = t1 - t0; 17030f3bb09SZhen Lei if (t < stat->min) 17130f3bb09SZhen Lei stat->min = t; 17230f3bb09SZhen Lei 17330f3bb09SZhen Lei if (t > stat->max) 17430f3bb09SZhen Lei stat->max = t; 17530f3bb09SZhen Lei 17630f3bb09SZhen Lei stat->real_cnt++; 17730f3bb09SZhen Lei stat->sum += t; 17830f3bb09SZhen Lei 17930f3bb09SZhen Lei return 0; 18030f3bb09SZhen Lei } 18130f3bb09SZhen Lei 18230f3bb09SZhen Lei static void test_perf_kallsyms_lookup_name(void) 18330f3bb09SZhen Lei { 18430f3bb09SZhen Lei struct test_stat stat; 18530f3bb09SZhen Lei 18630f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 18730f3bb09SZhen Lei stat.min = INT_MAX; 18830f3bb09SZhen Lei kallsyms_on_each_symbol(lookup_name, &stat); 18930f3bb09SZhen Lei pr_info("kallsyms_lookup_name() looked up %d symbols\n", stat.real_cnt); 19030f3bb09SZhen Lei pr_info("The time spent on each symbol is (ns): min=%d, max=%d, avg=%lld\n", 19130f3bb09SZhen Lei stat.min, stat.max, div_u64(stat.sum, stat.real_cnt)); 19230f3bb09SZhen Lei } 19330f3bb09SZhen Lei 19430f3bb09SZhen Lei static bool match_cleanup_name(const char *s, const char *name) 19530f3bb09SZhen Lei { 19630f3bb09SZhen Lei char *p; 19730f3bb09SZhen Lei int len; 19830f3bb09SZhen Lei 19930f3bb09SZhen Lei if (!IS_ENABLED(CONFIG_LTO_CLANG)) 20030f3bb09SZhen Lei return false; 20130f3bb09SZhen Lei 20230f3bb09SZhen Lei p = strchr(s, '.'); 20330f3bb09SZhen Lei if (!p) 20430f3bb09SZhen Lei return false; 20530f3bb09SZhen Lei 20630f3bb09SZhen Lei len = strlen(name); 20730f3bb09SZhen Lei if (p - s != len) 20830f3bb09SZhen Lei return false; 20930f3bb09SZhen Lei 21030f3bb09SZhen Lei return !strncmp(s, name, len); 21130f3bb09SZhen Lei } 21230f3bb09SZhen Lei 21330f3bb09SZhen Lei static int find_symbol(void *data, const char *name, struct module *mod, unsigned long addr) 21430f3bb09SZhen Lei { 21530f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 21630f3bb09SZhen Lei 21730f3bb09SZhen Lei if (strcmp(name, stat->name) == 0 || 21830f3bb09SZhen Lei (!stat->perf && match_cleanup_name(name, stat->name))) { 21930f3bb09SZhen Lei stat->real_cnt++; 22030f3bb09SZhen Lei stat->addr = addr; 22130f3bb09SZhen Lei 22230f3bb09SZhen Lei if (stat->save_cnt < MAX_NUM_OF_RECORDS) { 22330f3bb09SZhen Lei stat->addrs[stat->save_cnt] = addr; 22430f3bb09SZhen Lei stat->save_cnt++; 22530f3bb09SZhen Lei } 22630f3bb09SZhen Lei 22730f3bb09SZhen Lei if (stat->real_cnt == stat->max) 22830f3bb09SZhen Lei return 1; 22930f3bb09SZhen Lei } 23030f3bb09SZhen Lei 23130f3bb09SZhen Lei return 0; 23230f3bb09SZhen Lei } 23330f3bb09SZhen Lei 23430f3bb09SZhen Lei static void test_perf_kallsyms_on_each_symbol(void) 23530f3bb09SZhen Lei { 23630f3bb09SZhen Lei u64 t0, t1; 23730f3bb09SZhen Lei unsigned long flags; 23830f3bb09SZhen Lei struct test_stat stat; 23930f3bb09SZhen Lei 24030f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 24130f3bb09SZhen Lei stat.max = INT_MAX; 24230f3bb09SZhen Lei stat.name = stub_name; 24330f3bb09SZhen Lei stat.perf = 1; 24430f3bb09SZhen Lei local_irq_save(flags); 24530f3bb09SZhen Lei t0 = sched_clock(); 24630f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, &stat); 24730f3bb09SZhen Lei t1 = sched_clock(); 24830f3bb09SZhen Lei local_irq_restore(flags); 24930f3bb09SZhen Lei pr_info("kallsyms_on_each_symbol() traverse all: %lld ns\n", t1 - t0); 25030f3bb09SZhen Lei } 25130f3bb09SZhen Lei 25230f3bb09SZhen Lei static int match_symbol(void *data, unsigned long addr) 25330f3bb09SZhen Lei { 25430f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 25530f3bb09SZhen Lei 25630f3bb09SZhen Lei stat->real_cnt++; 25730f3bb09SZhen Lei stat->addr = addr; 25830f3bb09SZhen Lei 25930f3bb09SZhen Lei if (stat->save_cnt < MAX_NUM_OF_RECORDS) { 26030f3bb09SZhen Lei stat->addrs[stat->save_cnt] = addr; 26130f3bb09SZhen Lei stat->save_cnt++; 26230f3bb09SZhen Lei } 26330f3bb09SZhen Lei 26430f3bb09SZhen Lei if (stat->real_cnt == stat->max) 26530f3bb09SZhen Lei return 1; 26630f3bb09SZhen Lei 26730f3bb09SZhen Lei return 0; 26830f3bb09SZhen Lei } 26930f3bb09SZhen Lei 27030f3bb09SZhen Lei static void test_perf_kallsyms_on_each_match_symbol(void) 27130f3bb09SZhen Lei { 27230f3bb09SZhen Lei u64 t0, t1; 27330f3bb09SZhen Lei unsigned long flags; 27430f3bb09SZhen Lei struct test_stat stat; 27530f3bb09SZhen Lei 27630f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 27730f3bb09SZhen Lei stat.max = INT_MAX; 27830f3bb09SZhen Lei stat.name = stub_name; 27930f3bb09SZhen Lei local_irq_save(flags); 28030f3bb09SZhen Lei t0 = sched_clock(); 28130f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, stat.name, &stat); 28230f3bb09SZhen Lei t1 = sched_clock(); 28330f3bb09SZhen Lei local_irq_restore(flags); 28430f3bb09SZhen Lei pr_info("kallsyms_on_each_match_symbol() traverse all: %lld ns\n", t1 - t0); 28530f3bb09SZhen Lei } 28630f3bb09SZhen Lei 28730f3bb09SZhen Lei static int test_kallsyms_basic_function(void) 28830f3bb09SZhen Lei { 28930f3bb09SZhen Lei int i, j, ret; 29030f3bb09SZhen Lei int next = 0, nr_failed = 0; 29130f3bb09SZhen Lei char *prefix; 29230f3bb09SZhen Lei unsigned short rand; 29330f3bb09SZhen Lei unsigned long addr, lookup_addr; 29430f3bb09SZhen Lei char namebuf[KSYM_NAME_LEN]; 29530f3bb09SZhen Lei struct test_stat *stat, *stat2; 29630f3bb09SZhen Lei 29730f3bb09SZhen Lei stat = kmalloc(sizeof(*stat) * 2, GFP_KERNEL); 29830f3bb09SZhen Lei if (!stat) 29930f3bb09SZhen Lei return -ENOMEM; 30030f3bb09SZhen Lei stat2 = stat + 1; 30130f3bb09SZhen Lei 30230f3bb09SZhen Lei prefix = "kallsyms_lookup_name() for"; 30330f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 30430f3bb09SZhen Lei addr = kallsyms_lookup_name(test_items[i].name); 30530f3bb09SZhen Lei if (addr != test_items[i].addr) { 30630f3bb09SZhen Lei nr_failed++; 30730f3bb09SZhen Lei pr_info("%s %s failed: addr=%lx, expect %lx\n", 30830f3bb09SZhen Lei prefix, test_items[i].name, addr, test_items[i].addr); 30930f3bb09SZhen Lei } 31030f3bb09SZhen Lei } 31130f3bb09SZhen Lei 31230f3bb09SZhen Lei prefix = "kallsyms_on_each_symbol() for"; 31330f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 31430f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 31530f3bb09SZhen Lei stat->max = INT_MAX; 31630f3bb09SZhen Lei stat->name = test_items[i].name; 31730f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, stat); 31830f3bb09SZhen Lei if (stat->addr != test_items[i].addr || stat->real_cnt != 1) { 31930f3bb09SZhen Lei nr_failed++; 32030f3bb09SZhen Lei pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n", 32130f3bb09SZhen Lei prefix, test_items[i].name, 32230f3bb09SZhen Lei stat->real_cnt, stat->addr, test_items[i].addr); 32330f3bb09SZhen Lei } 32430f3bb09SZhen Lei } 32530f3bb09SZhen Lei 32630f3bb09SZhen Lei prefix = "kallsyms_on_each_match_symbol() for"; 32730f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 32830f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 32930f3bb09SZhen Lei stat->max = INT_MAX; 33030f3bb09SZhen Lei stat->name = test_items[i].name; 33130f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, stat); 33230f3bb09SZhen Lei if (stat->addr != test_items[i].addr || stat->real_cnt != 1) { 33330f3bb09SZhen Lei nr_failed++; 33430f3bb09SZhen Lei pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n", 33530f3bb09SZhen Lei prefix, test_items[i].name, 33630f3bb09SZhen Lei stat->real_cnt, stat->addr, test_items[i].addr); 33730f3bb09SZhen Lei } 33830f3bb09SZhen Lei } 33930f3bb09SZhen Lei 34030f3bb09SZhen Lei if (nr_failed) { 34130f3bb09SZhen Lei kfree(stat); 34230f3bb09SZhen Lei return -ESRCH; 34330f3bb09SZhen Lei } 34430f3bb09SZhen Lei 34530f3bb09SZhen Lei for (i = 0; i < kallsyms_num_syms; i++) { 34630f3bb09SZhen Lei addr = kallsyms_sym_address(i); 34730f3bb09SZhen Lei if (!is_ksym_addr(addr)) 34830f3bb09SZhen Lei continue; 34930f3bb09SZhen Lei 35030f3bb09SZhen Lei ret = lookup_symbol_name(addr, namebuf); 35130f3bb09SZhen Lei if (unlikely(ret)) { 35230f3bb09SZhen Lei namebuf[0] = 0; 35330f3bb09SZhen Lei goto failed; 35430f3bb09SZhen Lei } 35530f3bb09SZhen Lei 35630f3bb09SZhen Lei /* 35730f3bb09SZhen Lei * The first '.' may be the initial letter, in which case the 35830f3bb09SZhen Lei * entire symbol name will be truncated to an empty string in 35930f3bb09SZhen Lei * cleanup_symbol_name(). Do not test these symbols. 36030f3bb09SZhen Lei * 36130f3bb09SZhen Lei * For example: 36230f3bb09SZhen Lei * cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head 36330f3bb09SZhen Lei * .E_read_words 36430f3bb09SZhen Lei * .E_leading_bytes 36530f3bb09SZhen Lei * .E_trailing_bytes 36630f3bb09SZhen Lei * .E_write_words 36730f3bb09SZhen Lei * .E_copy 36830f3bb09SZhen Lei * .str.292.llvm.12122243386960820698 36930f3bb09SZhen Lei * .str.24.llvm.12122243386960820698 37030f3bb09SZhen Lei * .str.29.llvm.12122243386960820698 37130f3bb09SZhen Lei * .str.75.llvm.12122243386960820698 37230f3bb09SZhen Lei * .str.99.llvm.12122243386960820698 37330f3bb09SZhen Lei */ 37430f3bb09SZhen Lei if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0]) 37530f3bb09SZhen Lei continue; 37630f3bb09SZhen Lei 37730f3bb09SZhen Lei lookup_addr = kallsyms_lookup_name(namebuf); 37830f3bb09SZhen Lei 37930f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 38030f3bb09SZhen Lei stat->max = INT_MAX; 38130f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, namebuf, stat); 38230f3bb09SZhen Lei 38330f3bb09SZhen Lei /* 38430f3bb09SZhen Lei * kallsyms_on_each_symbol() is too slow, randomly select some 38530f3bb09SZhen Lei * symbols for test. 38630f3bb09SZhen Lei */ 38730f3bb09SZhen Lei if (i >= next) { 38830f3bb09SZhen Lei memset(stat2, 0, sizeof(*stat2)); 38930f3bb09SZhen Lei stat2->max = INT_MAX; 39030f3bb09SZhen Lei stat2->name = namebuf; 39130f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, stat2); 39230f3bb09SZhen Lei 39330f3bb09SZhen Lei /* 39430f3bb09SZhen Lei * kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol() 39530f3bb09SZhen Lei * need to get the same traversal result. 39630f3bb09SZhen Lei */ 39730f3bb09SZhen Lei if (stat->addr != stat2->addr || 39830f3bb09SZhen Lei stat->real_cnt != stat2->real_cnt || 39930f3bb09SZhen Lei memcmp(stat->addrs, stat2->addrs, 40030f3bb09SZhen Lei stat->save_cnt * sizeof(stat->addrs[0]))) 40130f3bb09SZhen Lei goto failed; 40230f3bb09SZhen Lei 40330f3bb09SZhen Lei /* 40430f3bb09SZhen Lei * The average of random increments is 128, that is, one of 40530f3bb09SZhen Lei * them is tested every 128 symbols. 40630f3bb09SZhen Lei */ 40730f3bb09SZhen Lei get_random_bytes(&rand, sizeof(rand)); 40830f3bb09SZhen Lei next = i + (rand & 0xff) + 1; 40930f3bb09SZhen Lei } 41030f3bb09SZhen Lei 41130f3bb09SZhen Lei /* Need to be found at least once */ 41230f3bb09SZhen Lei if (!stat->real_cnt) 41330f3bb09SZhen Lei goto failed; 41430f3bb09SZhen Lei 41530f3bb09SZhen Lei /* 41630f3bb09SZhen Lei * kallsyms_lookup_name() returns the address of the first 41730f3bb09SZhen Lei * symbol found and cannot be NULL. 41830f3bb09SZhen Lei */ 41930f3bb09SZhen Lei if (!lookup_addr || lookup_addr != stat->addrs[0]) 42030f3bb09SZhen Lei goto failed; 42130f3bb09SZhen Lei 42230f3bb09SZhen Lei /* 42330f3bb09SZhen Lei * If the addresses of all matching symbols are recorded, the 42430f3bb09SZhen Lei * target address needs to be exist. 42530f3bb09SZhen Lei */ 42630f3bb09SZhen Lei if (stat->real_cnt <= MAX_NUM_OF_RECORDS) { 42730f3bb09SZhen Lei for (j = 0; j < stat->save_cnt; j++) { 42830f3bb09SZhen Lei if (stat->addrs[j] == addr) 42930f3bb09SZhen Lei break; 43030f3bb09SZhen Lei } 43130f3bb09SZhen Lei 43230f3bb09SZhen Lei if (j == stat->save_cnt) 43330f3bb09SZhen Lei goto failed; 43430f3bb09SZhen Lei } 43530f3bb09SZhen Lei } 43630f3bb09SZhen Lei 43730f3bb09SZhen Lei kfree(stat); 43830f3bb09SZhen Lei 43930f3bb09SZhen Lei return 0; 44030f3bb09SZhen Lei 44130f3bb09SZhen Lei failed: 44230f3bb09SZhen Lei pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr); 44330f3bb09SZhen Lei kfree(stat); 44430f3bb09SZhen Lei return -ESRCH; 44530f3bb09SZhen Lei } 44630f3bb09SZhen Lei 44730f3bb09SZhen Lei static int test_entry(void *p) 44830f3bb09SZhen Lei { 44930f3bb09SZhen Lei int ret; 45030f3bb09SZhen Lei 45130f3bb09SZhen Lei do { 45230f3bb09SZhen Lei schedule_timeout(5 * HZ); 45330f3bb09SZhen Lei } while (system_state != SYSTEM_RUNNING); 45430f3bb09SZhen Lei 45530f3bb09SZhen Lei pr_info("start\n"); 45630f3bb09SZhen Lei ret = test_kallsyms_basic_function(); 45730f3bb09SZhen Lei if (ret) { 45830f3bb09SZhen Lei pr_info("abort\n"); 45930f3bb09SZhen Lei return 0; 46030f3bb09SZhen Lei } 46130f3bb09SZhen Lei 46230f3bb09SZhen Lei test_kallsyms_compression_ratio(); 46330f3bb09SZhen Lei test_perf_kallsyms_lookup_name(); 46430f3bb09SZhen Lei test_perf_kallsyms_on_each_symbol(); 46530f3bb09SZhen Lei test_perf_kallsyms_on_each_match_symbol(); 46630f3bb09SZhen Lei pr_info("finish\n"); 46730f3bb09SZhen Lei 46830f3bb09SZhen Lei return 0; 46930f3bb09SZhen Lei } 47030f3bb09SZhen Lei 47130f3bb09SZhen Lei static int __init kallsyms_test_init(void) 47230f3bb09SZhen Lei { 47330f3bb09SZhen Lei struct task_struct *t; 47430f3bb09SZhen Lei 47530f3bb09SZhen Lei t = kthread_create(test_entry, NULL, "kallsyms_test"); 47630f3bb09SZhen Lei if (IS_ERR(t)) { 47730f3bb09SZhen Lei pr_info("Create kallsyms selftest task failed\n"); 47830f3bb09SZhen Lei return PTR_ERR(t); 47930f3bb09SZhen Lei } 48030f3bb09SZhen Lei kthread_bind(t, 0); 48130f3bb09SZhen Lei wake_up_process(t); 48230f3bb09SZhen Lei 48330f3bb09SZhen Lei return 0; 48430f3bb09SZhen Lei } 48530f3bb09SZhen Lei late_initcall(kallsyms_test_init); 486