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 | #include <kunit/test-bug.h> |
12 | |
13 | #include "string-stream.h" |
14 | #include "debugfs.h" |
15 | |
16 | #define KUNIT_DEBUGFS_ROOT "kunit" |
17 | #define KUNIT_DEBUGFS_RESULTS "results" |
18 | #define KUNIT_DEBUGFS_RUN "run" |
19 | |
20 | /* |
21 | * Create a debugfs representation of test suites: |
22 | * |
23 | * Path Semantics |
24 | * /sys/kernel/debug/kunit/<testsuite>/results Show results of last run for |
25 | * testsuite |
26 | * /sys/kernel/debug/kunit/<testsuite>/run Write to this file to trigger |
27 | * testsuite to run |
28 | * |
29 | */ |
30 | |
31 | static struct dentry *debugfs_rootdir; |
32 | |
33 | void kunit_debugfs_cleanup(void) |
34 | { |
35 | debugfs_remove_recursive(dentry: debugfs_rootdir); |
36 | } |
37 | |
38 | void kunit_debugfs_init(void) |
39 | { |
40 | if (!debugfs_rootdir) |
41 | debugfs_rootdir = debugfs_create_dir(KUNIT_DEBUGFS_ROOT, NULL); |
42 | } |
43 | |
44 | static void debugfs_print_result(struct seq_file *seq, struct string_stream *log) |
45 | { |
46 | struct string_stream_fragment *frag_container; |
47 | |
48 | if (!log) |
49 | return; |
50 | |
51 | /* |
52 | * Walk the fragments so we don't need to allocate a temporary |
53 | * buffer to hold the entire string. |
54 | */ |
55 | spin_lock(lock: &log->lock); |
56 | list_for_each_entry(frag_container, &log->fragments, node) |
57 | seq_printf(m: seq, fmt: "%s" , frag_container->fragment); |
58 | spin_unlock(lock: &log->lock); |
59 | } |
60 | |
61 | /* |
62 | * /sys/kernel/debug/kunit/<testsuite>/results shows all results for testsuite. |
63 | */ |
64 | static int debugfs_print_results(struct seq_file *seq, void *v) |
65 | { |
66 | struct kunit_suite *suite = (struct kunit_suite *)seq->private; |
67 | enum kunit_status success; |
68 | struct kunit_case *test_case; |
69 | |
70 | if (!suite) |
71 | return 0; |
72 | |
73 | success = kunit_suite_has_succeeded(suite); |
74 | |
75 | /* Print KTAP header so the debugfs log can be parsed as valid KTAP. */ |
76 | seq_puts(m: seq, s: "KTAP version 1\n" ); |
77 | seq_puts(m: seq, s: "1..1\n" ); |
78 | |
79 | /* Print suite header because it is not stored in the test logs. */ |
80 | seq_puts(m: seq, KUNIT_SUBTEST_INDENT "KTAP version 1\n" ); |
81 | seq_printf(m: seq, KUNIT_SUBTEST_INDENT "# Subtest: %s\n" , suite->name); |
82 | seq_printf(m: seq, KUNIT_SUBTEST_INDENT "1..%zd\n" , kunit_suite_num_test_cases(suite)); |
83 | |
84 | kunit_suite_for_each_test_case(suite, test_case) |
85 | debugfs_print_result(seq, log: test_case->log); |
86 | |
87 | debugfs_print_result(seq, log: suite->log); |
88 | |
89 | seq_printf(m: seq, fmt: "%s %d %s\n" , |
90 | kunit_status_to_ok_not_ok(status: success), 1, suite->name); |
91 | return 0; |
92 | } |
93 | |
94 | static int debugfs_release(struct inode *inode, struct file *file) |
95 | { |
96 | return single_release(inode, file); |
97 | } |
98 | |
99 | static int debugfs_results_open(struct inode *inode, struct file *file) |
100 | { |
101 | struct kunit_suite *suite; |
102 | |
103 | suite = (struct kunit_suite *)inode->i_private; |
104 | |
105 | return single_open(file, debugfs_print_results, suite); |
106 | } |
107 | |
108 | /* |
109 | * Print a usage message to the debugfs "run" file |
110 | * (/sys/kernel/debug/kunit/<testsuite>/run) if opened. |
111 | */ |
112 | static int debugfs_print_run(struct seq_file *seq, void *v) |
113 | { |
114 | struct kunit_suite *suite = (struct kunit_suite *)seq->private; |
115 | |
116 | seq_puts(m: seq, s: "Write to this file to trigger the test suite to run.\n" ); |
117 | seq_printf(m: seq, fmt: "usage: echo \"any string\" > /sys/kernel/debugfs/kunit/%s/run\n" , |
118 | suite->name); |
119 | return 0; |
120 | } |
121 | |
122 | /* |
123 | * The debugfs "run" file (/sys/kernel/debug/kunit/<testsuite>/run) |
124 | * contains no information. Write to the file to trigger the test suite |
125 | * to run. |
126 | */ |
127 | static int debugfs_run_open(struct inode *inode, struct file *file) |
128 | { |
129 | struct kunit_suite *suite; |
130 | |
131 | suite = (struct kunit_suite *)inode->i_private; |
132 | |
133 | return single_open(file, debugfs_print_run, suite); |
134 | } |
135 | |
136 | /* |
137 | * Trigger a test suite to run by writing to the suite's "run" debugfs |
138 | * file found at: /sys/kernel/debug/kunit/<testsuite>/run |
139 | * |
140 | * Note: what is written to this file will not be saved. |
141 | */ |
142 | static ssize_t debugfs_run(struct file *file, |
143 | const char __user *buf, size_t count, loff_t *ppos) |
144 | { |
145 | struct inode *f_inode = file->f_inode; |
146 | struct kunit_suite *suite = (struct kunit_suite *) f_inode->i_private; |
147 | |
148 | __kunit_test_suites_init(suites: &suite, num_suites: 1); |
149 | |
150 | return count; |
151 | } |
152 | |
153 | static const struct file_operations debugfs_results_fops = { |
154 | .open = debugfs_results_open, |
155 | .read = seq_read, |
156 | .llseek = seq_lseek, |
157 | .release = debugfs_release, |
158 | }; |
159 | |
160 | static const struct file_operations debugfs_run_fops = { |
161 | .open = debugfs_run_open, |
162 | .read = seq_read, |
163 | .write = debugfs_run, |
164 | .llseek = seq_lseek, |
165 | .release = debugfs_release, |
166 | }; |
167 | |
168 | void kunit_debugfs_create_suite(struct kunit_suite *suite) |
169 | { |
170 | struct kunit_case *test_case; |
171 | struct string_stream *stream; |
172 | |
173 | /* If suite log already allocated, do not create new debugfs files. */ |
174 | if (suite->log) |
175 | return; |
176 | |
177 | /* |
178 | * Allocate logs before creating debugfs representation. |
179 | * The suite->log and test_case->log pointer are expected to be NULL |
180 | * if there isn't a log, so only set it if the log stream was created |
181 | * successfully. |
182 | */ |
183 | stream = alloc_string_stream(GFP_KERNEL); |
184 | if (IS_ERR_OR_NULL(ptr: stream)) |
185 | return; |
186 | |
187 | string_stream_set_append_newlines(stream, append_newlines: true); |
188 | suite->log = stream; |
189 | |
190 | kunit_suite_for_each_test_case(suite, test_case) { |
191 | stream = alloc_string_stream(GFP_KERNEL); |
192 | if (IS_ERR_OR_NULL(ptr: stream)) |
193 | goto err; |
194 | |
195 | string_stream_set_append_newlines(stream, append_newlines: true); |
196 | test_case->log = stream; |
197 | } |
198 | |
199 | suite->debugfs = debugfs_create_dir(name: suite->name, parent: debugfs_rootdir); |
200 | |
201 | debugfs_create_file(KUNIT_DEBUGFS_RESULTS, S_IFREG | 0444, |
202 | parent: suite->debugfs, |
203 | data: suite, fops: &debugfs_results_fops); |
204 | |
205 | /* Do not create file to re-run test if test runs on init */ |
206 | if (!suite->is_init) { |
207 | debugfs_create_file(KUNIT_DEBUGFS_RUN, S_IFREG | 0644, |
208 | parent: suite->debugfs, |
209 | data: suite, fops: &debugfs_run_fops); |
210 | } |
211 | return; |
212 | |
213 | err: |
214 | string_stream_destroy(stream: suite->log); |
215 | kunit_suite_for_each_test_case(suite, test_case) |
216 | string_stream_destroy(stream: test_case->log); |
217 | } |
218 | |
219 | void kunit_debugfs_destroy_suite(struct kunit_suite *suite) |
220 | { |
221 | struct kunit_case *test_case; |
222 | |
223 | debugfs_remove_recursive(dentry: suite->debugfs); |
224 | string_stream_destroy(stream: suite->log); |
225 | kunit_suite_for_each_test_case(suite, test_case) |
226 | string_stream_destroy(stream: test_case->log); |
227 | } |
228 | |