1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <[email protected]>
4 *
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <time.h>
12 #include <dirent.h>
13
14 #include <CUnit/CUnit.h>
15 #include <CUnit/Basic.h>
16
17 #include "tracefs.h"
18
19 #define TRACEFS_SUITE "trasefs library"
20 #define TEST_INSTANCE_NAME "cunit_test_iter"
21 #define TEST_ARRAY_SIZE 500
22
23 static struct tracefs_instance *test_instance;
24 static struct tep_handle *test_tep;
25 struct test_sample {
26 int cpu;
27 int value;
28 };
29 static struct test_sample test_array[TEST_ARRAY_SIZE];
30 static int test_found;
31
test_callback(struct tep_event * event,struct tep_record * record,int cpu,void * context)32 static int test_callback(struct tep_event *event, struct tep_record *record,
33 int cpu, void *context)
34 {
35 struct tep_format_field *field;
36 struct test_sample *sample;
37 int *cpu_test = (int *)context;
38 int i;
39
40 if (cpu_test && *cpu_test >= 0 && *cpu_test != cpu)
41 return 0;
42 field = tep_find_field(event, "buf");
43 if (field) {
44 sample = ((struct test_sample *)(record->data + field->offset));
45 for (i = 0; i < TEST_ARRAY_SIZE; i++) {
46 if (test_array[i].value == sample->value &&
47 test_array[i].cpu == cpu) {
48 test_array[i].value = 0;
49 test_found++;
50 break;
51 }
52 }
53 }
54
55 return 0;
56 }
57
test_iter_write(void)58 static void test_iter_write(void)
59 {
60 int cpus = sysconf(_SC_NPROCESSORS_CONF);
61 cpu_set_t *cpuset, *cpusave;
62 int cpu_size;
63 char *path;
64 int i, fd;
65 int ret;
66 cpuset = CPU_ALLOC(cpus);
67 cpusave = CPU_ALLOC(cpus);
68 cpu_size = CPU_ALLOC_SIZE(cpus);
69 CPU_ZERO_S(cpu_size, cpuset);
70
71 sched_getaffinity(0, cpu_size, cpusave);
72
73 path = tracefs_instance_get_file(test_instance, "trace_marker");
74 CU_TEST(path != NULL);
75 fd = open(path, O_WRONLY);
76 tracefs_put_tracing_file(path);
77 CU_TEST(fd >= 0);
78
79 for (i = 0; i < TEST_ARRAY_SIZE; i++) {
80 test_array[i].cpu = rand() % cpus;
81 test_array[i].value = random();
82 if (!test_array[i].value)
83 test_array[i].value++;
84 CU_TEST(test_array[i].cpu < cpus);
85 CPU_ZERO_S(cpu_size, cpuset);
86 CPU_SET(test_array[i].cpu, cpuset);
87 sched_setaffinity(0, cpu_size, cpuset);
88 ret = write(fd, test_array + i, sizeof(struct test_sample));
89 CU_TEST(ret == sizeof(struct test_sample));
90 }
91
92 sched_setaffinity(0, cpu_size, cpusave);
93 close(fd);
94 }
95
96
iter_raw_events_on_cpu(int cpu)97 static void iter_raw_events_on_cpu(int cpu)
98 {
99 int check = 0;
100 int ret;
101 int i;
102
103 test_found = 0;
104 test_iter_write();
105 ret = tracefs_iterate_raw_events(test_tep, test_instance, NULL, 0,
106 test_callback, &cpu);
107 CU_TEST(ret == 0);
108 if (cpu < 0) {
109 CU_TEST(test_found == TEST_ARRAY_SIZE);
110 } else {
111 for (i = 0; i < TEST_ARRAY_SIZE; i++) {
112 if (test_array[i].cpu == cpu) {
113 check++;
114 CU_TEST(test_array[i].value == 0)
115 } else {
116 CU_TEST(test_array[i].value != 0)
117 }
118 }
119 CU_TEST(test_found == check);
120 }
121 }
122
test_iter_raw_events(void)123 static void test_iter_raw_events(void)
124 {
125 int cpus = sysconf(_SC_NPROCESSORS_CONF);
126 int ret;
127 int i;
128
129 ret = tracefs_iterate_raw_events(NULL, test_instance, NULL, 0, test_callback, NULL);
130 CU_TEST(ret < 0);
131 ret = tracefs_iterate_raw_events(test_tep, NULL, NULL, 0, test_callback, NULL);
132 CU_TEST(ret == 0);
133 ret = tracefs_iterate_raw_events(test_tep, test_instance, NULL, 0, NULL, NULL);
134 CU_TEST(ret < 0);
135
136 iter_raw_events_on_cpu(-1);
137 for (i = 0; i < cpus; i++)
138 iter_raw_events_on_cpu(i);
139 }
140
141 #define RAND_STR_SIZE 20
142 #define RAND_ASCII "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
get_rand_str()143 static const char *get_rand_str()
144 {
145 static char str[RAND_STR_SIZE];
146 static char sym[] = RAND_ASCII;
147 struct timespec clk;
148 int i;
149
150 clock_gettime(CLOCK_REALTIME, &clk);
151 srand(clk.tv_nsec);
152 for (i = 0; i < RAND_STR_SIZE; i++)
153 str[i] = sym[rand() % (sizeof(sym) - 1)];
154
155 str[RAND_STR_SIZE - 1] = 0;
156 return str;
157 }
158
test_trace_file(void)159 static void test_trace_file(void)
160 {
161 const char *tmp = get_rand_str();
162 const char *tdir;
163 struct stat st;
164 char *file;
165
166 tdir = tracefs_tracing_dir();
167 CU_TEST(tdir != NULL);
168 CU_TEST(stat(tdir, &st) == 0);
169 CU_TEST(S_ISDIR(st.st_mode));
170
171 file = tracefs_get_tracing_file(NULL);
172 CU_TEST(file == NULL);
173 file = tracefs_get_tracing_file(tmp);
174 CU_TEST(file != NULL);
175 CU_TEST(stat(file, &st) != 0);
176 tracefs_put_tracing_file(file);
177
178 file = tracefs_get_tracing_file("trace");
179 CU_TEST(file != NULL);
180 CU_TEST(stat(file, &st) == 0);
181 tracefs_put_tracing_file(file);
182 }
183
test_instance_file_read(struct tracefs_instance * inst,char * fname)184 static void test_instance_file_read(struct tracefs_instance *inst, char *fname)
185 {
186 const char *tdir = tracefs_tracing_dir();
187 char buf[BUFSIZ];
188 char *fpath;
189 char *file;
190 size_t fsize = 0;
191 int size = 0;
192 int fd;
193
194 if (inst) {
195 CU_TEST(asprintf(&fpath, "%s/instances/%s/%s",
196 tdir, tracefs_instance_get_name(inst), fname) > 0);
197 } else {
198 CU_TEST(asprintf(&fpath, "%s/%s", tdir, fname) > 0);
199 }
200
201 memset(buf, 0, BUFSIZ);
202 fd = open(fpath, O_RDONLY);
203 CU_TEST(fd >= 0);
204 fsize = read(fd, buf, BUFSIZ);
205 CU_TEST(fsize >= 0);
206 close(fd);
207 buf[BUFSIZ - 1] = 0;
208
209 file = tracefs_instance_file_read(inst, fname, &size);
210 CU_TEST(file != NULL);
211 CU_TEST(size == fsize);
212 CU_TEST(strcmp(file, buf) == 0);
213
214 free(fpath);
215 free(file);
216 }
217
218 #define ALL_TRACERS "available_tracers"
219 #define CUR_TRACER "current_tracer"
220 #define PER_CPU "per_cpu"
test_instance_file(void)221 static void test_instance_file(void)
222 {
223 struct tracefs_instance *instance = NULL;
224 struct tracefs_instance *second = NULL;
225 const char *name = get_rand_str();
226 const char *inst_name = NULL;
227 const char *tdir;
228 char *inst_file;
229 char *inst_dir;
230 struct stat st;
231 char *fname;
232 char *file1;
233 char *file2;
234 char *tracer;
235 int size;
236 int ret;
237
238 tdir = tracefs_tracing_dir();
239 CU_TEST(tdir != NULL);
240 CU_TEST(asprintf(&inst_dir, "%s/instances/%s", tdir, name) > 0);
241 CU_TEST(stat(inst_dir, &st) != 0);
242
243 CU_TEST(tracefs_instance_exists(name) == false);
244 instance = tracefs_instance_create(name);
245 CU_TEST(instance != NULL);
246 CU_TEST(tracefs_instance_is_new(instance));
247 second = tracefs_instance_create(name);
248 CU_TEST(second != NULL);
249 CU_TEST(!tracefs_instance_is_new(second));
250 tracefs_instance_free(second);
251 CU_TEST(tracefs_instance_exists(name) == true);
252 CU_TEST(stat(inst_dir, &st) == 0);
253 CU_TEST(S_ISDIR(st.st_mode));
254 inst_name = tracefs_instance_get_name(instance);
255 CU_TEST(inst_name != NULL);
256 CU_TEST(strcmp(inst_name, name) == 0);
257
258 fname = tracefs_instance_get_dir(NULL);
259 CU_TEST(fname != NULL);
260 CU_TEST(strcmp(fname, tdir) == 0);
261 free(fname);
262
263 fname = tracefs_instance_get_dir(instance);
264 CU_TEST(fname != NULL);
265 CU_TEST(strcmp(fname, inst_dir) == 0);
266 free(fname);
267
268 CU_TEST(asprintf(&fname, "%s/"ALL_TRACERS, tdir) > 0);
269 CU_TEST(fname != NULL);
270 inst_file = tracefs_instance_get_file(NULL, ALL_TRACERS);
271 CU_TEST(inst_file != NULL);
272 CU_TEST(strcmp(fname, inst_file) == 0);
273 tracefs_put_tracing_file(inst_file);
274 free(fname);
275
276 CU_TEST(asprintf(&fname, "%s/instances/%s/"ALL_TRACERS, tdir, name) > 0);
277 CU_TEST(fname != NULL);
278 CU_TEST(stat(fname, &st) == 0);
279 inst_file = tracefs_instance_get_file(instance, ALL_TRACERS);
280 CU_TEST(inst_file != NULL);
281 CU_TEST(strcmp(fname, inst_file) == 0);
282
283 test_instance_file_read(NULL, ALL_TRACERS);
284 test_instance_file_read(instance, ALL_TRACERS);
285
286 file1 = tracefs_instance_file_read(instance, ALL_TRACERS, NULL);
287 CU_TEST(file1 != NULL);
288 tracer = strtok(file1, " ");
289 CU_TEST(tracer != NULL);
290 ret = tracefs_instance_file_write(instance, CUR_TRACER, tracer);
291 CU_TEST(ret == strlen(tracer));
292 file2 = tracefs_instance_file_read(instance, CUR_TRACER, &size);
293 CU_TEST(file2 != NULL);
294 CU_TEST(size >= strlen(tracer));
295 CU_TEST(strncmp(file2, tracer, strlen(tracer)) == 0);
296 free(file1);
297 free(file2);
298
299 tracefs_put_tracing_file(inst_file);
300 free(fname);
301
302 CU_TEST(tracefs_file_exists(NULL, (char *)name) == false);
303 CU_TEST(tracefs_dir_exists(NULL, (char *)name) == false);
304 CU_TEST(tracefs_file_exists(instance, (char *)name) == false);
305 CU_TEST(tracefs_dir_exists(instance, (char *)name) == false);
306
307 CU_TEST(tracefs_file_exists(NULL, CUR_TRACER) == true);
308 CU_TEST(tracefs_dir_exists(NULL, CUR_TRACER) == false);
309 CU_TEST(tracefs_file_exists(instance, CUR_TRACER) == true);
310 CU_TEST(tracefs_dir_exists(instance, CUR_TRACER) == false);
311
312 CU_TEST(tracefs_file_exists(NULL, PER_CPU) == false);
313 CU_TEST(tracefs_dir_exists(NULL, PER_CPU) == true);
314 CU_TEST(tracefs_file_exists(instance, PER_CPU) == false);
315 CU_TEST(tracefs_dir_exists(instance, PER_CPU) == true);
316
317 CU_TEST(tracefs_instance_destroy(NULL) != 0);
318 CU_TEST(tracefs_instance_destroy(instance) == 0);
319 CU_TEST(tracefs_instance_destroy(instance) != 0);
320 tracefs_instance_free(instance);
321 CU_TEST(stat(inst_dir, &st) != 0);
322 free(inst_dir);
323 }
324
exclude_string(char ** strings,char * name)325 static void exclude_string(char **strings, char *name)
326 {
327 int i;
328
329 for (i = 0; strings[i]; i++) {
330 if (strcmp(strings[i], name) == 0) {
331 free(strings[i]);
332 strings[i] = strdup("/");
333 return;
334 }
335 }
336 }
337
test_check_files(const char * fdir,char ** files)338 static void test_check_files(const char *fdir, char **files)
339 {
340 struct dirent *dent;
341 DIR *dir;
342 int i;
343
344 dir = opendir(fdir);
345 CU_TEST(dir != NULL);
346
347 while ((dent = readdir(dir)))
348 exclude_string(files, dent->d_name);
349
350 closedir(dir);
351
352 for (i = 0; files[i]; i++)
353 CU_TEST(files[i][0] == '/');
354 }
355
test_system_event(void)356 static void test_system_event(void)
357 {
358 const char *tdir;
359 char **systems;
360 char **events;
361 char *sdir = NULL;
362
363 tdir = tracefs_tracing_dir();
364 CU_TEST(tdir != NULL);
365
366 systems = tracefs_event_systems(tdir);
367 CU_TEST(systems != NULL);
368
369 events = tracefs_system_events(tdir, systems[0]);
370 CU_TEST(events != NULL);
371
372 asprintf(&sdir, "%s/events/%s", tdir, systems[0]);
373 CU_TEST(sdir != NULL);
374 test_check_files(sdir, events);
375 free(sdir);
376 sdir = NULL;
377
378 asprintf(&sdir, "%s/events", tdir);
379 CU_TEST(sdir != NULL);
380 test_check_files(sdir, systems);
381
382 tracefs_list_free(systems);
383 tracefs_list_free(events);
384
385 free(sdir);
386 }
387
test_tracers(void)388 static void test_tracers(void)
389 {
390 const char *tdir;
391 char **tracers;
392 char *tfile;
393 char *tracer;
394 int i;
395
396 tdir = tracefs_tracing_dir();
397 CU_TEST(tdir != NULL);
398
399 tracers = tracefs_tracers(tdir);
400 CU_TEST(tracers != NULL);
401
402 tfile = tracefs_instance_file_read(NULL, ALL_TRACERS, NULL);
403
404 tracer = strtok(tfile, " ");
405 while (tracer) {
406 exclude_string(tracers, tracer);
407 tracer = strtok(NULL, " ");
408 }
409
410 for (i = 0; tracers[i]; i++)
411 CU_TEST(tracers[i][0] == '/');
412
413 tracefs_list_free(tracers);
414 free(tfile);
415 }
416
test_check_events(struct tep_handle * tep,char * system,bool exist)417 static void test_check_events(struct tep_handle *tep, char *system, bool exist)
418 {
419 struct dirent *dent;
420 char file[PATH_MAX];
421 char buf[1024];
422 char *edir = NULL;
423 const char *tdir;
424 DIR *dir;
425 int fd;
426
427 tdir = tracefs_tracing_dir();
428 CU_TEST(tdir != NULL);
429
430 asprintf(&edir, "%s/events/%s", tdir, system);
431 dir = opendir(edir);
432 CU_TEST(dir != NULL);
433
434 while ((dent = readdir(dir))) {
435 if (dent->d_name[0] == '.')
436 continue;
437 sprintf(file, "%s/%s/id", edir, dent->d_name);
438 fd = open(file, O_RDONLY);
439 if (fd < 0)
440 continue;
441 CU_TEST(read(fd, buf, 1024) > 0);
442 if (exist) {
443 CU_TEST(tep_find_event(tep, atoi(buf)) != NULL);
444 } else {
445 CU_TEST(tep_find_event(tep, atoi(buf)) == NULL);
446 }
447
448 close(fd);
449 }
450
451 closedir(dir);
452 free(edir);
453
454 }
455
test_local_events(void)456 static void test_local_events(void)
457 {
458 struct tep_handle *tep;
459 const char *tdir;
460 char **systems;
461 char *lsystems[3];
462 int i;
463
464 tdir = tracefs_tracing_dir();
465 CU_TEST(tdir != NULL);
466
467 tep = tracefs_local_events(tdir);
468 CU_TEST(tep != NULL);
469
470 systems = tracefs_event_systems(tdir);
471 CU_TEST(systems != NULL);
472
473 for (i = 0; systems[i]; i++)
474 test_check_events(tep, systems[i], true);
475 tep_free(tep);
476
477 memset(lsystems, 0, sizeof(lsystems));
478 for (i = 0; systems[i]; i++) {
479 if (!lsystems[0])
480 lsystems[0] = systems[i];
481 else if (!lsystems[2])
482 lsystems[2] = systems[i];
483 else
484 break;
485 }
486
487 if (lsystems[0] && lsystems[2]) {
488 tep = tracefs_local_events_system(tdir,
489 (const char * const *)lsystems);
490 CU_TEST(tep != NULL);
491 test_check_events(tep, lsystems[0], true);
492 test_check_events(tep, lsystems[2], false);
493 }
494 tep_free(tep);
495
496 tep = tep_alloc();
497 CU_TEST(tep != NULL);
498 CU_TEST(tracefs_fill_local_events(tdir, tep, NULL) == 0);
499 for (i = 0; systems[i]; i++)
500 test_check_events(tep, systems[i], true);
501
502 tep_free(tep);
503
504 tracefs_list_free(systems);
505 }
506
507 struct test_walk_instance {
508 struct tracefs_instance *instance;
509 bool found;
510 };
511 #define WALK_COUNT 10
test_instances_walk_cb(const char * name,void * data)512 int test_instances_walk_cb(const char *name, void *data)
513 {
514 struct test_walk_instance *instances = (struct test_walk_instance *)data;
515 int i;
516
517 CU_TEST(instances != NULL);
518 CU_TEST(name != NULL);
519
520 for (i = 0; i < WALK_COUNT; i++) {
521 if (!strcmp(name,
522 tracefs_instance_get_name(instances[i].instance))) {
523 instances[i].found = true;
524 break;
525 }
526 }
527
528 return 0;
529 }
530
test_instances_walk(void)531 static void test_instances_walk(void)
532 {
533 struct test_walk_instance instances[WALK_COUNT];
534 int i;
535
536 memset(instances, 0, WALK_COUNT * sizeof(struct test_walk_instance));
537 for (i = 0; i < WALK_COUNT; i++) {
538 instances[i].instance = tracefs_instance_create(get_rand_str());
539 CU_TEST(instances[i].instance != NULL);
540 }
541
542 CU_TEST(tracefs_instances_walk(test_instances_walk_cb, instances) == 0);
543 for (i = 0; i < WALK_COUNT; i++) {
544 CU_TEST(instances[i].found);
545 tracefs_instance_destroy(instances[i].instance);
546 instances[i].found = false;
547 }
548
549 CU_TEST(tracefs_instances_walk(test_instances_walk_cb, instances) == 0);
550 for (i = 0; i < WALK_COUNT; i++) {
551 CU_TEST(!instances[i].found);
552 tracefs_instance_free(instances[i].instance);
553 }
554 }
555
current_clock_check(const char * clock)556 static void current_clock_check(const char *clock)
557 {
558 int size = 0;
559 char *clocks;
560 char *str;
561
562 clocks = tracefs_instance_file_read(test_instance, "trace_clock", &size);
563 CU_TEST(clocks != NULL);
564 CU_TEST(size > strlen(clock));
565 str = strstr(clocks, clock);
566 CU_TEST(str != NULL);
567 CU_TEST(str != clocks);
568 CU_TEST(*(str - 1) == '[');
569 CU_TEST(*(str + strlen(clock)) == ']');
570 free(clocks);
571 }
572
test_get_clock(void)573 static void test_get_clock(void)
574 {
575 const char *clock;
576
577 clock = tracefs_get_clock(test_instance);
578 CU_TEST(clock != NULL);
579 current_clock_check(clock);
580 free((char *)clock);
581 }
582
test_suite_destroy(void)583 static int test_suite_destroy(void)
584 {
585 tracefs_instance_destroy(test_instance);
586 tracefs_instance_free(test_instance);
587 tep_free(test_tep);
588 return 0;
589 }
590
test_suite_init(void)591 static int test_suite_init(void)
592 {
593 const char *systems[] = {"ftrace", NULL};
594
595 test_tep = tracefs_local_events_system(NULL, systems);
596 if (test_tep == NULL)
597 return 1;
598 test_instance = tracefs_instance_create(TEST_INSTANCE_NAME);
599 if (!test_instance)
600 return 1;
601
602 return 0;
603 }
604
test_tracefs_lib(void)605 void test_tracefs_lib(void)
606 {
607 CU_pSuite suite = NULL;
608
609 suite = CU_add_suite(TRACEFS_SUITE, test_suite_init, test_suite_destroy);
610 if (suite == NULL) {
611 fprintf(stderr, "Suite \"%s\" cannot be ceated\n", TRACEFS_SUITE);
612 return;
613 }
614 CU_add_test(suite, "tracing file / directory APIs",
615 test_trace_file);
616 CU_add_test(suite, "instance file / directory APIs",
617 test_instance_file);
618 CU_add_test(suite, "systems and events APIs",
619 test_system_event);
620 CU_add_test(suite, "tracefs_iterate_raw_events API",
621 test_iter_raw_events);
622 CU_add_test(suite, "tracefs_tracers API",
623 test_tracers);
624 CU_add_test(suite, "tracefs_local events API",
625 test_local_events);
626 CU_add_test(suite, "tracefs_instances_walk API",
627 test_instances_walk);
628 CU_add_test(suite, "tracefs_get_clock API",
629 test_get_clock);
630 }
631