[v7,11/11] kallsyms: Add self-test facility
Commit Message
Added test cases for basic functions and performance of functions
kallsyms_lookup_name(), kallsyms_on_each_symbol() and
kallsyms_on_each_match_symbol(). It also calculates the compression rate
of the kallsyms compression algorithm for the current symbol set.
The basic functions test begins by testing a set of symbols whose address
values are known. Then, traverse all symbol addresses and find the
corresponding symbol name based on the address. It's impossible to
determine whether these addresses are correct, but we can use the above
three functions along with the addresses to test each other. Due to the
traversal operation of kallsyms_on_each_symbol() is too slow, only 60
symbols can be tested in one second, so let it test on average once
every 128 symbols. The other two functions validate all symbols.
If the basic functions test is passed, print only performance test
results. If the test fails, print error information, but do not perform
subsequent performance tests.
Start self-test automatically after system startup if
CONFIG_KALLSYMS_SELFTEST=y.
Example of output content: (prefix 'kallsyms_selftest:' is omitted)
start
---------------------------------------------------------
| nr_symbols | compressed size | original size | ratio(%) |
|---------------------------------------------------------|
| 174099 | 1960154 | 3750756 | 52.26 |
---------------------------------------------------------
kallsyms_lookup_name() looked up 174099 symbols
The time spent on each symbol is (ns): min=5250, max=726560, avg=302132
kallsyms_on_each_symbol() traverse all: 16659500 ns
kallsyms_on_each_match_symbol() traverse all: 557400 ns
finish
Signed-off-by: Zhen Lei <thunder.leizhen@huawei.com>
---
include/linux/kallsyms.h | 1 +
init/Kconfig | 13 ++
kernel/Makefile | 1 +
kernel/kallsyms.c | 2 +-
kernel/kallsyms_selftest.c | 464 +++++++++++++++++++++++++++++++++++++
5 files changed, 480 insertions(+), 1 deletion(-)
create mode 100644 kernel/kallsyms_selftest.c
Comments
Hi Zhen,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on masahiroy-kbuild/for-next]
[also build test WARNING on linus/master v6.1-rc1 next-20221018]
[cannot apply to mcgrof/modules-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
base: https://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git for-next
patch link: https://lore.kernel.org/r/20221017064950.2038-12-thunder.leizhen%40huawei.com
patch subject: [PATCH v7 11/11] kallsyms: Add self-test facility
config: sh-allmodconfig
compiler: sh4-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
git checkout 3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>):
kernel/kallsyms_selftest.c:67:5: warning: no previous prototype for 'test_func' [-Wmissing-prototypes]
67 | int test_func(void)
| ^~~~~~~~~
kernel/kallsyms_selftest.c:72:12: warning: no previous prototype for 'test_func_weak' [-Wmissing-prototypes]
72 | __weak int test_func_weak(void)
| ^~~~~~~~~~~~~~
kernel/kallsyms_selftest.c: In function 'test_kallsyms_basic_function':
>> kernel/kallsyms_selftest.c:424:1: warning: the frame size of 1124 bytes is larger than 1024 bytes [-Wframe-larger-than=]
424 | }
| ^
vim +424 kernel/kallsyms_selftest.c
275
276 static int test_kallsyms_basic_function(void)
277 {
278 int i, j, ret;
279 int next = 0, nr_failed = 0;
280 char *prefix;
281 unsigned short rand;
282 unsigned long addr, lookup_addr;
283 char namebuf[KSYM_NAME_LEN];
284 struct test_stat stat, stat2;
285
286 prefix = "kallsyms_lookup_name() for";
287 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
288 addr = kallsyms_lookup_name(test_items[i].name);
289 if (addr != test_items[i].addr) {
290 nr_failed++;
291 pr_info("%s %s failed: addr=%lx, expect %lx\n",
292 prefix, test_items[i].name, addr, test_items[i].addr);
293 }
294 }
295
296 prefix = "kallsyms_on_each_symbol() for";
297 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
298 memset(&stat, 0, sizeof(stat));
299 stat.max = INT_MAX;
300 stat.name = test_items[i].name;
301 kallsyms_on_each_symbol(find_symbol, &stat);
302 if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
303 nr_failed++;
304 pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
305 prefix, test_items[i].name,
306 stat.real_cnt, stat.addr, test_items[i].addr);
307 }
308 }
309
310 prefix = "kallsyms_on_each_match_symbol() for";
311 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
312 memset(&stat, 0, sizeof(stat));
313 stat.max = INT_MAX;
314 stat.name = test_items[i].name;
315 kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, &stat);
316 if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
317 nr_failed++;
318 pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
319 prefix, test_items[i].name,
320 stat.real_cnt, stat.addr, test_items[i].addr);
321 }
322 }
323
324 if (nr_failed)
325 return -ESRCH;
326
327 for (i = 0; i < kallsyms_num_syms; i++) {
328 addr = kallsyms_sym_address(i);
329 if (!is_ksym_addr(addr))
330 continue;
331
332 ret = lookup_symbol_name(addr, namebuf);
333 if (unlikely(ret)) {
334 namebuf[0] = 0;
335 goto failed;
336 }
337
338 /*
339 * The first '.' may be the initial letter, in which case the
340 * entire symbol name will be truncated to an empty string in
341 * cleanup_symbol_name(). Do not test these symbols.
342 *
343 * For example:
344 * cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head
345 * .E_read_words
346 * .E_leading_bytes
347 * .E_trailing_bytes
348 * .E_write_words
349 * .E_copy
350 * .str.292.llvm.12122243386960820698
351 * .str.24.llvm.12122243386960820698
352 * .str.29.llvm.12122243386960820698
353 * .str.75.llvm.12122243386960820698
354 * .str.99.llvm.12122243386960820698
355 */
356 if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0])
357 continue;
358
359 lookup_addr = kallsyms_lookup_name(namebuf);
360
361 memset(&stat, 0, sizeof(stat));
362 stat.max = INT_MAX;
363 kallsyms_on_each_match_symbol(match_symbol, namebuf, &stat);
364
365 /*
366 * kallsyms_on_each_symbol() is too slow, randomly select some
367 * symbols for test.
368 */
369 if (i >= next) {
370 memset(&stat2, 0, sizeof(stat2));
371 stat2.max = INT_MAX;
372 stat2.name = namebuf;
373 kallsyms_on_each_symbol(find_symbol, &stat2);
374
375 /*
376 * kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol()
377 * need to get the same traversal result.
378 */
379 if (stat.addr != stat2.addr ||
380 stat.real_cnt != stat2.real_cnt ||
381 memcmp(stat.addrs, stat2.addrs,
382 stat.save_cnt * sizeof(stat.addrs[0])))
383 goto failed;
384
385 /*
386 * The average of random increments is 128, that is, one of
387 * them is tested every 128 symbols.
388 */
389 get_random_bytes(&rand, sizeof(rand));
390 next = i + (rand & 0xff) + 1;
391 }
392
393 /* Need to be found at least once */
394 if (!stat.real_cnt)
395 goto failed;
396
397 /*
398 * kallsyms_lookup_name() returns the address of the first
399 * symbol found and cannot be NULL.
400 */
401 if (!lookup_addr || lookup_addr != stat.addrs[0])
402 goto failed;
403
404 /*
405 * If the addresses of all matching symbols are recorded, the
406 * target address needs to be exist.
407 */
408 if (stat.real_cnt <= MAX_NUM_OF_RECORDS) {
409 for (j = 0; j < stat.save_cnt; j++) {
410 if (stat.addrs[j] == addr)
411 break;
412 }
413
414 if (j == stat.save_cnt)
415 goto failed;
416 }
417 }
418
419 return 0;
420
421 failed:
422 pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr);
423 return -ESRCH;
> 424 }
425
On 2022/10/18 16:21, kernel test robot wrote:
> Hi Zhen,
>
> Thank you for the patch! Perhaps something to improve:
>
> [auto build test WARNING on masahiroy-kbuild/for-next]
> [also build test WARNING on linus/master v6.1-rc1 next-20221018]
> [cannot apply to mcgrof/modules-next]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
> base: https://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git for-next
> patch link: https://lore.kernel.org/r/20221017064950.2038-12-thunder.leizhen%40huawei.com
> patch subject: [PATCH v7 11/11] kallsyms: Add self-test facility
> config: sh-allmodconfig
> compiler: sh4-linux-gcc (GCC) 12.1.0
> reproduce (this is a W=1 build):
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> # https://github.com/intel-lab-lkp/linux/commit/3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
> git remote add linux-review https://github.com/intel-lab-lkp/linux
> git fetch --no-tags linux-review Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
> git checkout 3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
> # save the config file
> mkdir build_dir && cp config build_dir/.config
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=sh SHELL=/bin/bash
>
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <lkp@intel.com>
>
> All warnings (new ones prefixed by >>):
>
> kernel/kallsyms_selftest.c:67:5: warning: no previous prototype for 'test_func' [-Wmissing-prototypes]
> 67 | int test_func(void)
> | ^~~~~~~~~
> kernel/kallsyms_selftest.c:72:12: warning: no previous prototype for 'test_func_weak' [-Wmissing-prototypes]
> 72 | __weak int test_func_weak(void)
> | ^~~~~~~~~~~~~~
> kernel/kallsyms_selftest.c: In function 'test_kallsyms_basic_function':
>>> kernel/kallsyms_selftest.c:424:1: warning: the frame size of 1124 bytes is larger than 1024 bytes [-Wframe-larger-than=]
> 424 | }
> | ^
OK, thanks. These warnings are minor. I will fix them in the next version after collecting review comments.
>
>
> vim +424 kernel/kallsyms_selftest.c
>
> 275
> 276 static int test_kallsyms_basic_function(void)
> 277 {
> 278 int i, j, ret;
> 279 int next = 0, nr_failed = 0;
> 280 char *prefix;
> 281 unsigned short rand;
> 282 unsigned long addr, lookup_addr;
> 283 char namebuf[KSYM_NAME_LEN];
> 284 struct test_stat stat, stat2;
> 285
> 286 prefix = "kallsyms_lookup_name() for";
> 287 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
> 288 addr = kallsyms_lookup_name(test_items[i].name);
> 289 if (addr != test_items[i].addr) {
> 290 nr_failed++;
> 291 pr_info("%s %s failed: addr=%lx, expect %lx\n",
> 292 prefix, test_items[i].name, addr, test_items[i].addr);
> 293 }
> 294 }
> 295
> 296 prefix = "kallsyms_on_each_symbol() for";
> 297 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
> 298 memset(&stat, 0, sizeof(stat));
> 299 stat.max = INT_MAX;
> 300 stat.name = test_items[i].name;
> 301 kallsyms_on_each_symbol(find_symbol, &stat);
> 302 if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
> 303 nr_failed++;
> 304 pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
> 305 prefix, test_items[i].name,
> 306 stat.real_cnt, stat.addr, test_items[i].addr);
> 307 }
> 308 }
> 309
> 310 prefix = "kallsyms_on_each_match_symbol() for";
> 311 for (i = 0; i < ARRAY_SIZE(test_items); i++) {
> 312 memset(&stat, 0, sizeof(stat));
> 313 stat.max = INT_MAX;
> 314 stat.name = test_items[i].name;
> 315 kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, &stat);
> 316 if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
> 317 nr_failed++;
> 318 pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
> 319 prefix, test_items[i].name,
> 320 stat.real_cnt, stat.addr, test_items[i].addr);
> 321 }
> 322 }
> 323
> 324 if (nr_failed)
> 325 return -ESRCH;
> 326
> 327 for (i = 0; i < kallsyms_num_syms; i++) {
> 328 addr = kallsyms_sym_address(i);
> 329 if (!is_ksym_addr(addr))
> 330 continue;
> 331
> 332 ret = lookup_symbol_name(addr, namebuf);
> 333 if (unlikely(ret)) {
> 334 namebuf[0] = 0;
> 335 goto failed;
> 336 }
> 337
> 338 /*
> 339 * The first '.' may be the initial letter, in which case the
> 340 * entire symbol name will be truncated to an empty string in
> 341 * cleanup_symbol_name(). Do not test these symbols.
> 342 *
> 343 * For example:
> 344 * cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head
> 345 * .E_read_words
> 346 * .E_leading_bytes
> 347 * .E_trailing_bytes
> 348 * .E_write_words
> 349 * .E_copy
> 350 * .str.292.llvm.12122243386960820698
> 351 * .str.24.llvm.12122243386960820698
> 352 * .str.29.llvm.12122243386960820698
> 353 * .str.75.llvm.12122243386960820698
> 354 * .str.99.llvm.12122243386960820698
> 355 */
> 356 if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0])
> 357 continue;
> 358
> 359 lookup_addr = kallsyms_lookup_name(namebuf);
> 360
> 361 memset(&stat, 0, sizeof(stat));
> 362 stat.max = INT_MAX;
> 363 kallsyms_on_each_match_symbol(match_symbol, namebuf, &stat);
> 364
> 365 /*
> 366 * kallsyms_on_each_symbol() is too slow, randomly select some
> 367 * symbols for test.
> 368 */
> 369 if (i >= next) {
> 370 memset(&stat2, 0, sizeof(stat2));
> 371 stat2.max = INT_MAX;
> 372 stat2.name = namebuf;
> 373 kallsyms_on_each_symbol(find_symbol, &stat2);
> 374
> 375 /*
> 376 * kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol()
> 377 * need to get the same traversal result.
> 378 */
> 379 if (stat.addr != stat2.addr ||
> 380 stat.real_cnt != stat2.real_cnt ||
> 381 memcmp(stat.addrs, stat2.addrs,
> 382 stat.save_cnt * sizeof(stat.addrs[0])))
> 383 goto failed;
> 384
> 385 /*
> 386 * The average of random increments is 128, that is, one of
> 387 * them is tested every 128 symbols.
> 388 */
> 389 get_random_bytes(&rand, sizeof(rand));
> 390 next = i + (rand & 0xff) + 1;
> 391 }
> 392
> 393 /* Need to be found at least once */
> 394 if (!stat.real_cnt)
> 395 goto failed;
> 396
> 397 /*
> 398 * kallsyms_lookup_name() returns the address of the first
> 399 * symbol found and cannot be NULL.
> 400 */
> 401 if (!lookup_addr || lookup_addr != stat.addrs[0])
> 402 goto failed;
> 403
> 404 /*
> 405 * If the addresses of all matching symbols are recorded, the
> 406 * target address needs to be exist.
> 407 */
> 408 if (stat.real_cnt <= MAX_NUM_OF_RECORDS) {
> 409 for (j = 0; j < stat.save_cnt; j++) {
> 410 if (stat.addrs[j] == addr)
> 411 break;
> 412 }
> 413
> 414 if (j == stat.save_cnt)
> 415 goto failed;
> 416 }
> 417 }
> 418
> 419 return 0;
> 420
> 421 failed:
> 422 pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr);
> 423 return -ESRCH;
> > 424 }
> 425
>
Hi Zhen,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on masahiroy-kbuild/for-next]
[also build test ERROR on linus/master v6.1-rc1 next-20221018]
[cannot apply to mcgrof/modules-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
base: https://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git for-next
patch link: https://lore.kernel.org/r/20221017064950.2038-12-thunder.leizhen%40huawei.com
patch subject: [PATCH v7 11/11] kallsyms: Add self-test facility
config: mips-allyesconfig
compiler: mips-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
git checkout 3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
arch/mips/kernel/head.o: in function `kernel_entry':
(.ref.text+0xac): relocation truncated to fit: R_MIPS_26 against `start_kernel'
init/main.o: in function `set_reset_devices':
main.c:(.init.text+0x20): relocation truncated to fit: R_MIPS_26 against `_mcount'
main.c:(.init.text+0x30): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
init/main.o: in function `debug_kernel':
main.c:(.init.text+0xa4): relocation truncated to fit: R_MIPS_26 against `_mcount'
main.c:(.init.text+0xb4): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
init/main.o: in function `quiet_kernel':
main.c:(.init.text+0x128): relocation truncated to fit: R_MIPS_26 against `_mcount'
main.c:(.init.text+0x138): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
init/main.o: in function `warn_bootconfig':
main.c:(.init.text+0x1ac): relocation truncated to fit: R_MIPS_26 against `_mcount'
main.c:(.init.text+0x1bc): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
init/main.o: in function `init_setup':
main.c:(.init.text+0x234): relocation truncated to fit: R_MIPS_26 against `_mcount'
main.c:(.init.text+0x254): additional relocation overflows omitted from the output
mips-linux-ld: kernel/kallsyms_selftest.o: in function `test_perf_kallsyms_lookup_name':
>> kallsyms_selftest.c:(.text.unlikely.test_perf_kallsyms_lookup_name+0x100): undefined reference to `__udivdi3'
On 2022/10/18 17:32, kernel test robot wrote:
> Hi Zhen,
>
> Thank you for the patch! Yet something to improve:
>
> [auto build test ERROR on masahiroy-kbuild/for-next]
> [also build test ERROR on linus/master v6.1-rc1 next-20221018]
> [cannot apply to mcgrof/modules-next]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
> base: https://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git for-next
> patch link: https://lore.kernel.org/r/20221017064950.2038-12-thunder.leizhen%40huawei.com
> patch subject: [PATCH v7 11/11] kallsyms: Add self-test facility
> config: mips-allyesconfig
> compiler: mips-linux-gcc (GCC) 12.1.0
> reproduce (this is a W=1 build):
> wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
> chmod +x ~/bin/make.cross
> # https://github.com/intel-lab-lkp/linux/commit/3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
> git remote add linux-review https://github.com/intel-lab-lkp/linux
> git fetch --no-tags linux-review Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
> git checkout 3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
> # save the config file
> mkdir build_dir && cp config build_dir/.config
> COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash
>
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <lkp@intel.com>>
> All errors (new ones prefixed by >>):
>
> arch/mips/kernel/head.o: in function `kernel_entry':
> (.ref.text+0xac): relocation truncated to fit: R_MIPS_26 against `start_kernel'
> init/main.o: in function `set_reset_devices':
> main.c:(.init.text+0x20): relocation truncated to fit: R_MIPS_26 against `_mcount'
> main.c:(.init.text+0x30): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
> init/main.o: in function `debug_kernel':
> main.c:(.init.text+0xa4): relocation truncated to fit: R_MIPS_26 against `_mcount'
> main.c:(.init.text+0xb4): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
> init/main.o: in function `quiet_kernel':
> main.c:(.init.text+0x128): relocation truncated to fit: R_MIPS_26 against `_mcount'
> main.c:(.init.text+0x138): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
> init/main.o: in function `warn_bootconfig':
> main.c:(.init.text+0x1ac): relocation truncated to fit: R_MIPS_26 against `_mcount'
> main.c:(.init.text+0x1bc): relocation truncated to fit: R_MIPS_26 against `__sanitizer_cov_trace_pc'
> init/main.o: in function `init_setup':
> main.c:(.init.text+0x234): relocation truncated to fit: R_MIPS_26 against `_mcount'
> main.c:(.init.text+0x254): additional relocation overflows omitted from the output
> mips-linux-ld: kernel/kallsyms_selftest.o: in function `test_perf_kallsyms_lookup_name':
>>> kallsyms_selftest.c:(.text.unlikely.test_perf_kallsyms_lookup_name+0x100): undefined reference to `__udivdi3'
OK, thanks. I will fix it in the next version.
>
Hi Zhen,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on masahiroy-kbuild/for-next]
[also build test ERROR on linus/master v6.1-rc1 next-20221020]
[cannot apply to mcgrof/modules-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
base: https://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git for-next
patch link: https://lore.kernel.org/r/20221017064950.2038-12-thunder.leizhen%40huawei.com
patch subject: [PATCH v7 11/11] kallsyms: Add self-test facility
config: i386-allyesconfig
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
# https://github.com/intel-lab-lkp/linux/commit/3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Zhen-Lei/kallsyms-Optimizes-the-performance-of-lookup-symbols/20221017-145455
git checkout 3f5fc7fa1f657df865ef14b2d24f837a7cc079c9
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
ld: kernel/kallsyms_selftest.o: in function `test_perf_kallsyms_lookup_name':
>> kallsyms_selftest.c:(.text.unlikely+0x5db): undefined reference to `__udivdi3'
@@ -66,6 +66,7 @@ static inline void *dereference_symbol_descriptor(void *ptr)
}
#ifdef CONFIG_KALLSYMS
+unsigned long kallsyms_sym_address(int idx);
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, unsigned long),
void *data);
int kallsyms_on_each_match_symbol(int (*fn)(void *, unsigned long),
@@ -1723,6 +1723,19 @@ config KALLSYMS
symbolic stack backtraces. This increases the size of the kernel
somewhat, as all symbols have to be loaded into the kernel image.
+config KALLSYMS_SELFTEST
+ bool "Test the basic functions and performance of kallsyms"
+ depends on KALLSYMS
+ default n
+ help
+ Test the basic functions and performance of some interfaces, such as
+ kallsyms_lookup_name. It also calculates the compression rate of the
+ kallsyms compression algorithm for the current symbol set.
+
+ Start self-test automatically after system startup. Suggest executing
+ "dmesg | grep kallsyms_selftest" to collect test results. "finish" is
+ displayed in the last line, indicating that the test is complete.
+
config KALLSYMS_ALL
bool "Include all symbols in kallsyms"
depends on DEBUG_KERNEL && KALLSYMS
@@ -69,6 +69,7 @@ endif
obj-$(CONFIG_UID16) += uid16.o
obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o
obj-$(CONFIG_KALLSYMS) += kallsyms.o
+obj-$(CONFIG_KALLSYMS_SELFTEST) += kallsyms_selftest.o
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_CRASH_CORE) += crash_core.o
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
@@ -226,7 +226,7 @@ static unsigned int get_symbol_offset(unsigned long pos)
return name - kallsyms_names;
}
-static unsigned long kallsyms_sym_address(int idx)
+unsigned long kallsyms_sym_address(int idx)
{
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
return kallsyms_addresses[idx];
new file mode 100644
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Test the function and performance of kallsyms
+ *
+ * Copyright (C) Huawei Technologies Co., Ltd., 2022
+ *
+ * Authors: Zhen Lei <thunder.leizhen@huawei.com> Huawei
+ */
+
+#define pr_fmt(fmt) "kallsyms_selftest: " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/random.h>
+#include <linux/sched/clock.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+
+#include "kallsyms_internal.h"
+
+
+#define MAX_NUM_OF_RECORDS 64
+
+struct test_stat {
+ int min;
+ int max;
+ int save_cnt;
+ int real_cnt;
+ int perf;
+ u64 sum;
+ char *name;
+ unsigned long addr;
+ unsigned long addrs[MAX_NUM_OF_RECORDS];
+};
+
+struct test_item {
+ char *name;
+ unsigned long addr;
+};
+
+#define ITEM_FUNC(s) \
+ { \
+ .name = #s, \
+ .addr = (unsigned long)s, \
+ }
+
+#define ITEM_DATA(s) \
+ { \
+ .name = #s, \
+ .addr = (unsigned long)&s, \
+ }
+
+static int test_var_bss_static;
+static int test_var_data_static = 1;
+int test_var_bss;
+int test_var_data = 1;
+
+static int test_func_static(void)
+{
+ test_var_bss_static++;
+ test_var_data_static++;
+
+ return 0;
+}
+
+int test_func(void)
+{
+ return test_func_static();
+}
+
+__weak int test_func_weak(void)
+{
+ test_var_bss++;
+ test_var_data++;
+ return 0;
+}
+
+static struct test_item test_items[] = {
+ ITEM_FUNC(test_func_static),
+ ITEM_FUNC(test_func),
+ ITEM_FUNC(test_func_weak),
+ ITEM_FUNC(vmalloc),
+ ITEM_FUNC(vfree),
+#ifdef CONFIG_KALLSYMS_ALL
+ ITEM_DATA(test_var_bss_static),
+ ITEM_DATA(test_var_data_static),
+ ITEM_DATA(test_var_bss),
+ ITEM_DATA(test_var_data),
+ ITEM_DATA(vmap_area_list),
+#endif
+};
+
+static char stub_name[KSYM_NAME_LEN];
+
+static int stat_symbol_len(void *data, const char *name, unsigned long addr)
+{
+ *(u32 *)data += strlen(name);
+
+ return 0;
+}
+
+static void test_kallsyms_compression_ratio(void)
+{
+ int i;
+ const u8 *name;
+ u32 pos;
+ u32 ratio, total_size, total_len = 0;
+
+ kallsyms_on_each_symbol(stat_symbol_len, &total_len);
+
+ /*
+ * A symbol name cannot start with a number. This stub name helps us
+ * traverse the entire symbol table without finding a match. It's used
+ * for subsequent performance tests, and its length is the average
+ * length of all symbol names.
+ */
+ memset(stub_name, '4', sizeof(stub_name));
+ pos = total_len / kallsyms_num_syms;
+ stub_name[pos] = 0;
+
+ pos = kallsyms_num_syms - 1;
+ name = &kallsyms_names[kallsyms_markers[pos >> 8]];
+ for (i = 0; i <= (pos & 0xff); i++)
+ name = name + (*name) + 1;
+
+ /*
+ * 1. The length fields is not counted
+ * 2. The memory occupied by array kallsyms_token_table[] and
+ * kallsyms_token_index[] needs to be counted.
+ */
+ total_size = (name - kallsyms_names) - kallsyms_num_syms;
+ pos = kallsyms_token_index[0xff];
+ total_size += pos + strlen(&kallsyms_token_table[pos]) + 1;
+ total_size += 0x100 * sizeof(u16);
+
+ pr_info(" ---------------------------------------------------------\n");
+ pr_info("| nr_symbols | compressed size | original size | ratio(%%) |\n");
+ pr_info("|---------------------------------------------------------|\n");
+ ratio = (u32)div_u64(10000ULL * total_size, total_len);
+ pr_info("| %10d | %10d | %10d | %2d.%-2d |\n",
+ kallsyms_num_syms, total_size, total_len, ratio / 100, ratio % 100);
+ pr_info(" ---------------------------------------------------------\n");
+}
+
+static int lookup_name(void *data, const char *name, unsigned long addr)
+{
+ u64 t0, t1, t;
+ unsigned long flags;
+ struct test_stat *stat = (struct test_stat *)data;
+
+ local_irq_save(flags);
+ t0 = sched_clock();
+ (void)kallsyms_lookup_name(name);
+ t1 = sched_clock();
+ local_irq_restore(flags);
+
+ t = t1 - t0;
+ if (t < stat->min)
+ stat->min = t;
+
+ if (t > stat->max)
+ stat->max = t;
+
+ stat->real_cnt++;
+ stat->sum += t;
+
+ return 0;
+}
+
+static void test_perf_kallsyms_lookup_name(void)
+{
+ struct test_stat stat;
+
+ memset(&stat, 0, sizeof(stat));
+ stat.min = INT_MAX;
+ kallsyms_on_each_symbol(lookup_name, &stat);
+ pr_info("kallsyms_lookup_name() looked up %d symbols\n", stat.real_cnt);
+ pr_info("The time spent on each symbol is (ns): min=%d, max=%d, avg=%lld\n",
+ stat.min, stat.max, stat.sum / stat.real_cnt);
+}
+
+static bool match_cleanup_name(const char *s, const char *name)
+{
+ char *p;
+ int len;
+
+ if (!IS_ENABLED(CONFIG_LTO_CLANG))
+ return false;
+
+ p = strchr(s, '.');
+ if (!p)
+ return false;
+
+ len = strlen(name);
+ if (p - s != len)
+ return false;
+
+ return !strncmp(s, name, len);
+}
+
+static int find_symbol(void *data, const char *name, unsigned long addr)
+{
+ struct test_stat *stat = (struct test_stat *)data;
+
+ if (strcmp(name, stat->name) == 0 ||
+ (!stat->perf && match_cleanup_name(name, stat->name))) {
+ stat->real_cnt++;
+ stat->addr = addr;
+
+ if (stat->save_cnt < MAX_NUM_OF_RECORDS) {
+ stat->addrs[stat->save_cnt] = addr;
+ stat->save_cnt++;
+ }
+
+ if (stat->real_cnt == stat->max)
+ return 1;
+ }
+
+ return 0;
+}
+
+static void test_perf_kallsyms_on_each_symbol(void)
+{
+ u64 t0, t1;
+ unsigned long flags;
+ struct test_stat stat;
+
+ memset(&stat, 0, sizeof(stat));
+ stat.max = INT_MAX;
+ stat.name = stub_name;
+ stat.perf = 1;
+ local_irq_save(flags);
+ t0 = sched_clock();
+ kallsyms_on_each_symbol(find_symbol, &stat);
+ t1 = sched_clock();
+ local_irq_restore(flags);
+ pr_info("kallsyms_on_each_symbol() traverse all: %lld ns\n", t1 - t0);
+}
+
+static int match_symbol(void *data, unsigned long addr)
+{
+ struct test_stat *stat = (struct test_stat *)data;
+
+ stat->real_cnt++;
+ stat->addr = addr;
+
+ if (stat->save_cnt < MAX_NUM_OF_RECORDS) {
+ stat->addrs[stat->save_cnt] = addr;
+ stat->save_cnt++;
+ }
+
+ if (stat->real_cnt == stat->max)
+ return 1;
+
+ return 0;
+}
+
+static void test_perf_kallsyms_on_each_match_symbol(void)
+{
+ u64 t0, t1;
+ unsigned long flags;
+ struct test_stat stat;
+
+ memset(&stat, 0, sizeof(stat));
+ stat.max = INT_MAX;
+ stat.name = stub_name;
+ local_irq_save(flags);
+ t0 = sched_clock();
+ kallsyms_on_each_match_symbol(match_symbol, stat.name, &stat);
+ t1 = sched_clock();
+ local_irq_restore(flags);
+ pr_info("kallsyms_on_each_match_symbol() traverse all: %lld ns\n", t1 - t0);
+}
+
+static int test_kallsyms_basic_function(void)
+{
+ int i, j, ret;
+ int next = 0, nr_failed = 0;
+ char *prefix;
+ unsigned short rand;
+ unsigned long addr, lookup_addr;
+ char namebuf[KSYM_NAME_LEN];
+ struct test_stat stat, stat2;
+
+ prefix = "kallsyms_lookup_name() for";
+ for (i = 0; i < ARRAY_SIZE(test_items); i++) {
+ addr = kallsyms_lookup_name(test_items[i].name);
+ if (addr != test_items[i].addr) {
+ nr_failed++;
+ pr_info("%s %s failed: addr=%lx, expect %lx\n",
+ prefix, test_items[i].name, addr, test_items[i].addr);
+ }
+ }
+
+ prefix = "kallsyms_on_each_symbol() for";
+ for (i = 0; i < ARRAY_SIZE(test_items); i++) {
+ memset(&stat, 0, sizeof(stat));
+ stat.max = INT_MAX;
+ stat.name = test_items[i].name;
+ kallsyms_on_each_symbol(find_symbol, &stat);
+ if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
+ nr_failed++;
+ pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
+ prefix, test_items[i].name,
+ stat.real_cnt, stat.addr, test_items[i].addr);
+ }
+ }
+
+ prefix = "kallsyms_on_each_match_symbol() for";
+ for (i = 0; i < ARRAY_SIZE(test_items); i++) {
+ memset(&stat, 0, sizeof(stat));
+ stat.max = INT_MAX;
+ stat.name = test_items[i].name;
+ kallsyms_on_each_match_symbol(match_symbol, test_items[i].name, &stat);
+ if (stat.addr != test_items[i].addr || stat.real_cnt != 1) {
+ nr_failed++;
+ pr_info("%s %s failed: count=%d, addr=%lx, expect %lx\n",
+ prefix, test_items[i].name,
+ stat.real_cnt, stat.addr, test_items[i].addr);
+ }
+ }
+
+ if (nr_failed)
+ return -ESRCH;
+
+ for (i = 0; i < kallsyms_num_syms; i++) {
+ addr = kallsyms_sym_address(i);
+ if (!is_ksym_addr(addr))
+ continue;
+
+ ret = lookup_symbol_name(addr, namebuf);
+ if (unlikely(ret)) {
+ namebuf[0] = 0;
+ goto failed;
+ }
+
+ /*
+ * The first '.' may be the initial letter, in which case the
+ * entire symbol name will be truncated to an empty string in
+ * cleanup_symbol_name(). Do not test these symbols.
+ *
+ * For example:
+ * cat /proc/kallsyms | awk '{print $3}' | grep -E "^\." | head
+ * .E_read_words
+ * .E_leading_bytes
+ * .E_trailing_bytes
+ * .E_write_words
+ * .E_copy
+ * .str.292.llvm.12122243386960820698
+ * .str.24.llvm.12122243386960820698
+ * .str.29.llvm.12122243386960820698
+ * .str.75.llvm.12122243386960820698
+ * .str.99.llvm.12122243386960820698
+ */
+ if (IS_ENABLED(CONFIG_LTO_CLANG) && !namebuf[0])
+ continue;
+
+ lookup_addr = kallsyms_lookup_name(namebuf);
+
+ memset(&stat, 0, sizeof(stat));
+ stat.max = INT_MAX;
+ kallsyms_on_each_match_symbol(match_symbol, namebuf, &stat);
+
+ /*
+ * kallsyms_on_each_symbol() is too slow, randomly select some
+ * symbols for test.
+ */
+ if (i >= next) {
+ memset(&stat2, 0, sizeof(stat2));
+ stat2.max = INT_MAX;
+ stat2.name = namebuf;
+ kallsyms_on_each_symbol(find_symbol, &stat2);
+
+ /*
+ * kallsyms_on_each_symbol() and kallsyms_on_each_match_symbol()
+ * need to get the same traversal result.
+ */
+ if (stat.addr != stat2.addr ||
+ stat.real_cnt != stat2.real_cnt ||
+ memcmp(stat.addrs, stat2.addrs,
+ stat.save_cnt * sizeof(stat.addrs[0])))
+ goto failed;
+
+ /*
+ * The average of random increments is 128, that is, one of
+ * them is tested every 128 symbols.
+ */
+ get_random_bytes(&rand, sizeof(rand));
+ next = i + (rand & 0xff) + 1;
+ }
+
+ /* Need to be found at least once */
+ if (!stat.real_cnt)
+ goto failed;
+
+ /*
+ * kallsyms_lookup_name() returns the address of the first
+ * symbol found and cannot be NULL.
+ */
+ if (!lookup_addr || lookup_addr != stat.addrs[0])
+ goto failed;
+
+ /*
+ * If the addresses of all matching symbols are recorded, the
+ * target address needs to be exist.
+ */
+ if (stat.real_cnt <= MAX_NUM_OF_RECORDS) {
+ for (j = 0; j < stat.save_cnt; j++) {
+ if (stat.addrs[j] == addr)
+ break;
+ }
+
+ if (j == stat.save_cnt)
+ goto failed;
+ }
+ }
+
+ return 0;
+
+failed:
+ pr_info("Test for %dth symbol failed: (%s) addr=%lx", i, namebuf, addr);
+ return -ESRCH;
+}
+
+static int test_entry(void *p)
+{
+ int ret;
+
+ do {
+ schedule_timeout(5 * HZ);
+ } while (system_state != SYSTEM_RUNNING);
+
+ pr_info("start\n");
+ ret = test_kallsyms_basic_function();
+ if (ret) {
+ pr_info("abort\n");
+ return 0;
+ }
+
+ test_kallsyms_compression_ratio();
+ test_perf_kallsyms_lookup_name();
+ test_perf_kallsyms_on_each_symbol();
+ test_perf_kallsyms_on_each_match_symbol();
+ pr_info("finish\n");
+
+ return 0;
+}
+
+static int __init kallsyms_test_init(void)
+{
+ struct task_struct *t;
+
+ t = kthread_create(test_entry, NULL, "kallsyms_test");
+ if (IS_ERR(t)) {
+ pr_info("Create kallsyms selftest task failed\n");
+ return PTR_ERR(t);
+ }
+ kthread_bind(t, 0);
+ wake_up_process(t);
+
+ return 0;
+}
+late_initcall(kallsyms_test_init);