Commit | Line | Data |
---|---|---|
64836248 TZ |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Test module for in-kernel kprobe event creation and generation. | |
4 | * | |
5 | * Copyright (C) 2019 Tom Zanussi <zanussi@kernel.org> | |
6 | */ | |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/trace_events.h> | |
10 | ||
11 | /* | |
12 | * This module is a simple test of basic functionality for in-kernel | |
13 | * kprobe/kretprobe event creation. The first test uses | |
14 | * kprobe_event_gen_cmd_start(), kprobe_event_add_fields() and | |
15 | * kprobe_event_gen_cmd_end() to create a kprobe event, which is then | |
16 | * enabled in order to generate trace output. The second creates a | |
17 | * kretprobe event using kretprobe_event_gen_cmd_start() and | |
18 | * kretprobe_event_gen_cmd_end(), and is also then enabled. | |
19 | * | |
20 | * To test, select CONFIG_KPROBE_EVENT_GEN_TEST and build the module. | |
21 | * Then: | |
22 | * | |
23 | * # insmod kernel/trace/kprobe_event_gen_test.ko | |
2455f0e1 | 24 | * # cat /sys/kernel/tracing/trace |
64836248 TZ |
25 | * |
26 | * You should see many instances of the "gen_kprobe_test" and | |
27 | * "gen_kretprobe_test" events in the trace buffer. | |
28 | * | |
29 | * To remove the events, remove the module: | |
30 | * | |
31 | * # rmmod kprobe_event_gen_test | |
32 | * | |
33 | */ | |
34 | ||
35 | static struct trace_event_file *gen_kprobe_test; | |
36 | static struct trace_event_file *gen_kretprobe_test; | |
37 | ||
d8ef45d6 YZ |
38 | #define KPROBE_GEN_TEST_FUNC "do_sys_open" |
39 | ||
40 | /* X86 */ | |
41 | #if defined(CONFIG_X86_64) || defined(CONFIG_X86_32) | |
42 | #define KPROBE_GEN_TEST_ARG0 "dfd=%ax" | |
43 | #define KPROBE_GEN_TEST_ARG1 "filename=%dx" | |
44 | #define KPROBE_GEN_TEST_ARG2 "flags=%cx" | |
45 | #define KPROBE_GEN_TEST_ARG3 "mode=+4($stack)" | |
46 | ||
47 | /* ARM64 */ | |
48 | #elif defined(CONFIG_ARM64) | |
49 | #define KPROBE_GEN_TEST_ARG0 "dfd=%x0" | |
50 | #define KPROBE_GEN_TEST_ARG1 "filename=%x1" | |
51 | #define KPROBE_GEN_TEST_ARG2 "flags=%x2" | |
52 | #define KPROBE_GEN_TEST_ARG3 "mode=%x3" | |
53 | ||
54 | /* ARM */ | |
55 | #elif defined(CONFIG_ARM) | |
56 | #define KPROBE_GEN_TEST_ARG0 "dfd=%r0" | |
57 | #define KPROBE_GEN_TEST_ARG1 "filename=%r1" | |
58 | #define KPROBE_GEN_TEST_ARG2 "flags=%r2" | |
59 | #define KPROBE_GEN_TEST_ARG3 "mode=%r3" | |
60 | ||
61 | /* RISCV */ | |
62 | #elif defined(CONFIG_RISCV) | |
63 | #define KPROBE_GEN_TEST_ARG0 "dfd=%a0" | |
64 | #define KPROBE_GEN_TEST_ARG1 "filename=%a1" | |
65 | #define KPROBE_GEN_TEST_ARG2 "flags=%a2" | |
66 | #define KPROBE_GEN_TEST_ARG3 "mode=%a3" | |
67 | ||
68 | /* others */ | |
69 | #else | |
70 | #define KPROBE_GEN_TEST_ARG0 NULL | |
71 | #define KPROBE_GEN_TEST_ARG1 NULL | |
72 | #define KPROBE_GEN_TEST_ARG2 NULL | |
73 | #define KPROBE_GEN_TEST_ARG3 NULL | |
74 | #endif | |
75 | ||
e0d75267 SX |
76 | static bool trace_event_file_is_valid(struct trace_event_file *input) |
77 | { | |
78 | return input && !IS_ERR(input); | |
79 | } | |
d8ef45d6 | 80 | |
64836248 TZ |
81 | /* |
82 | * Test to make sure we can create a kprobe event, then add more | |
83 | * fields. | |
84 | */ | |
85 | static int __init test_gen_kprobe_cmd(void) | |
86 | { | |
87 | struct dynevent_cmd cmd; | |
88 | char *buf; | |
89 | int ret; | |
90 | ||
91 | /* Create a buffer to hold the generated command */ | |
92 | buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); | |
93 | if (!buf) | |
94 | return -ENOMEM; | |
95 | ||
96 | /* Before generating the command, initialize the cmd object */ | |
97 | kprobe_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); | |
98 | ||
99 | /* | |
100 | * Define the gen_kprobe_test event with the first 2 kprobe | |
101 | * fields. | |
102 | */ | |
103 | ret = kprobe_event_gen_cmd_start(&cmd, "gen_kprobe_test", | |
d8ef45d6 YZ |
104 | KPROBE_GEN_TEST_FUNC, |
105 | KPROBE_GEN_TEST_ARG0, KPROBE_GEN_TEST_ARG1); | |
64836248 | 106 | if (ret) |
66f0919c | 107 | goto out; |
64836248 TZ |
108 | |
109 | /* Use kprobe_event_add_fields to add the rest of the fields */ | |
110 | ||
d8ef45d6 | 111 | ret = kprobe_event_add_fields(&cmd, KPROBE_GEN_TEST_ARG2, KPROBE_GEN_TEST_ARG3); |
64836248 | 112 | if (ret) |
66f0919c | 113 | goto out; |
64836248 TZ |
114 | |
115 | /* | |
116 | * This actually creates the event. | |
117 | */ | |
118 | ret = kprobe_event_gen_cmd_end(&cmd); | |
119 | if (ret) | |
66f0919c | 120 | goto out; |
64836248 TZ |
121 | |
122 | /* | |
123 | * Now get the gen_kprobe_test event file. We need to prevent | |
124 | * the instance and event from disappearing from underneath | |
125 | * us, which trace_get_event_file() does (though in this case | |
126 | * we're using the top-level instance which never goes away). | |
127 | */ | |
128 | gen_kprobe_test = trace_get_event_file(NULL, "kprobes", | |
129 | "gen_kprobe_test"); | |
130 | if (IS_ERR(gen_kprobe_test)) { | |
131 | ret = PTR_ERR(gen_kprobe_test); | |
132 | goto delete; | |
133 | } | |
134 | ||
135 | /* Enable the event or you won't see anything */ | |
136 | ret = trace_array_set_clr_event(gen_kprobe_test->tr, | |
137 | "kprobes", "gen_kprobe_test", true); | |
138 | if (ret) { | |
139 | trace_put_event_file(gen_kprobe_test); | |
140 | goto delete; | |
141 | } | |
142 | out: | |
66f0919c | 143 | kfree(buf); |
64836248 TZ |
144 | return ret; |
145 | delete: | |
22ea4ca9 SX |
146 | if (trace_event_file_is_valid(gen_kprobe_test)) |
147 | gen_kprobe_test = NULL; | |
64836248 | 148 | /* We got an error after creating the event, delete it */ |
bc4f359b | 149 | kprobe_event_delete("gen_kprobe_test"); |
64836248 TZ |
150 | goto out; |
151 | } | |
152 | ||
153 | /* | |
154 | * Test to make sure we can create a kretprobe event. | |
155 | */ | |
156 | static int __init test_gen_kretprobe_cmd(void) | |
157 | { | |
158 | struct dynevent_cmd cmd; | |
159 | char *buf; | |
160 | int ret; | |
161 | ||
162 | /* Create a buffer to hold the generated command */ | |
163 | buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); | |
164 | if (!buf) | |
165 | return -ENOMEM; | |
166 | ||
167 | /* Before generating the command, initialize the cmd object */ | |
168 | kprobe_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); | |
169 | ||
170 | /* | |
171 | * Define the kretprobe event. | |
172 | */ | |
173 | ret = kretprobe_event_gen_cmd_start(&cmd, "gen_kretprobe_test", | |
d8ef45d6 | 174 | KPROBE_GEN_TEST_FUNC, |
64836248 TZ |
175 | "$retval"); |
176 | if (ret) | |
66f0919c | 177 | goto out; |
64836248 TZ |
178 | |
179 | /* | |
180 | * This actually creates the event. | |
181 | */ | |
182 | ret = kretprobe_event_gen_cmd_end(&cmd); | |
183 | if (ret) | |
66f0919c | 184 | goto out; |
64836248 TZ |
185 | |
186 | /* | |
187 | * Now get the gen_kretprobe_test event file. We need to | |
188 | * prevent the instance and event from disappearing from | |
189 | * underneath us, which trace_get_event_file() does (though in | |
190 | * this case we're using the top-level instance which never | |
191 | * goes away). | |
192 | */ | |
193 | gen_kretprobe_test = trace_get_event_file(NULL, "kprobes", | |
194 | "gen_kretprobe_test"); | |
195 | if (IS_ERR(gen_kretprobe_test)) { | |
196 | ret = PTR_ERR(gen_kretprobe_test); | |
197 | goto delete; | |
198 | } | |
199 | ||
200 | /* Enable the event or you won't see anything */ | |
201 | ret = trace_array_set_clr_event(gen_kretprobe_test->tr, | |
202 | "kprobes", "gen_kretprobe_test", true); | |
203 | if (ret) { | |
204 | trace_put_event_file(gen_kretprobe_test); | |
205 | goto delete; | |
206 | } | |
207 | out: | |
66f0919c | 208 | kfree(buf); |
64836248 TZ |
209 | return ret; |
210 | delete: | |
22ea4ca9 SX |
211 | if (trace_event_file_is_valid(gen_kretprobe_test)) |
212 | gen_kretprobe_test = NULL; | |
64836248 | 213 | /* We got an error after creating the event, delete it */ |
bc4f359b | 214 | kprobe_event_delete("gen_kretprobe_test"); |
64836248 TZ |
215 | goto out; |
216 | } | |
217 | ||
218 | static int __init kprobe_event_gen_test_init(void) | |
219 | { | |
220 | int ret; | |
221 | ||
222 | ret = test_gen_kprobe_cmd(); | |
223 | if (ret) | |
224 | return ret; | |
225 | ||
226 | ret = test_gen_kretprobe_cmd(); | |
227 | if (ret) { | |
e0d75267 SX |
228 | if (trace_event_file_is_valid(gen_kretprobe_test)) { |
229 | WARN_ON(trace_array_set_clr_event(gen_kretprobe_test->tr, | |
230 | "kprobes", | |
231 | "gen_kretprobe_test", false)); | |
232 | trace_put_event_file(gen_kretprobe_test); | |
233 | } | |
64836248 TZ |
234 | WARN_ON(kprobe_event_delete("gen_kretprobe_test")); |
235 | } | |
236 | ||
237 | return ret; | |
238 | } | |
239 | ||
240 | static void __exit kprobe_event_gen_test_exit(void) | |
241 | { | |
e0d75267 SX |
242 | if (trace_event_file_is_valid(gen_kprobe_test)) { |
243 | /* Disable the event or you can't remove it */ | |
244 | WARN_ON(trace_array_set_clr_event(gen_kprobe_test->tr, | |
245 | "kprobes", | |
246 | "gen_kprobe_test", false)); | |
247 | ||
248 | /* Now give the file and instance back */ | |
249 | trace_put_event_file(gen_kprobe_test); | |
250 | } | |
64836248 | 251 | |
64836248 TZ |
252 | |
253 | /* Now unregister and free the event */ | |
254 | WARN_ON(kprobe_event_delete("gen_kprobe_test")); | |
255 | ||
e0d75267 SX |
256 | if (trace_event_file_is_valid(gen_kretprobe_test)) { |
257 | /* Disable the event or you can't remove it */ | |
258 | WARN_ON(trace_array_set_clr_event(gen_kretprobe_test->tr, | |
259 | "kprobes", | |
260 | "gen_kretprobe_test", false)); | |
261 | ||
262 | /* Now give the file and instance back */ | |
263 | trace_put_event_file(gen_kretprobe_test); | |
264 | } | |
64836248 | 265 | |
64836248 TZ |
266 | |
267 | /* Now unregister and free the event */ | |
268 | WARN_ON(kprobe_event_delete("gen_kretprobe_test")); | |
269 | } | |
270 | ||
271 | module_init(kprobe_event_gen_test_init) | |
272 | module_exit(kprobe_event_gen_test_exit) | |
273 | ||
274 | MODULE_AUTHOR("Tom Zanussi"); | |
275 | MODULE_DESCRIPTION("kprobe event generation test"); | |
276 | MODULE_LICENSE("GPL v2"); |