Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kunit: add debugfs /sys/kernel/debug/kunit/<suite>/results display

add debugfs support for displaying kunit test suite results; this is
especially useful for module-loaded tests to allow disentangling of
test result display from other dmesg events. debugfs support is
provided if CONFIG_KUNIT_DEBUGFS=y.

As well as printk()ing messages, we append them to a per-test log.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Reviewed-by: Frank Rowand <frank.rowand@sony.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Alan Maguire and committed by
Shuah Khan
e2219db2 0d5792c9

+322 -41
+46 -8
include/kunit/test.h
··· 81 81 82 82 struct kunit; 83 83 84 + /* Size of log associated with test. */ 85 + #define KUNIT_LOG_SIZE 512 86 + 84 87 /** 85 88 * struct kunit_case - represents an individual test case. 86 89 * ··· 126 123 127 124 /* private: internal use only. */ 128 125 bool success; 126 + char *log; 129 127 }; 128 + 129 + static inline char *kunit_status_to_string(bool status) 130 + { 131 + return status ? "ok" : "not ok"; 132 + } 130 133 131 134 /** 132 135 * KUNIT_CASE - A helper for creating a &struct kunit_case ··· 166 157 int (*init)(struct kunit *test); 167 158 void (*exit)(struct kunit *test); 168 159 struct kunit_case *test_cases; 160 + 161 + /* private - internal use only */ 162 + struct dentry *debugfs; 163 + char *log; 169 164 }; 170 165 171 166 /** ··· 188 175 189 176 /* private: internal use only. */ 190 177 const char *name; /* Read only after initialization! */ 178 + char *log; /* Points at case log after initialization */ 191 179 struct kunit_try_catch try_catch; 192 180 /* 193 181 * success starts as true, and may only be set to false during a ··· 207 193 struct list_head resources; /* Protected by lock. */ 208 194 }; 209 195 210 - void kunit_init_test(struct kunit *test, const char *name); 196 + void kunit_init_test(struct kunit *test, const char *name, char *log); 211 197 212 198 int kunit_run_tests(struct kunit_suite *suite); 199 + 200 + size_t kunit_suite_num_test_cases(struct kunit_suite *suite); 201 + 202 + unsigned int kunit_test_case_num(struct kunit_suite *suite, 203 + struct kunit_case *test_case); 204 + 205 + int __kunit_test_suites_init(struct kunit_suite **suites); 206 + 207 + void __kunit_test_suites_exit(struct kunit_suite **suites); 213 208 214 209 /** 215 210 * kunit_test_suites() - used to register one or more &struct kunit_suite ··· 249 226 static struct kunit_suite *suites[] = { __VA_ARGS__, NULL}; \ 250 227 static int kunit_test_suites_init(void) \ 251 228 { \ 252 - unsigned int i; \ 253 - for (i = 0; suites[i] != NULL; i++) \ 254 - kunit_run_tests(suites[i]); \ 255 - return 0; \ 229 + return __kunit_test_suites_init(suites); \ 256 230 } \ 257 231 late_initcall(kunit_test_suites_init); \ 258 232 static void __exit kunit_test_suites_exit(void) \ 259 233 { \ 260 - return; \ 234 + return __kunit_test_suites_exit(suites); \ 261 235 } \ 262 236 module_exit(kunit_test_suites_exit) 263 237 264 238 #define kunit_test_suite(suite) kunit_test_suites(&suite) 239 + 240 + #define kunit_suite_for_each_test_case(suite, test_case) \ 241 + for (test_case = suite->test_cases; test_case->run_case; test_case++) 242 + 243 + bool kunit_suite_has_succeeded(struct kunit_suite *suite); 265 244 266 245 /* 267 246 * Like kunit_alloc_resource() below, but returns the struct kunit_resource ··· 381 356 382 357 void kunit_cleanup(struct kunit *test); 383 358 384 - #define kunit_printk(lvl, test, fmt, ...) \ 385 - printk(lvl "\t# %s: " fmt, (test)->name, ##__VA_ARGS__) 359 + void kunit_log_append(char *log, const char *fmt, ...); 360 + 361 + /* 362 + * printk and log to per-test or per-suite log buffer. Logging only done 363 + * if CONFIG_KUNIT_DEBUGFS is 'y'; if it is 'n', no log is allocated/used. 364 + */ 365 + #define kunit_log(lvl, test_or_suite, fmt, ...) \ 366 + do { \ 367 + printk(lvl fmt, ##__VA_ARGS__); \ 368 + kunit_log_append((test_or_suite)->log, fmt "\n", \ 369 + ##__VA_ARGS__); \ 370 + } while (0) 371 + 372 + #define kunit_printk(lvl, test, fmt, ...) \ 373 + kunit_log(lvl, test, "\t# %s: " fmt, (test)->name, ##__VA_ARGS__) 386 374 387 375 /** 388 376 * kunit_info() - Prints an INFO level message associated with @test.
+8
lib/kunit/Kconfig
··· 14 14 15 15 if KUNIT 16 16 17 + config KUNIT_DEBUGFS 18 + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" 19 + help 20 + Enable debugfs representation for kunit. Currently this consists 21 + of /sys/kernel/debug/kunit/<test_suite>/results files for each 22 + test suite, which allow users to see results of the last test suite 23 + run that occurred. 24 + 17 25 config KUNIT_TEST 18 26 tristate "KUnit test for KUnit" 19 27 help
+4
lib/kunit/Makefile
··· 5 5 assert.o \ 6 6 try-catch.o 7 7 8 + ifeq ($(CONFIG_KUNIT_DEBUGFS),y) 9 + kunit-objs += debugfs.o 10 + endif 11 + 8 12 obj-$(CONFIG_KUNIT_TEST) += kunit-test.o 9 13 10 14 # string-stream-test compiles built-in only.
+116
lib/kunit/debugfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (c) 2020, Oracle and/or its affiliates. 4 + * Author: Alan Maguire <alan.maguire@oracle.com> 5 + */ 6 + 7 + #include <linux/debugfs.h> 8 + #include <linux/module.h> 9 + 10 + #include <kunit/test.h> 11 + 12 + #include "string-stream.h" 13 + 14 + #define KUNIT_DEBUGFS_ROOT "kunit" 15 + #define KUNIT_DEBUGFS_RESULTS "results" 16 + 17 + /* 18 + * Create a debugfs representation of test suites: 19 + * 20 + * Path Semantics 21 + * /sys/kernel/debug/kunit/<testsuite>/results Show results of last run for 22 + * testsuite 23 + * 24 + */ 25 + 26 + static struct dentry *debugfs_rootdir; 27 + 28 + void kunit_debugfs_cleanup(void) 29 + { 30 + debugfs_remove_recursive(debugfs_rootdir); 31 + } 32 + 33 + void kunit_debugfs_init(void) 34 + { 35 + if (!debugfs_rootdir) 36 + debugfs_rootdir = debugfs_create_dir(KUNIT_DEBUGFS_ROOT, NULL); 37 + } 38 + 39 + static void debugfs_print_result(struct seq_file *seq, 40 + struct kunit_suite *suite, 41 + struct kunit_case *test_case) 42 + { 43 + if (!test_case || !test_case->log) 44 + return; 45 + 46 + seq_printf(seq, "%s", test_case->log); 47 + } 48 + 49 + /* 50 + * /sys/kernel/debug/kunit/<testsuite>/results shows all results for testsuite. 51 + */ 52 + static int debugfs_print_results(struct seq_file *seq, void *v) 53 + { 54 + struct kunit_suite *suite = (struct kunit_suite *)seq->private; 55 + bool success = kunit_suite_has_succeeded(suite); 56 + struct kunit_case *test_case; 57 + 58 + if (!suite || !suite->log) 59 + return 0; 60 + 61 + seq_printf(seq, "%s", suite->log); 62 + 63 + kunit_suite_for_each_test_case(suite, test_case) 64 + debugfs_print_result(seq, suite, test_case); 65 + 66 + seq_printf(seq, "%s %d - %s\n", 67 + kunit_status_to_string(success), 1, suite->name); 68 + return 0; 69 + } 70 + 71 + static int debugfs_release(struct inode *inode, struct file *file) 72 + { 73 + return single_release(inode, file); 74 + } 75 + 76 + static int debugfs_results_open(struct inode *inode, struct file *file) 77 + { 78 + struct kunit_suite *suite; 79 + 80 + suite = (struct kunit_suite *)inode->i_private; 81 + 82 + return single_open(file, debugfs_print_results, suite); 83 + } 84 + 85 + static const struct file_operations debugfs_results_fops = { 86 + .open = debugfs_results_open, 87 + .read = seq_read, 88 + .llseek = seq_lseek, 89 + .release = debugfs_release, 90 + }; 91 + 92 + void kunit_debugfs_create_suite(struct kunit_suite *suite) 93 + { 94 + struct kunit_case *test_case; 95 + 96 + /* Allocate logs before creating debugfs representation. */ 97 + suite->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL); 98 + kunit_suite_for_each_test_case(suite, test_case) 99 + test_case->log = kzalloc(KUNIT_LOG_SIZE, GFP_KERNEL); 100 + 101 + suite->debugfs = debugfs_create_dir(suite->name, debugfs_rootdir); 102 + 103 + debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, 104 + suite->debugfs, 105 + suite, &debugfs_results_fops); 106 + } 107 + 108 + void kunit_debugfs_destroy_suite(struct kunit_suite *suite) 109 + { 110 + struct kunit_case *test_case; 111 + 112 + debugfs_remove_recursive(suite->debugfs); 113 + kfree(suite->log); 114 + kunit_suite_for_each_test_case(suite, test_case) 115 + kfree(test_case->log); 116 + }
+30
lib/kunit/debugfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020, Oracle and/or its affiliates. 4 + */ 5 + 6 + #ifndef _KUNIT_DEBUGFS_H 7 + #define _KUNIT_DEBUGFS_H 8 + 9 + #include <kunit/test.h> 10 + 11 + #ifdef CONFIG_KUNIT_DEBUGFS 12 + 13 + void kunit_debugfs_create_suite(struct kunit_suite *suite); 14 + void kunit_debugfs_destroy_suite(struct kunit_suite *suite); 15 + void kunit_debugfs_init(void); 16 + void kunit_debugfs_cleanup(void); 17 + 18 + #else 19 + 20 + static inline void kunit_debugfs_create_suite(struct kunit_suite *suite) { } 21 + 22 + static inline void kunit_debugfs_destroy_suite(struct kunit_suite *suite) { } 23 + 24 + static inline void kunit_debugfs_init(void) { } 25 + 26 + static inline void kunit_debugfs_cleanup(void) { } 27 + 28 + #endif /* CONFIG_KUNIT_DEBUGFS */ 29 + 30 + #endif /* _KUNIT_DEBUGFS_H */
+2 -2
lib/kunit/kunit-test.c
··· 134 134 { 135 135 struct kunit_test_resource_context *ctx = test->priv; 136 136 137 - kunit_init_test(&ctx->test, "testing_test_init_test"); 137 + kunit_init_test(&ctx->test, "testing_test_init_test", NULL); 138 138 139 139 KUNIT_EXPECT_TRUE(test, list_empty(&ctx->test.resources)); 140 140 } ··· 301 301 302 302 test->priv = ctx; 303 303 304 - kunit_init_test(&ctx->test, "test_test_context"); 304 + kunit_init_test(&ctx->test, "test_test_context", NULL); 305 305 306 306 return 0; 307 307 }
+116 -31
lib/kunit/test.c
··· 10 10 #include <linux/kernel.h> 11 11 #include <linux/sched/debug.h> 12 12 13 + #include "debugfs.h" 13 14 #include "string-stream.h" 14 15 #include "try-catch-impl.h" 15 16 ··· 29 28 } 30 29 } 31 30 32 - static size_t kunit_test_cases_len(struct kunit_case *test_cases) 31 + /* 32 + * Append formatted message to log, size of which is limited to 33 + * KUNIT_LOG_SIZE bytes (including null terminating byte). 34 + */ 35 + void kunit_log_append(char *log, const char *fmt, ...) 36 + { 37 + char line[KUNIT_LOG_SIZE]; 38 + va_list args; 39 + int len_left; 40 + 41 + if (!log) 42 + return; 43 + 44 + len_left = KUNIT_LOG_SIZE - strlen(log) - 1; 45 + if (len_left <= 0) 46 + return; 47 + 48 + va_start(args, fmt); 49 + vsnprintf(line, sizeof(line), fmt, args); 50 + va_end(args); 51 + 52 + strncat(log, line, len_left); 53 + } 54 + EXPORT_SYMBOL_GPL(kunit_log_append); 55 + 56 + size_t kunit_suite_num_test_cases(struct kunit_suite *suite) 33 57 { 34 58 struct kunit_case *test_case; 35 59 size_t len = 0; 36 60 37 - for (test_case = test_cases; test_case->run_case; test_case++) 61 + kunit_suite_for_each_test_case(suite, test_case) 38 62 len++; 39 63 40 64 return len; 41 65 } 66 + EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases); 42 67 43 68 static void kunit_print_subtest_start(struct kunit_suite *suite) 44 69 { 45 70 kunit_print_tap_version(); 46 - pr_info("\t# Subtest: %s\n", suite->name); 47 - pr_info("\t1..%zd\n", kunit_test_cases_len(suite->test_cases)); 71 + kunit_log(KERN_INFO, suite, "\t# Subtest: %s", suite->name); 72 + kunit_log(KERN_INFO, suite, "\t1..%zd", 73 + kunit_suite_num_test_cases(suite)); 48 74 } 49 75 50 - static void kunit_print_ok_not_ok(bool should_indent, 76 + static void kunit_print_ok_not_ok(void *test_or_suite, 77 + bool is_test, 51 78 bool is_ok, 52 79 size_t test_number, 53 80 const char *description) 54 81 { 55 - const char *indent, *ok_not_ok; 82 + struct kunit_suite *suite = is_test ? NULL : test_or_suite; 83 + struct kunit *test = is_test ? test_or_suite : NULL; 56 84 57 - if (should_indent) 58 - indent = "\t"; 85 + /* 86 + * We do not log the test suite results as doing so would 87 + * mean debugfs display would consist of the test suite 88 + * description and status prior to individual test results. 89 + * Hence directly printk the suite status, and we will 90 + * separately seq_printf() the suite status for the debugfs 91 + * representation. 92 + */ 93 + if (suite) 94 + pr_info("%s %zd - %s", 95 + kunit_status_to_string(is_ok), 96 + test_number, description); 59 97 else 60 - indent = ""; 61 - 62 - if (is_ok) 63 - ok_not_ok = "ok"; 64 - else 65 - ok_not_ok = "not ok"; 66 - 67 - pr_info("%s%s %zd - %s\n", indent, ok_not_ok, test_number, description); 98 + kunit_log(KERN_INFO, test, "\t%s %zd - %s", 99 + kunit_status_to_string(is_ok), 100 + test_number, description); 68 101 } 69 102 70 - static bool kunit_suite_has_succeeded(struct kunit_suite *suite) 103 + bool kunit_suite_has_succeeded(struct kunit_suite *suite) 71 104 { 72 105 const struct kunit_case *test_case; 73 106 74 - for (test_case = suite->test_cases; test_case->run_case; test_case++) 107 + kunit_suite_for_each_test_case(suite, test_case) { 75 108 if (!test_case->success) 76 109 return false; 110 + } 77 111 78 112 return true; 79 113 } 114 + EXPORT_SYMBOL_GPL(kunit_suite_has_succeeded); 80 115 81 116 static void kunit_print_subtest_end(struct kunit_suite *suite) 82 117 { 83 118 static size_t kunit_suite_counter = 1; 84 119 85 - kunit_print_ok_not_ok(false, 120 + kunit_print_ok_not_ok((void *)suite, false, 86 121 kunit_suite_has_succeeded(suite), 87 122 kunit_suite_counter++, 88 123 suite->name); 89 124 } 90 125 91 - static void kunit_print_test_case_ok_not_ok(struct kunit_case *test_case, 92 - size_t test_number) 126 + unsigned int kunit_test_case_num(struct kunit_suite *suite, 127 + struct kunit_case *test_case) 93 128 { 94 - kunit_print_ok_not_ok(true, 95 - test_case->success, 96 - test_number, 97 - test_case->name); 129 + struct kunit_case *tc; 130 + unsigned int i = 1; 131 + 132 + kunit_suite_for_each_test_case(suite, tc) { 133 + if (tc == test_case) 134 + return i; 135 + i++; 136 + } 137 + 138 + return 0; 98 139 } 140 + EXPORT_SYMBOL_GPL(kunit_test_case_num); 99 141 100 142 static void kunit_print_string_stream(struct kunit *test, 101 143 struct string_stream *stream) 102 144 { 103 145 struct string_stream_fragment *fragment; 104 146 char *buf; 147 + 148 + if (string_stream_is_empty(stream)) 149 + return; 105 150 106 151 buf = string_stream_get_string(stream); 107 152 if (!buf) { ··· 222 175 } 223 176 EXPORT_SYMBOL_GPL(kunit_do_assertion); 224 177 225 - void kunit_init_test(struct kunit *test, const char *name) 178 + void kunit_init_test(struct kunit *test, const char *name, char *log) 226 179 { 227 180 spin_lock_init(&test->lock); 228 181 INIT_LIST_HEAD(&test->resources); 229 182 test->name = name; 183 + test->log = log; 184 + if (test->log) 185 + test->log[0] = '\0'; 230 186 test->success = true; 231 187 } 232 188 EXPORT_SYMBOL_GPL(kunit_init_test); ··· 340 290 struct kunit_try_catch *try_catch; 341 291 struct kunit test; 342 292 343 - kunit_init_test(&test, test_case->name); 293 + kunit_init_test(&test, test_case->name, test_case->log); 344 294 try_catch = &test.try_catch; 345 295 346 296 kunit_try_catch_init(try_catch, ··· 353 303 kunit_try_catch_run(try_catch, &context); 354 304 355 305 test_case->success = test.success; 306 + 307 + kunit_print_ok_not_ok(&test, true, test_case->success, 308 + kunit_test_case_num(suite, test_case), 309 + test_case->name); 356 310 } 357 311 358 312 int kunit_run_tests(struct kunit_suite *suite) 359 313 { 360 314 struct kunit_case *test_case; 361 - size_t test_case_count = 1; 362 315 363 316 kunit_print_subtest_start(suite); 364 317 365 - for (test_case = suite->test_cases; test_case->run_case; test_case++) { 318 + kunit_suite_for_each_test_case(suite, test_case) 366 319 kunit_run_case_catch_errors(suite, test_case); 367 - kunit_print_test_case_ok_not_ok(test_case, test_case_count++); 368 - } 369 320 370 321 kunit_print_subtest_end(suite); 371 322 372 323 return 0; 373 324 } 374 325 EXPORT_SYMBOL_GPL(kunit_run_tests); 326 + 327 + static void kunit_init_suite(struct kunit_suite *suite) 328 + { 329 + kunit_debugfs_create_suite(suite); 330 + } 331 + 332 + int __kunit_test_suites_init(struct kunit_suite **suites) 333 + { 334 + unsigned int i; 335 + 336 + for (i = 0; suites[i] != NULL; i++) { 337 + kunit_init_suite(suites[i]); 338 + kunit_run_tests(suites[i]); 339 + } 340 + return 0; 341 + } 342 + EXPORT_SYMBOL_GPL(__kunit_test_suites_init); 343 + 344 + static void kunit_exit_suite(struct kunit_suite *suite) 345 + { 346 + kunit_debugfs_destroy_suite(suite); 347 + } 348 + 349 + void __kunit_test_suites_exit(struct kunit_suite **suites) 350 + { 351 + unsigned int i; 352 + 353 + for (i = 0; suites[i] != NULL; i++) 354 + kunit_exit_suite(suites[i]); 355 + } 356 + EXPORT_SYMBOL_GPL(__kunit_test_suites_exit); 375 357 376 358 struct kunit_resource *kunit_alloc_and_get_resource(struct kunit *test, 377 359 kunit_resource_init_t init, ··· 571 489 572 490 static int __init kunit_init(void) 573 491 { 492 + kunit_debugfs_init(); 493 + 574 494 return 0; 575 495 } 576 496 late_initcall(kunit_init); 577 497 578 498 static void __exit kunit_exit(void) 579 499 { 500 + kunit_debugfs_cleanup(); 580 501 } 581 502 module_exit(kunit_exit); 582 503