kallsyms_selftest.c 10 KB

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