1*30f3bb09SZhen Lei // SPDX-License-Identifier: GPL-2.0-or-later 2*30f3bb09SZhen Lei /* 3*30f3bb09SZhen Lei * Test the function and performance of kallsyms 4*30f3bb09SZhen Lei * 5*30f3bb09SZhen Lei * Copyright (C) Huawei Technologies Co., Ltd., 2022 6*30f3bb09SZhen Lei * 7*30f3bb09SZhen Lei * Authors: Zhen Lei <thunder.leizhen@huawei.com> Huawei 8*30f3bb09SZhen Lei */ 9*30f3bb09SZhen Lei 10*30f3bb09SZhen Lei #define pr_fmt(fmt) "kallsyms_selftest: " fmt 11*30f3bb09SZhen Lei 12*30f3bb09SZhen Lei #include <linux/init.h> 13*30f3bb09SZhen Lei #include <linux/module.h> 14*30f3bb09SZhen Lei #include <linux/kallsyms.h> 15*30f3bb09SZhen Lei #include <linux/random.h> 16*30f3bb09SZhen Lei #include <linux/sched/clock.h> 17*30f3bb09SZhen Lei #include <linux/kthread.h> 18*30f3bb09SZhen Lei #include <linux/vmalloc.h> 19*30f3bb09SZhen Lei 20*30f3bb09SZhen Lei #include "kallsyms_internal.h" 21*30f3bb09SZhen Lei #include "kallsyms_selftest.h" 22*30f3bb09SZhen Lei 23*30f3bb09SZhen Lei 24*30f3bb09SZhen Lei #define MAX_NUM_OF_RECORDS 64 25*30f3bb09SZhen Lei 26*30f3bb09SZhen Lei struct test_stat { 27*30f3bb09SZhen Lei int min; 28*30f3bb09SZhen Lei int max; 29*30f3bb09SZhen Lei int save_cnt; 30*30f3bb09SZhen Lei int real_cnt; 31*30f3bb09SZhen Lei int perf; 32*30f3bb09SZhen Lei u64 sum; 33*30f3bb09SZhen Lei char *name; 34*30f3bb09SZhen Lei unsigned long addr; 35*30f3bb09SZhen Lei unsigned long addrs[MAX_NUM_OF_RECORDS]; 36*30f3bb09SZhen Lei }; 37*30f3bb09SZhen Lei 38*30f3bb09SZhen Lei struct test_item { 39*30f3bb09SZhen Lei char *name; 40*30f3bb09SZhen Lei unsigned long addr; 41*30f3bb09SZhen Lei }; 42*30f3bb09SZhen Lei 43*30f3bb09SZhen Lei #define ITEM_FUNC(s) \ 44*30f3bb09SZhen Lei { \ 45*30f3bb09SZhen Lei .name = #s, \ 46*30f3bb09SZhen Lei .addr = (unsigned long)s, \ 47*30f3bb09SZhen Lei } 48*30f3bb09SZhen Lei 49*30f3bb09SZhen Lei #define ITEM_DATA(s) \ 50*30f3bb09SZhen Lei { \ 51*30f3bb09SZhen Lei .name = #s, \ 52*30f3bb09SZhen Lei .addr = (unsigned long)&s, \ 53*30f3bb09SZhen Lei } 54*30f3bb09SZhen Lei 55*30f3bb09SZhen Lei 56*30f3bb09SZhen Lei static int kallsyms_test_var_bss_static; 57*30f3bb09SZhen Lei static int kallsyms_test_var_data_static = 1; 58*30f3bb09SZhen Lei int kallsyms_test_var_bss; 59*30f3bb09SZhen Lei int kallsyms_test_var_data = 1; 60*30f3bb09SZhen Lei 61*30f3bb09SZhen Lei static int kallsyms_test_func_static(void) 62*30f3bb09SZhen Lei { 63*30f3bb09SZhen Lei kallsyms_test_var_bss_static++; 64*30f3bb09SZhen Lei kallsyms_test_var_data_static++; 65*30f3bb09SZhen Lei 66*30f3bb09SZhen Lei return 0; 67*30f3bb09SZhen Lei } 68*30f3bb09SZhen Lei 69*30f3bb09SZhen Lei int kallsyms_test_func(void) 70*30f3bb09SZhen Lei { 71*30f3bb09SZhen Lei return kallsyms_test_func_static(); 72*30f3bb09SZhen Lei } 73*30f3bb09SZhen Lei 74*30f3bb09SZhen Lei __weak int kallsyms_test_func_weak(void) 75*30f3bb09SZhen Lei { 76*30f3bb09SZhen Lei kallsyms_test_var_bss++; 77*30f3bb09SZhen Lei kallsyms_test_var_data++; 78*30f3bb09SZhen Lei return 0; 79*30f3bb09SZhen Lei } 80*30f3bb09SZhen Lei 81*30f3bb09SZhen Lei static struct test_item test_items[] = { 82*30f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func_static), 83*30f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func), 84*30f3bb09SZhen Lei ITEM_FUNC(kallsyms_test_func_weak), 85*30f3bb09SZhen Lei ITEM_FUNC(vmalloc), 86*30f3bb09SZhen Lei ITEM_FUNC(vfree), 87*30f3bb09SZhen Lei #ifdef CONFIG_KALLSYMS_ALL 88*30f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_bss_static), 89*30f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_data_static), 90*30f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_bss), 91*30f3bb09SZhen Lei ITEM_DATA(kallsyms_test_var_data), 92*30f3bb09SZhen Lei ITEM_DATA(vmap_area_list), 93*30f3bb09SZhen Lei #endif 94*30f3bb09SZhen Lei }; 95*30f3bb09SZhen Lei 96*30f3bb09SZhen Lei static char stub_name[KSYM_NAME_LEN]; 97*30f3bb09SZhen Lei 98*30f3bb09SZhen Lei static int stat_symbol_len(void *data, const char *name, struct module *mod, unsigned long addr) 99*30f3bb09SZhen Lei { 100*30f3bb09SZhen Lei *(u32 *)data += strlen(name); 101*30f3bb09SZhen Lei 102*30f3bb09SZhen Lei return 0; 103*30f3bb09SZhen Lei } 104*30f3bb09SZhen Lei 105*30f3bb09SZhen Lei static void test_kallsyms_compression_ratio(void) 106*30f3bb09SZhen Lei { 107*30f3bb09SZhen Lei u32 pos, off, len, num; 108*30f3bb09SZhen Lei u32 ratio, total_size, total_len = 0; 109*30f3bb09SZhen Lei 110*30f3bb09SZhen Lei kallsyms_on_each_symbol(stat_symbol_len, &total_len); 111*30f3bb09SZhen Lei 112*30f3bb09SZhen Lei /* 113*30f3bb09SZhen Lei * A symbol name cannot start with a number. This stub name helps us 114*30f3bb09SZhen Lei * traverse the entire symbol table without finding a match. It's used 115*30f3bb09SZhen Lei * for subsequent performance tests, and its length is the average 116*30f3bb09SZhen Lei * length of all symbol names. 117*30f3bb09SZhen Lei */ 118*30f3bb09SZhen Lei memset(stub_name, '4', sizeof(stub_name)); 119*30f3bb09SZhen Lei pos = total_len / kallsyms_num_syms; 120*30f3bb09SZhen Lei stub_name[pos] = 0; 121*30f3bb09SZhen Lei 122*30f3bb09SZhen Lei pos = 0; 123*30f3bb09SZhen Lei num = 0; 124*30f3bb09SZhen Lei off = 0; 125*30f3bb09SZhen Lei while (pos < kallsyms_num_syms) { 126*30f3bb09SZhen Lei len = kallsyms_names[off]; 127*30f3bb09SZhen Lei num++; 128*30f3bb09SZhen Lei off++; 129*30f3bb09SZhen Lei pos++; 130*30f3bb09SZhen Lei if ((len & 0x80) != 0) { 131*30f3bb09SZhen Lei len = (len & 0x7f) | (kallsyms_names[off] << 7); 132*30f3bb09SZhen Lei num++; 133*30f3bb09SZhen Lei off++; 134*30f3bb09SZhen Lei } 135*30f3bb09SZhen Lei off += len; 136*30f3bb09SZhen Lei }; 137*30f3bb09SZhen Lei 138*30f3bb09SZhen Lei /* 139*30f3bb09SZhen Lei * 1. The length fields is not counted 140*30f3bb09SZhen Lei * 2. The memory occupied by array kallsyms_token_table[] and 141*30f3bb09SZhen Lei * kallsyms_token_index[] needs to be counted. 142*30f3bb09SZhen Lei */ 143*30f3bb09SZhen Lei total_size = off - num; 144*30f3bb09SZhen Lei pos = kallsyms_token_index[0xff]; 145*30f3bb09SZhen Lei total_size += pos + strlen(&kallsyms_token_table[pos]) + 1; 146*30f3bb09SZhen Lei total_size += 0x100 * sizeof(u16); 147*30f3bb09SZhen Lei 148*30f3bb09SZhen Lei pr_info(" ---------------------------------------------------------\n"); 149*30f3bb09SZhen Lei pr_info("| nr_symbols | compressed size | original size | ratio(%%) |\n"); 150*30f3bb09SZhen Lei pr_info("|---------------------------------------------------------|\n"); 151*30f3bb09SZhen Lei ratio = (u32)div_u64(10000ULL * total_size, total_len); 152*30f3bb09SZhen Lei pr_info("| %10d | %10d | %10d | %2d.%-2d |\n", 153*30f3bb09SZhen Lei kallsyms_num_syms, total_size, total_len, ratio / 100, ratio % 100); 154*30f3bb09SZhen Lei pr_info(" ---------------------------------------------------------\n"); 155*30f3bb09SZhen Lei } 156*30f3bb09SZhen Lei 157*30f3bb09SZhen Lei static int lookup_name(void *data, const char *name, struct module *mod, unsigned long addr) 158*30f3bb09SZhen Lei { 159*30f3bb09SZhen Lei u64 t0, t1, t; 160*30f3bb09SZhen Lei unsigned long flags; 161*30f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 162*30f3bb09SZhen Lei 163*30f3bb09SZhen Lei local_irq_save(flags); 164*30f3bb09SZhen Lei t0 = sched_clock(); 165*30f3bb09SZhen Lei (void)kallsyms_lookup_name(name); 166*30f3bb09SZhen Lei t1 = sched_clock(); 167*30f3bb09SZhen Lei local_irq_restore(flags); 168*30f3bb09SZhen Lei 169*30f3bb09SZhen Lei t = t1 - t0; 170*30f3bb09SZhen Lei if (t < stat->min) 171*30f3bb09SZhen Lei stat->min = t; 172*30f3bb09SZhen Lei 173*30f3bb09SZhen Lei if (t > stat->max) 174*30f3bb09SZhen Lei stat->max = t; 175*30f3bb09SZhen Lei 176*30f3bb09SZhen Lei stat->real_cnt++; 177*30f3bb09SZhen Lei stat->sum += t; 178*30f3bb09SZhen Lei 179*30f3bb09SZhen Lei return 0; 180*30f3bb09SZhen Lei } 181*30f3bb09SZhen Lei 182*30f3bb09SZhen Lei static void test_perf_kallsyms_lookup_name(void) 183*30f3bb09SZhen Lei { 184*30f3bb09SZhen Lei struct test_stat stat; 185*30f3bb09SZhen Lei 186*30f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 187*30f3bb09SZhen Lei stat.min = INT_MAX; 188*30f3bb09SZhen Lei kallsyms_on_each_symbol(lookup_name, &stat); 189*30f3bb09SZhen Lei pr_info("kallsyms_lookup_name() looked up %d symbols\n", stat.real_cnt); 190*30f3bb09SZhen Lei pr_info("The time spent on each symbol is (ns): min=%d, max=%d, avg=%lld\n", 191*30f3bb09SZhen Lei stat.min, stat.max, div_u64(stat.sum, stat.real_cnt)); 192*30f3bb09SZhen Lei } 193*30f3bb09SZhen Lei 194*30f3bb09SZhen Lei static bool match_cleanup_name(const char *s, const char *name) 195*30f3bb09SZhen Lei { 196*30f3bb09SZhen Lei char *p; 197*30f3bb09SZhen Lei int len; 198*30f3bb09SZhen Lei 199*30f3bb09SZhen Lei if (!IS_ENABLED(CONFIG_LTO_CLANG)) 200*30f3bb09SZhen Lei return false; 201*30f3bb09SZhen Lei 202*30f3bb09SZhen Lei p = strchr(s, '.'); 203*30f3bb09SZhen Lei if (!p) 204*30f3bb09SZhen Lei return false; 205*30f3bb09SZhen Lei 206*30f3bb09SZhen Lei len = strlen(name); 207*30f3bb09SZhen Lei if (p - s != len) 208*30f3bb09SZhen Lei return false; 209*30f3bb09SZhen Lei 210*30f3bb09SZhen Lei return !strncmp(s, name, len); 211*30f3bb09SZhen Lei } 212*30f3bb09SZhen Lei 213*30f3bb09SZhen Lei static int find_symbol(void *data, const char *name, struct module *mod, unsigned long addr) 214*30f3bb09SZhen Lei { 215*30f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 216*30f3bb09SZhen Lei 217*30f3bb09SZhen Lei if (strcmp(name, stat->name) == 0 || 218*30f3bb09SZhen Lei (!stat->perf && match_cleanup_name(name, stat->name))) { 219*30f3bb09SZhen Lei stat->real_cnt++; 220*30f3bb09SZhen Lei stat->addr = addr; 221*30f3bb09SZhen Lei 222*30f3bb09SZhen Lei if (stat->save_cnt < MAX_NUM_OF_RECORDS) { 223*30f3bb09SZhen Lei stat->addrs[stat->save_cnt] = addr; 224*30f3bb09SZhen Lei stat->save_cnt++; 225*30f3bb09SZhen Lei } 226*30f3bb09SZhen Lei 227*30f3bb09SZhen Lei if (stat->real_cnt == stat->max) 228*30f3bb09SZhen Lei return 1; 229*30f3bb09SZhen Lei } 230*30f3bb09SZhen Lei 231*30f3bb09SZhen Lei return 0; 232*30f3bb09SZhen Lei } 233*30f3bb09SZhen Lei 234*30f3bb09SZhen Lei static void test_perf_kallsyms_on_each_symbol(void) 235*30f3bb09SZhen Lei { 236*30f3bb09SZhen Lei u64 t0, t1; 237*30f3bb09SZhen Lei unsigned long flags; 238*30f3bb09SZhen Lei struct test_stat stat; 239*30f3bb09SZhen Lei 240*30f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 241*30f3bb09SZhen Lei stat.max = INT_MAX; 242*30f3bb09SZhen Lei stat.name = stub_name; 243*30f3bb09SZhen Lei stat.perf = 1; 244*30f3bb09SZhen Lei local_irq_save(flags); 245*30f3bb09SZhen Lei t0 = sched_clock(); 246*30f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, &stat); 247*30f3bb09SZhen Lei t1 = sched_clock(); 248*30f3bb09SZhen Lei local_irq_restore(flags); 249*30f3bb09SZhen Lei pr_info("kallsyms_on_each_symbol() traverse all: %lld ns\n", t1 - t0); 250*30f3bb09SZhen Lei } 251*30f3bb09SZhen Lei 252*30f3bb09SZhen Lei static int match_symbol(void *data, unsigned long addr) 253*30f3bb09SZhen Lei { 254*30f3bb09SZhen Lei struct test_stat *stat = (struct test_stat *)data; 255*30f3bb09SZhen Lei 256*30f3bb09SZhen Lei stat->real_cnt++; 257*30f3bb09SZhen Lei stat->addr = addr; 258*30f3bb09SZhen Lei 259*30f3bb09SZhen Lei if (stat->save_cnt < MAX_NUM_OF_RECORDS) { 260*30f3bb09SZhen Lei stat->addrs[stat->save_cnt] = addr; 261*30f3bb09SZhen Lei stat->save_cnt++; 262*30f3bb09SZhen Lei } 263*30f3bb09SZhen Lei 264*30f3bb09SZhen Lei if (stat->real_cnt == stat->max) 265*30f3bb09SZhen Lei return 1; 266*30f3bb09SZhen Lei 267*30f3bb09SZhen Lei return 0; 268*30f3bb09SZhen Lei } 269*30f3bb09SZhen Lei 270*30f3bb09SZhen Lei static void test_perf_kallsyms_on_each_match_symbol(void) 271*30f3bb09SZhen Lei { 272*30f3bb09SZhen Lei u64 t0, t1; 273*30f3bb09SZhen Lei unsigned long flags; 274*30f3bb09SZhen Lei struct test_stat stat; 275*30f3bb09SZhen Lei 276*30f3bb09SZhen Lei memset(&stat, 0, sizeof(stat)); 277*30f3bb09SZhen Lei stat.max = INT_MAX; 278*30f3bb09SZhen Lei stat.name = stub_name; 279*30f3bb09SZhen Lei local_irq_save(flags); 280*30f3bb09SZhen Lei t0 = sched_clock(); 281*30f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, stat.name, &stat); 282*30f3bb09SZhen Lei t1 = sched_clock(); 283*30f3bb09SZhen Lei local_irq_restore(flags); 284*30f3bb09SZhen Lei pr_info("kallsyms_on_each_match_symbol() traverse all: %lld ns\n", t1 - t0); 285*30f3bb09SZhen Lei } 286*30f3bb09SZhen Lei 287*30f3bb09SZhen Lei static int test_kallsyms_basic_function(void) 288*30f3bb09SZhen Lei { 289*30f3bb09SZhen Lei int i, j, ret; 290*30f3bb09SZhen Lei int next = 0, nr_failed = 0; 291*30f3bb09SZhen Lei char *prefix; 292*30f3bb09SZhen Lei unsigned short rand; 293*30f3bb09SZhen Lei unsigned long addr, lookup_addr; 294*30f3bb09SZhen Lei char namebuf[KSYM_NAME_LEN]; 295*30f3bb09SZhen Lei struct test_stat *stat, *stat2; 296*30f3bb09SZhen Lei 297*30f3bb09SZhen Lei stat = kmalloc(sizeof(*stat) * 2, GFP_KERNEL); 298*30f3bb09SZhen Lei if (!stat) 299*30f3bb09SZhen Lei return -ENOMEM; 300*30f3bb09SZhen Lei stat2 = stat + 1; 301*30f3bb09SZhen Lei 302*30f3bb09SZhen Lei prefix = "kallsyms_lookup_name() for"; 303*30f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 304*30f3bb09SZhen Lei addr = kallsyms_lookup_name(test_items[i].name); 305*30f3bb09SZhen Lei if (addr != test_items[i].addr) { 306*30f3bb09SZhen Lei nr_failed++; 307*30f3bb09SZhen Lei pr_info("%s %s failed: addr=%lx, expect %lx\n", 308*30f3bb09SZhen Lei prefix, test_items[i].name, addr, test_items[i].addr); 309*30f3bb09SZhen Lei } 310*30f3bb09SZhen Lei } 311*30f3bb09SZhen Lei 312*30f3bb09SZhen Lei prefix = "kallsyms_on_each_symbol() for"; 313*30f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 314*30f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 315*30f3bb09SZhen Lei stat->max = INT_MAX; 316*30f3bb09SZhen Lei stat->name = test_items[i].name; 317*30f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, stat); 318*30f3bb09SZhen Lei if (stat->addr != test_items[i].addr || stat->real_cnt != 1) { 319*30f3bb09SZhen Lei nr_failed++; 320*30f3bb09SZhen Lei pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n", 321*30f3bb09SZhen Lei prefix, test_items[i].name, 322*30f3bb09SZhen Lei stat->real_cnt, stat->addr, test_items[i].addr); 323*30f3bb09SZhen Lei } 324*30f3bb09SZhen Lei } 325*30f3bb09SZhen Lei 326*30f3bb09SZhen Lei prefix = "kallsyms_on_each_match_symbol() for"; 327*30f3bb09SZhen Lei for (i = 0; i < ARRAY_SIZE(test_items); i++) { 328*30f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 329*30f3bb09SZhen Lei stat->max = INT_MAX; 330*30f3bb09SZhen Lei stat->name = test_items[i].name; 331*30f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, stat); 332*30f3bb09SZhen Lei if (stat->addr != test_items[i].addr || stat->real_cnt != 1) { 333*30f3bb09SZhen Lei nr_failed++; 334*30f3bb09SZhen Lei pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n", 335*30f3bb09SZhen Lei prefix, test_items[i].name, 336*30f3bb09SZhen Lei stat->real_cnt, stat->addr, test_items[i].addr); 337*30f3bb09SZhen Lei } 338*30f3bb09SZhen Lei } 339*30f3bb09SZhen Lei 340*30f3bb09SZhen Lei if (nr_failed) { 341*30f3bb09SZhen Lei kfree(stat); 342*30f3bb09SZhen Lei return -ESRCH; 343*30f3bb09SZhen Lei } 344*30f3bb09SZhen Lei 345*30f3bb09SZhen Lei for (i = 0; i < kallsyms_num_syms; i++) { 346*30f3bb09SZhen Lei addr = kallsyms_sym_address(i); 347*30f3bb09SZhen Lei if (!is_ksym_addr(addr)) 348*30f3bb09SZhen Lei continue; 349*30f3bb09SZhen Lei 350*30f3bb09SZhen Lei ret = lookup_symbol_name(addr, namebuf); 351*30f3bb09SZhen Lei if (unlikely(ret)) { 352*30f3bb09SZhen Lei namebuf[0] = 0; 353*30f3bb09SZhen Lei goto failed; 354*30f3bb09SZhen Lei } 355*30f3bb09SZhen Lei 356*30f3bb09SZhen Lei /* 357*30f3bb09SZhen Lei * The first '.' may be the initial letter, in which case the 358*30f3bb09SZhen Lei * entire symbol name will be truncated to an empty string in 359*30f3bb09SZhen Lei * cleanup_symbol_name(). Do not test these symbols. 360*30f3bb09SZhen Lei * 361*30f3bb09SZhen Lei * For example: 362*30f3bb09SZhen Lei * cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head 363*30f3bb09SZhen Lei * .E_read_words 364*30f3bb09SZhen Lei * .E_leading_bytes 365*30f3bb09SZhen Lei * .E_trailing_bytes 366*30f3bb09SZhen Lei * .E_write_words 367*30f3bb09SZhen Lei * .E_copy 368*30f3bb09SZhen Lei * .str.292.llvm.12122243386960820698 369*30f3bb09SZhen Lei * .str.24.llvm.12122243386960820698 370*30f3bb09SZhen Lei * .str.29.llvm.12122243386960820698 371*30f3bb09SZhen Lei * .str.75.llvm.12122243386960820698 372*30f3bb09SZhen Lei * .str.99.llvm.12122243386960820698 373*30f3bb09SZhen Lei */ 374*30f3bb09SZhen Lei if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0]) 375*30f3bb09SZhen Lei continue; 376*30f3bb09SZhen Lei 377*30f3bb09SZhen Lei lookup_addr = kallsyms_lookup_name(namebuf); 378*30f3bb09SZhen Lei 379*30f3bb09SZhen Lei memset(stat, 0, sizeof(*stat)); 380*30f3bb09SZhen Lei stat->max = INT_MAX; 381*30f3bb09SZhen Lei kallsyms_on_each_match_symbol(match_symbol, namebuf, stat); 382*30f3bb09SZhen Lei 383*30f3bb09SZhen Lei /* 384*30f3bb09SZhen Lei * kallsyms_on_each_symbol() is too slow, randomly select some 385*30f3bb09SZhen Lei * symbols for test. 386*30f3bb09SZhen Lei */ 387*30f3bb09SZhen Lei if (i >= next) { 388*30f3bb09SZhen Lei memset(stat2, 0, sizeof(*stat2)); 389*30f3bb09SZhen Lei stat2->max = INT_MAX; 390*30f3bb09SZhen Lei stat2->name = namebuf; 391*30f3bb09SZhen Lei kallsyms_on_each_symbol(find_symbol, stat2); 392*30f3bb09SZhen Lei 393*30f3bb09SZhen Lei /* 394*30f3bb09SZhen Lei * kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol() 395*30f3bb09SZhen Lei * need to get the same traversal result. 396*30f3bb09SZhen Lei */ 397*30f3bb09SZhen Lei if (stat->addr != stat2->addr || 398*30f3bb09SZhen Lei stat->real_cnt != stat2->real_cnt || 399*30f3bb09SZhen Lei memcmp(stat->addrs, stat2->addrs, 400*30f3bb09SZhen Lei stat->save_cnt * sizeof(stat->addrs[0]))) 401*30f3bb09SZhen Lei goto failed; 402*30f3bb09SZhen Lei 403*30f3bb09SZhen Lei /* 404*30f3bb09SZhen Lei * The average of random increments is 128, that is, one of 405*30f3bb09SZhen Lei * them is tested every 128 symbols. 406*30f3bb09SZhen Lei */ 407*30f3bb09SZhen Lei get_random_bytes(&rand, sizeof(rand)); 408*30f3bb09SZhen Lei next = i + (rand & 0xff) + 1; 409*30f3bb09SZhen Lei } 410*30f3bb09SZhen Lei 411*30f3bb09SZhen Lei /* Need to be found at least once */ 412*30f3bb09SZhen Lei if (!stat->real_cnt) 413*30f3bb09SZhen Lei goto failed; 414*30f3bb09SZhen Lei 415*30f3bb09SZhen Lei /* 416*30f3bb09SZhen Lei * kallsyms_lookup_name() returns the address of the first 417*30f3bb09SZhen Lei * symbol found and cannot be NULL. 418*30f3bb09SZhen Lei */ 419*30f3bb09SZhen Lei if (!lookup_addr || lookup_addr != stat->addrs[0]) 420*30f3bb09SZhen Lei goto failed; 421*30f3bb09SZhen Lei 422*30f3bb09SZhen Lei /* 423*30f3bb09SZhen Lei * If the addresses of all matching symbols are recorded, the 424*30f3bb09SZhen Lei * target address needs to be exist. 425*30f3bb09SZhen Lei */ 426*30f3bb09SZhen Lei if (stat->real_cnt <= MAX_NUM_OF_RECORDS) { 427*30f3bb09SZhen Lei for (j = 0; j < stat->save_cnt; j++) { 428*30f3bb09SZhen Lei if (stat->addrs[j] == addr) 429*30f3bb09SZhen Lei break; 430*30f3bb09SZhen Lei } 431*30f3bb09SZhen Lei 432*30f3bb09SZhen Lei if (j == stat->save_cnt) 433*30f3bb09SZhen Lei goto failed; 434*30f3bb09SZhen Lei } 435*30f3bb09SZhen Lei } 436*30f3bb09SZhen Lei 437*30f3bb09SZhen Lei kfree(stat); 438*30f3bb09SZhen Lei 439*30f3bb09SZhen Lei return 0; 440*30f3bb09SZhen Lei 441*30f3bb09SZhen Lei failed: 442*30f3bb09SZhen Lei pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr); 443*30f3bb09SZhen Lei kfree(stat); 444*30f3bb09SZhen Lei return -ESRCH; 445*30f3bb09SZhen Lei } 446*30f3bb09SZhen Lei 447*30f3bb09SZhen Lei static int test_entry(void *p) 448*30f3bb09SZhen Lei { 449*30f3bb09SZhen Lei int ret; 450*30f3bb09SZhen Lei 451*30f3bb09SZhen Lei do { 452*30f3bb09SZhen Lei schedule_timeout(5 * HZ); 453*30f3bb09SZhen Lei } while (system_state != SYSTEM_RUNNING); 454*30f3bb09SZhen Lei 455*30f3bb09SZhen Lei pr_info("start\n"); 456*30f3bb09SZhen Lei ret = test_kallsyms_basic_function(); 457*30f3bb09SZhen Lei if (ret) { 458*30f3bb09SZhen Lei pr_info("abort\n"); 459*30f3bb09SZhen Lei return 0; 460*30f3bb09SZhen Lei } 461*30f3bb09SZhen Lei 462*30f3bb09SZhen Lei test_kallsyms_compression_ratio(); 463*30f3bb09SZhen Lei test_perf_kallsyms_lookup_name(); 464*30f3bb09SZhen Lei test_perf_kallsyms_on_each_symbol(); 465*30f3bb09SZhen Lei test_perf_kallsyms_on_each_match_symbol(); 466*30f3bb09SZhen Lei pr_info("finish\n"); 467*30f3bb09SZhen Lei 468*30f3bb09SZhen Lei return 0; 469*30f3bb09SZhen Lei } 470*30f3bb09SZhen Lei 471*30f3bb09SZhen Lei static int __init kallsyms_test_init(void) 472*30f3bb09SZhen Lei { 473*30f3bb09SZhen Lei struct task_struct *t; 474*30f3bb09SZhen Lei 475*30f3bb09SZhen Lei t = kthread_create(test_entry, NULL, "kallsyms_test"); 476*30f3bb09SZhen Lei if (IS_ERR(t)) { 477*30f3bb09SZhen Lei pr_info("Create kallsyms selftest task failed\n"); 478*30f3bb09SZhen Lei return PTR_ERR(t); 479*30f3bb09SZhen Lei } 480*30f3bb09SZhen Lei kthread_bind(t, 0); 481*30f3bb09SZhen Lei wake_up_process(t); 482*30f3bb09SZhen Lei 483*30f3bb09SZhen Lei return 0; 484*30f3bb09SZhen Lei } 485*30f3bb09SZhen Lei late_initcall(kallsyms_test_init); 486