]>
Commit | Line | Data |
---|---|---|
9fe41efa TZ |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
f2cc020d | 3 | * Test module for in-kernel synthetic event creation and generation. |
9fe41efa TZ |
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 | * synthetic event creation and generation, the first and second tests | |
14 | * using synth_event_gen_cmd_start() and synth_event_add_field(), the | |
15 | * third uses synth_event_create() to do it all at once with a static | |
16 | * field array. | |
17 | * | |
18 | * Following that are a few examples using the created events to test | |
19 | * various ways of tracing a synthetic event. | |
20 | * | |
21 | * To test, select CONFIG_SYNTH_EVENT_GEN_TEST and build the module. | |
22 | * Then: | |
23 | * | |
24 | * # insmod kernel/trace/synth_event_gen_test.ko | |
25 | * # cat /sys/kernel/debug/tracing/trace | |
26 | * | |
27 | * You should see several events in the trace buffer - | |
28 | * "create_synth_test", "empty_synth_test", and several instances of | |
29 | * "gen_synth_test". | |
30 | * | |
31 | * To remove the events, remove the module: | |
32 | * | |
33 | * # rmmod synth_event_gen_test | |
34 | * | |
35 | */ | |
36 | ||
37 | static struct trace_event_file *create_synth_test; | |
38 | static struct trace_event_file *empty_synth_test; | |
39 | static struct trace_event_file *gen_synth_test; | |
40 | ||
41 | /* | |
42 | * Test to make sure we can create a synthetic event, then add more | |
43 | * fields. | |
44 | */ | |
45 | static int __init test_gen_synth_cmd(void) | |
46 | { | |
47 | struct dynevent_cmd cmd; | |
48 | u64 vals[7]; | |
49 | char *buf; | |
50 | int ret; | |
51 | ||
52 | /* Create a buffer to hold the generated command */ | |
53 | buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); | |
54 | if (!buf) | |
55 | return -ENOMEM; | |
56 | ||
57 | /* Before generating the command, initialize the cmd object */ | |
58 | synth_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); | |
59 | ||
60 | /* | |
61 | * Create the empty gen_synth_test synthetic event with the | |
62 | * first 4 fields. | |
63 | */ | |
64 | ret = synth_event_gen_cmd_start(&cmd, "gen_synth_test", THIS_MODULE, | |
65 | "pid_t", "next_pid_field", | |
66 | "char[16]", "next_comm_field", | |
67 | "u64", "ts_ns", | |
68 | "u64", "ts_ms"); | |
69 | if (ret) | |
70 | goto free; | |
71 | ||
72 | /* Use synth_event_add_field to add the rest of the fields */ | |
73 | ||
74 | ret = synth_event_add_field(&cmd, "unsigned int", "cpu"); | |
75 | if (ret) | |
76 | goto free; | |
77 | ||
78 | ret = synth_event_add_field(&cmd, "char[64]", "my_string_field"); | |
79 | if (ret) | |
80 | goto free; | |
81 | ||
82 | ret = synth_event_add_field(&cmd, "int", "my_int_field"); | |
83 | if (ret) | |
84 | goto free; | |
85 | ||
86 | ret = synth_event_gen_cmd_end(&cmd); | |
87 | if (ret) | |
88 | goto free; | |
89 | ||
90 | /* | |
91 | * Now get the gen_synth_test event file. We need to prevent | |
92 | * the instance and event from disappearing from underneath | |
93 | * us, which trace_get_event_file() does (though in this case | |
94 | * we're using the top-level instance which never goes away). | |
95 | */ | |
96 | gen_synth_test = trace_get_event_file(NULL, "synthetic", | |
97 | "gen_synth_test"); | |
98 | if (IS_ERR(gen_synth_test)) { | |
99 | ret = PTR_ERR(gen_synth_test); | |
100 | goto delete; | |
101 | } | |
102 | ||
103 | /* Enable the event or you won't see anything */ | |
104 | ret = trace_array_set_clr_event(gen_synth_test->tr, | |
105 | "synthetic", "gen_synth_test", true); | |
106 | if (ret) { | |
107 | trace_put_event_file(gen_synth_test); | |
108 | goto delete; | |
109 | } | |
110 | ||
111 | /* Create some bogus values just for testing */ | |
112 | ||
113 | vals[0] = 777; /* next_pid_field */ | |
279eef05 | 114 | vals[1] = (u64)(long)"hula hoops"; /* next_comm_field */ |
9fe41efa TZ |
115 | vals[2] = 1000000; /* ts_ns */ |
116 | vals[3] = 1000; /* ts_ms */ | |
3c18a9be | 117 | vals[4] = raw_smp_processor_id(); /* cpu */ |
279eef05 | 118 | vals[5] = (u64)(long)"thneed"; /* my_string_field */ |
9fe41efa TZ |
119 | vals[6] = 598; /* my_int_field */ |
120 | ||
121 | /* Now generate a gen_synth_test event */ | |
122 | ret = synth_event_trace_array(gen_synth_test, vals, ARRAY_SIZE(vals)); | |
123 | out: | |
124 | return ret; | |
125 | delete: | |
126 | /* We got an error after creating the event, delete it */ | |
127 | synth_event_delete("gen_synth_test"); | |
128 | free: | |
129 | kfree(buf); | |
130 | ||
131 | goto out; | |
132 | } | |
133 | ||
134 | /* | |
135 | * Test to make sure we can create an initially empty synthetic event, | |
136 | * then add all the fields. | |
137 | */ | |
138 | static int __init test_empty_synth_event(void) | |
139 | { | |
140 | struct dynevent_cmd cmd; | |
141 | u64 vals[7]; | |
142 | char *buf; | |
143 | int ret; | |
144 | ||
145 | /* Create a buffer to hold the generated command */ | |
146 | buf = kzalloc(MAX_DYNEVENT_CMD_LEN, GFP_KERNEL); | |
147 | if (!buf) | |
148 | return -ENOMEM; | |
149 | ||
150 | /* Before generating the command, initialize the cmd object */ | |
151 | synth_event_cmd_init(&cmd, buf, MAX_DYNEVENT_CMD_LEN); | |
152 | ||
153 | /* | |
154 | * Create the empty_synth_test synthetic event with no fields. | |
155 | */ | |
156 | ret = synth_event_gen_cmd_start(&cmd, "empty_synth_test", THIS_MODULE); | |
157 | if (ret) | |
158 | goto free; | |
159 | ||
160 | /* Use synth_event_add_field to add all of the fields */ | |
161 | ||
162 | ret = synth_event_add_field(&cmd, "pid_t", "next_pid_field"); | |
163 | if (ret) | |
164 | goto free; | |
165 | ||
166 | ret = synth_event_add_field(&cmd, "char[16]", "next_comm_field"); | |
167 | if (ret) | |
168 | goto free; | |
169 | ||
170 | ret = synth_event_add_field(&cmd, "u64", "ts_ns"); | |
171 | if (ret) | |
172 | goto free; | |
173 | ||
174 | ret = synth_event_add_field(&cmd, "u64", "ts_ms"); | |
175 | if (ret) | |
176 | goto free; | |
177 | ||
178 | ret = synth_event_add_field(&cmd, "unsigned int", "cpu"); | |
179 | if (ret) | |
180 | goto free; | |
181 | ||
182 | ret = synth_event_add_field(&cmd, "char[64]", "my_string_field"); | |
183 | if (ret) | |
184 | goto free; | |
185 | ||
186 | ret = synth_event_add_field(&cmd, "int", "my_int_field"); | |
187 | if (ret) | |
188 | goto free; | |
189 | ||
190 | /* All fields have been added, close and register the synth event */ | |
191 | ||
192 | ret = synth_event_gen_cmd_end(&cmd); | |
193 | if (ret) | |
194 | goto free; | |
195 | ||
196 | /* | |
197 | * Now get the empty_synth_test event file. We need to | |
198 | * prevent the instance and event from disappearing from | |
199 | * underneath us, which trace_get_event_file() does (though in | |
200 | * this case we're using the top-level instance which never | |
201 | * goes away). | |
202 | */ | |
203 | empty_synth_test = trace_get_event_file(NULL, "synthetic", | |
204 | "empty_synth_test"); | |
205 | if (IS_ERR(empty_synth_test)) { | |
206 | ret = PTR_ERR(empty_synth_test); | |
207 | goto delete; | |
208 | } | |
209 | ||
210 | /* Enable the event or you won't see anything */ | |
211 | ret = trace_array_set_clr_event(empty_synth_test->tr, | |
212 | "synthetic", "empty_synth_test", true); | |
213 | if (ret) { | |
214 | trace_put_event_file(empty_synth_test); | |
215 | goto delete; | |
216 | } | |
217 | ||
218 | /* Create some bogus values just for testing */ | |
219 | ||
220 | vals[0] = 777; /* next_pid_field */ | |
279eef05 | 221 | vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */ |
9fe41efa TZ |
222 | vals[2] = 1000000; /* ts_ns */ |
223 | vals[3] = 1000; /* ts_ms */ | |
3c18a9be | 224 | vals[4] = raw_smp_processor_id(); /* cpu */ |
279eef05 | 225 | vals[5] = (u64)(long)"thneed_2.0"; /* my_string_field */ |
9fe41efa TZ |
226 | vals[6] = 399; /* my_int_field */ |
227 | ||
228 | /* Now trace an empty_synth_test event */ | |
229 | ret = synth_event_trace_array(empty_synth_test, vals, ARRAY_SIZE(vals)); | |
230 | out: | |
231 | return ret; | |
232 | delete: | |
233 | /* We got an error after creating the event, delete it */ | |
234 | synth_event_delete("empty_synth_test"); | |
235 | free: | |
236 | kfree(buf); | |
237 | ||
238 | goto out; | |
239 | } | |
240 | ||
241 | static struct synth_field_desc create_synth_test_fields[] = { | |
242 | { .type = "pid_t", .name = "next_pid_field" }, | |
243 | { .type = "char[16]", .name = "next_comm_field" }, | |
244 | { .type = "u64", .name = "ts_ns" }, | |
bd82631d | 245 | { .type = "char[]", .name = "dynstring_field_1" }, |
9fe41efa TZ |
246 | { .type = "u64", .name = "ts_ms" }, |
247 | { .type = "unsigned int", .name = "cpu" }, | |
248 | { .type = "char[64]", .name = "my_string_field" }, | |
bd82631d | 249 | { .type = "char[]", .name = "dynstring_field_2" }, |
9fe41efa TZ |
250 | { .type = "int", .name = "my_int_field" }, |
251 | }; | |
252 | ||
253 | /* | |
254 | * Test synthetic event creation all at once from array of field | |
255 | * descriptors. | |
256 | */ | |
257 | static int __init test_create_synth_event(void) | |
258 | { | |
bd82631d | 259 | u64 vals[9]; |
9fe41efa TZ |
260 | int ret; |
261 | ||
262 | /* Create the create_synth_test event with the fields above */ | |
263 | ret = synth_event_create("create_synth_test", | |
264 | create_synth_test_fields, | |
265 | ARRAY_SIZE(create_synth_test_fields), | |
266 | THIS_MODULE); | |
267 | if (ret) | |
268 | goto out; | |
269 | ||
270 | /* | |
271 | * Now get the create_synth_test event file. We need to | |
272 | * prevent the instance and event from disappearing from | |
273 | * underneath us, which trace_get_event_file() does (though in | |
274 | * this case we're using the top-level instance which never | |
275 | * goes away). | |
276 | */ | |
277 | create_synth_test = trace_get_event_file(NULL, "synthetic", | |
278 | "create_synth_test"); | |
279 | if (IS_ERR(create_synth_test)) { | |
280 | ret = PTR_ERR(create_synth_test); | |
281 | goto delete; | |
282 | } | |
283 | ||
284 | /* Enable the event or you won't see anything */ | |
285 | ret = trace_array_set_clr_event(create_synth_test->tr, | |
286 | "synthetic", "create_synth_test", true); | |
287 | if (ret) { | |
288 | trace_put_event_file(create_synth_test); | |
289 | goto delete; | |
290 | } | |
291 | ||
292 | /* Create some bogus values just for testing */ | |
293 | ||
294 | vals[0] = 777; /* next_pid_field */ | |
279eef05 | 295 | vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */ |
9fe41efa | 296 | vals[2] = 1000000; /* ts_ns */ |
bd82631d TZ |
297 | vals[3] = (u64)(long)"xrayspecs"; /* dynstring_field_1 */ |
298 | vals[4] = 1000; /* ts_ms */ | |
299 | vals[5] = raw_smp_processor_id(); /* cpu */ | |
300 | vals[6] = (u64)(long)"thneed"; /* my_string_field */ | |
301 | vals[7] = (u64)(long)"kerplunk"; /* dynstring_field_2 */ | |
302 | vals[8] = 398; /* my_int_field */ | |
9fe41efa TZ |
303 | |
304 | /* Now generate a create_synth_test event */ | |
305 | ret = synth_event_trace_array(create_synth_test, vals, ARRAY_SIZE(vals)); | |
306 | out: | |
307 | return ret; | |
308 | delete: | |
309 | /* We got an error after creating the event, delete it */ | |
b111545d | 310 | synth_event_delete("create_synth_test"); |
9fe41efa TZ |
311 | |
312 | goto out; | |
313 | } | |
314 | ||
315 | /* | |
316 | * Test tracing a synthetic event by reserving trace buffer space, | |
317 | * then filling in fields one after another. | |
318 | */ | |
319 | static int __init test_add_next_synth_val(void) | |
320 | { | |
321 | struct synth_event_trace_state trace_state; | |
322 | int ret; | |
323 | ||
324 | /* Start by reserving space in the trace buffer */ | |
325 | ret = synth_event_trace_start(gen_synth_test, &trace_state); | |
326 | if (ret) | |
327 | return ret; | |
328 | ||
329 | /* Write some bogus values into the trace buffer, one after another */ | |
330 | ||
331 | /* next_pid_field */ | |
332 | ret = synth_event_add_next_val(777, &trace_state); | |
333 | if (ret) | |
334 | goto out; | |
335 | ||
336 | /* next_comm_field */ | |
279eef05 | 337 | ret = synth_event_add_next_val((u64)(long)"slinky", &trace_state); |
9fe41efa TZ |
338 | if (ret) |
339 | goto out; | |
340 | ||
341 | /* ts_ns */ | |
342 | ret = synth_event_add_next_val(1000000, &trace_state); | |
343 | if (ret) | |
344 | goto out; | |
345 | ||
346 | /* ts_ms */ | |
347 | ret = synth_event_add_next_val(1000, &trace_state); | |
348 | if (ret) | |
349 | goto out; | |
350 | ||
351 | /* cpu */ | |
3c18a9be | 352 | ret = synth_event_add_next_val(raw_smp_processor_id(), &trace_state); |
9fe41efa TZ |
353 | if (ret) |
354 | goto out; | |
355 | ||
356 | /* my_string_field */ | |
279eef05 | 357 | ret = synth_event_add_next_val((u64)(long)"thneed_2.01", &trace_state); |
9fe41efa TZ |
358 | if (ret) |
359 | goto out; | |
360 | ||
361 | /* my_int_field */ | |
362 | ret = synth_event_add_next_val(395, &trace_state); | |
363 | out: | |
364 | /* Finally, commit the event */ | |
365 | ret = synth_event_trace_end(&trace_state); | |
366 | ||
367 | return ret; | |
368 | } | |
369 | ||
370 | /* | |
371 | * Test tracing a synthetic event by reserving trace buffer space, | |
372 | * then filling in fields using field names, which can be done in any | |
373 | * order. | |
374 | */ | |
375 | static int __init test_add_synth_val(void) | |
376 | { | |
377 | struct synth_event_trace_state trace_state; | |
378 | int ret; | |
379 | ||
380 | /* Start by reserving space in the trace buffer */ | |
381 | ret = synth_event_trace_start(gen_synth_test, &trace_state); | |
382 | if (ret) | |
383 | return ret; | |
384 | ||
385 | /* Write some bogus values into the trace buffer, using field names */ | |
386 | ||
387 | ret = synth_event_add_val("ts_ns", 1000000, &trace_state); | |
388 | if (ret) | |
389 | goto out; | |
390 | ||
391 | ret = synth_event_add_val("ts_ms", 1000, &trace_state); | |
392 | if (ret) | |
393 | goto out; | |
394 | ||
3c18a9be | 395 | ret = synth_event_add_val("cpu", raw_smp_processor_id(), &trace_state); |
9fe41efa TZ |
396 | if (ret) |
397 | goto out; | |
398 | ||
399 | ret = synth_event_add_val("next_pid_field", 777, &trace_state); | |
400 | if (ret) | |
401 | goto out; | |
402 | ||
279eef05 | 403 | ret = synth_event_add_val("next_comm_field", (u64)(long)"silly putty", |
9fe41efa TZ |
404 | &trace_state); |
405 | if (ret) | |
406 | goto out; | |
407 | ||
279eef05 | 408 | ret = synth_event_add_val("my_string_field", (u64)(long)"thneed_9", |
9fe41efa TZ |
409 | &trace_state); |
410 | if (ret) | |
411 | goto out; | |
412 | ||
413 | ret = synth_event_add_val("my_int_field", 3999, &trace_state); | |
414 | out: | |
415 | /* Finally, commit the event */ | |
416 | ret = synth_event_trace_end(&trace_state); | |
417 | ||
418 | return ret; | |
419 | } | |
420 | ||
421 | /* | |
422 | * Test tracing a synthetic event all at once from array of values. | |
423 | */ | |
424 | static int __init test_trace_synth_event(void) | |
425 | { | |
426 | int ret; | |
427 | ||
428 | /* Trace some bogus values just for testing */ | |
bd82631d | 429 | ret = synth_event_trace(create_synth_test, 9, /* number of values */ |
279eef05 TZ |
430 | (u64)444, /* next_pid_field */ |
431 | (u64)(long)"clackers", /* next_comm_field */ | |
432 | (u64)1000000, /* ts_ns */ | |
bd82631d | 433 | (u64)(long)"viewmaster",/* dynstring_field_1 */ |
279eef05 | 434 | (u64)1000, /* ts_ms */ |
3c18a9be | 435 | (u64)raw_smp_processor_id(), /* cpu */ |
279eef05 | 436 | (u64)(long)"Thneed", /* my_string_field */ |
bd82631d | 437 | (u64)(long)"yoyos", /* dynstring_field_2 */ |
279eef05 | 438 | (u64)999); /* my_int_field */ |
9fe41efa TZ |
439 | return ret; |
440 | } | |
441 | ||
442 | static int __init synth_event_gen_test_init(void) | |
443 | { | |
444 | int ret; | |
445 | ||
446 | ret = test_gen_synth_cmd(); | |
447 | if (ret) | |
448 | return ret; | |
449 | ||
450 | ret = test_empty_synth_event(); | |
451 | if (ret) { | |
452 | WARN_ON(trace_array_set_clr_event(gen_synth_test->tr, | |
453 | "synthetic", | |
454 | "gen_synth_test", false)); | |
455 | trace_put_event_file(gen_synth_test); | |
456 | WARN_ON(synth_event_delete("gen_synth_test")); | |
457 | goto out; | |
458 | } | |
459 | ||
460 | ret = test_create_synth_event(); | |
461 | if (ret) { | |
462 | WARN_ON(trace_array_set_clr_event(gen_synth_test->tr, | |
463 | "synthetic", | |
464 | "gen_synth_test", false)); | |
465 | trace_put_event_file(gen_synth_test); | |
466 | WARN_ON(synth_event_delete("gen_synth_test")); | |
467 | ||
468 | WARN_ON(trace_array_set_clr_event(empty_synth_test->tr, | |
469 | "synthetic", | |
470 | "empty_synth_test", false)); | |
471 | trace_put_event_file(empty_synth_test); | |
472 | WARN_ON(synth_event_delete("empty_synth_test")); | |
473 | goto out; | |
474 | } | |
475 | ||
476 | ret = test_add_next_synth_val(); | |
477 | WARN_ON(ret); | |
478 | ||
479 | ret = test_add_synth_val(); | |
480 | WARN_ON(ret); | |
481 | ||
482 | ret = test_trace_synth_event(); | |
483 | WARN_ON(ret); | |
484 | out: | |
485 | return ret; | |
486 | } | |
487 | ||
488 | static void __exit synth_event_gen_test_exit(void) | |
489 | { | |
490 | /* Disable the event or you can't remove it */ | |
491 | WARN_ON(trace_array_set_clr_event(gen_synth_test->tr, | |
492 | "synthetic", | |
493 | "gen_synth_test", false)); | |
494 | ||
495 | /* Now give the file and instance back */ | |
496 | trace_put_event_file(gen_synth_test); | |
497 | ||
498 | /* Now unregister and free the synthetic event */ | |
499 | WARN_ON(synth_event_delete("gen_synth_test")); | |
500 | ||
501 | /* Disable the event or you can't remove it */ | |
502 | WARN_ON(trace_array_set_clr_event(empty_synth_test->tr, | |
503 | "synthetic", | |
504 | "empty_synth_test", false)); | |
505 | ||
506 | /* Now give the file and instance back */ | |
507 | trace_put_event_file(empty_synth_test); | |
508 | ||
509 | /* Now unregister and free the synthetic event */ | |
510 | WARN_ON(synth_event_delete("empty_synth_test")); | |
511 | ||
512 | /* Disable the event or you can't remove it */ | |
513 | WARN_ON(trace_array_set_clr_event(create_synth_test->tr, | |
514 | "synthetic", | |
515 | "create_synth_test", false)); | |
516 | ||
517 | /* Now give the file and instance back */ | |
518 | trace_put_event_file(create_synth_test); | |
519 | ||
520 | /* Now unregister and free the synthetic event */ | |
521 | WARN_ON(synth_event_delete("create_synth_test")); | |
522 | } | |
523 | ||
524 | module_init(synth_event_gen_test_init) | |
525 | module_exit(synth_event_gen_test_exit) | |
526 | ||
527 | MODULE_AUTHOR("Tom Zanussi"); | |
528 | MODULE_DESCRIPTION("synthetic event generation test"); | |
529 | MODULE_LICENSE("GPL v2"); |