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 |
24 | * # cat /sys/kernel/tracing/trace |
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 | |
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 | |
76 | static bool trace_event_file_is_valid(struct trace_event_file *input) |
77 | { |
78 | return input && !IS_ERR(ptr: input); |
79 | } |
80 | |
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: &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" , |
104 | KPROBE_GEN_TEST_FUNC, |
105 | KPROBE_GEN_TEST_ARG0, KPROBE_GEN_TEST_ARG1); |
106 | if (ret) |
107 | goto out; |
108 | |
109 | /* Use kprobe_event_add_fields to add the rest of the fields */ |
110 | |
111 | ret = kprobe_event_add_fields(&cmd, KPROBE_GEN_TEST_ARG2, KPROBE_GEN_TEST_ARG3); |
112 | if (ret) |
113 | goto out; |
114 | |
115 | /* |
116 | * This actually creates the event. |
117 | */ |
118 | ret = kprobe_event_gen_cmd_end(&cmd); |
119 | if (ret) |
120 | goto out; |
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, system: "kprobes" , |
129 | event: "gen_kprobe_test" ); |
130 | if (IS_ERR(ptr: gen_kprobe_test)) { |
131 | ret = PTR_ERR(ptr: 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(tr: gen_kprobe_test->tr, |
137 | system: "kprobes" , event: "gen_kprobe_test" , enable: true); |
138 | if (ret) { |
139 | trace_put_event_file(file: gen_kprobe_test); |
140 | goto delete; |
141 | } |
142 | out: |
143 | kfree(objp: buf); |
144 | return ret; |
145 | delete: |
146 | if (trace_event_file_is_valid(input: gen_kprobe_test)) |
147 | gen_kprobe_test = NULL; |
148 | /* We got an error after creating the event, delete it */ |
149 | kprobe_event_delete(name: "gen_kprobe_test" ); |
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: &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" , |
174 | KPROBE_GEN_TEST_FUNC, |
175 | "$retval" ); |
176 | if (ret) |
177 | goto out; |
178 | |
179 | /* |
180 | * This actually creates the event. |
181 | */ |
182 | ret = kretprobe_event_gen_cmd_end(&cmd); |
183 | if (ret) |
184 | goto out; |
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, system: "kprobes" , |
194 | event: "gen_kretprobe_test" ); |
195 | if (IS_ERR(ptr: gen_kretprobe_test)) { |
196 | ret = PTR_ERR(ptr: 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(tr: gen_kretprobe_test->tr, |
202 | system: "kprobes" , event: "gen_kretprobe_test" , enable: true); |
203 | if (ret) { |
204 | trace_put_event_file(file: gen_kretprobe_test); |
205 | goto delete; |
206 | } |
207 | out: |
208 | kfree(objp: buf); |
209 | return ret; |
210 | delete: |
211 | if (trace_event_file_is_valid(input: gen_kretprobe_test)) |
212 | gen_kretprobe_test = NULL; |
213 | /* We got an error after creating the event, delete it */ |
214 | kprobe_event_delete(name: "gen_kretprobe_test" ); |
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) { |
228 | if (trace_event_file_is_valid(input: 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(file: gen_kretprobe_test); |
233 | } |
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 | { |
242 | if (trace_event_file_is_valid(input: 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(file: gen_kprobe_test); |
250 | } |
251 | |
252 | |
253 | /* Now unregister and free the event */ |
254 | WARN_ON(kprobe_event_delete("gen_kprobe_test" )); |
255 | |
256 | if (trace_event_file_is_valid(input: 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(file: gen_kretprobe_test); |
264 | } |
265 | |
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" ); |
277 | |