1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4 *
5 * Updates:
6 * Copyright (C) 2019, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
7 *
8 */
9 #include <stdlib.h>
10 #include <sys/mount.h>
11 #include <sys/stat.h>
12 #include <linux/limits.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <errno.h>
17
18 #include <event-parse.h>
19 #include <event-utils.h>
20 #include "tracefs.h"
21 #include "tracefs-local.h"
22
23 #define TRACEFS_PATH "/sys/kernel/tracing"
24 #define DEBUGFS_PATH "/sys/kernel/debug"
25
26 #define ERROR_LOG "error_log"
27
28 #define _STR(x) #x
29 #define STR(x) _STR(x)
30
31 static int log_level = TEP_LOG_CRITICAL;
32 static char *custom_tracing_dir;
33
34 /**
35 * tracefs_set_loglevel - set log level of the library
36 * @level: desired level of the library messages
37 */
tracefs_set_loglevel(enum tep_loglevel level)38 void tracefs_set_loglevel(enum tep_loglevel level)
39 {
40 log_level = level;
41 tep_set_loglevel(level);
42 }
43
tracefs_warning(const char * fmt,...)44 void __weak tracefs_warning(const char *fmt, ...)
45 {
46 va_list ap;
47
48 if (log_level < TEP_LOG_WARNING)
49 return;
50
51 va_start(ap, fmt);
52 tep_vprint("libtracefs", TEP_LOG_WARNING, true, fmt, ap);
53 va_end(ap);
54 }
55
mount_tracefs(void)56 static int mount_tracefs(void)
57 {
58 struct stat st;
59 int ret;
60
61 /* make sure debugfs exists */
62 ret = stat(TRACEFS_PATH, &st);
63 if (ret < 0)
64 return -1;
65
66 ret = mount("nodev", TRACEFS_PATH,
67 "tracefs", 0, NULL);
68
69 return ret;
70 }
71
mount_debugfs(void)72 static int mount_debugfs(void)
73 {
74 struct stat st;
75 int ret;
76
77 /* make sure debugfs exists */
78 ret = stat(DEBUGFS_PATH, &st);
79 if (ret < 0)
80 return -1;
81
82 ret = mount("nodev", DEBUGFS_PATH,
83 "debugfs", 0, NULL);
84
85 return ret;
86 }
87
88 /* Exported for testing purpose only */
find_tracing_dir(bool debugfs,bool mount)89 __hidden char *find_tracing_dir(bool debugfs, bool mount)
90 {
91 char *debug_str = NULL;
92 char fspath[PATH_MAX+1];
93 char *tracing_dir;
94 char type[100];
95 int use_debug = 0;
96 FILE *fp;
97
98 fp = fopen("/proc/mounts", "r");
99 if (!fp) {
100 tracefs_warning("Can't open /proc/mounts for read");
101 return NULL;
102 }
103
104 while (fscanf(fp, "%*s %"
105 STR(PATH_MAX)
106 "s %99s %*s %*d %*d\n",
107 fspath, type) == 2) {
108 if (!debugfs && strcmp(type, "tracefs") == 0)
109 break;
110 if (!debug_str && strcmp(type, "debugfs") == 0) {
111 if (debugfs)
112 break;
113 debug_str = strdup(fspath);
114 if (!debug_str) {
115 fclose(fp);
116 return NULL;
117 }
118 }
119 }
120 fclose(fp);
121
122 if (debugfs) {
123 if (strcmp(type, "debugfs") != 0) {
124 if (!mount || mount_debugfs() < 0)
125 return NULL;
126 strcpy(fspath, DEBUGFS_PATH);
127 }
128 } else if (strcmp(type, "tracefs") != 0) {
129 if (!mount || mount_tracefs() < 0) {
130 if (debug_str) {
131 strncpy(fspath, debug_str, PATH_MAX);
132 fspath[PATH_MAX] = 0;
133 } else {
134 if (!mount || mount_debugfs() < 0) {
135 if (mount)
136 tracefs_warning("debugfs not mounted, please mount");
137 free(debug_str);
138 return NULL;
139 }
140 strcpy(fspath, DEBUGFS_PATH);
141 }
142 use_debug = 1;
143 } else
144 strcpy(fspath, TRACEFS_PATH);
145 }
146 free(debug_str);
147
148 if (use_debug) {
149 int ret;
150
151 ret = asprintf(&tracing_dir, "%s/tracing", fspath);
152 if (ret < 0)
153 return NULL;
154 } else {
155 tracing_dir = strdup(fspath);
156 if (!tracing_dir)
157 return NULL;
158 }
159
160 return tracing_dir;
161 }
162
163 /**
164 * tracefs_tracing_dir_is_mounted - test if the tracing dir is already mounted
165 * @mount: Mount it if it is not already mounted
166 * @path: the path to the tracing directory if mounted or was mounted
167 *
168 * Returns 1 if the tracing directory is already mounted and 0 if it is not.
169 * If @mount is set and it fails to mount, it returns -1.
170 *
171 * If path is not NULL, and the tracing directory is or was mounted, it holds
172 * the path to the tracing directory. It must not be freed.
173 */
tracefs_tracing_dir_is_mounted(bool mount,const char ** path)174 int tracefs_tracing_dir_is_mounted(bool mount, const char **path)
175 {
176 const char *dir;
177
178 dir = find_tracing_dir(false, false);
179 if (dir) {
180 if (path)
181 *path = dir;
182 return 1;
183 }
184 if (!mount)
185 return 0;
186
187 dir = find_tracing_dir(false, mount);
188 if (!dir)
189 return -1;
190 if (path)
191 *path = dir;
192 return 0;
193 }
194
195 /**
196 * trace_find_tracing_dir - Find tracing directory
197 * @debugfs: Boolean to just return the debugfs directory
198 *
199 * Returns string containing the full path to the system's tracing directory.
200 * The string must be freed by free()
201 */
trace_find_tracing_dir(bool debugfs)202 __hidden char *trace_find_tracing_dir(bool debugfs)
203 {
204 return find_tracing_dir(debugfs, false);
205 }
206
207 /**
208 * tracefs_set_tracing_dir - Set location of the tracing directory
209 * @tracing_dir: full path to the system's tracing directory mount point.
210 *
211 * Set the location to the system's tracing directory. This API should be used
212 * to set a custom location of the tracing directory. There is no need to call
213 * it if the location is standard, in that case the library will auto detect it.
214 *
215 * Returns 0 on success, -1 otherwise.
216 */
tracefs_set_tracing_dir(char * tracing_dir)217 int tracefs_set_tracing_dir(char *tracing_dir)
218 {
219 if (custom_tracing_dir) {
220 free(custom_tracing_dir);
221 custom_tracing_dir = NULL;
222 }
223
224 if (tracing_dir) {
225 custom_tracing_dir = strdup(tracing_dir);
226 if (!custom_tracing_dir)
227 return -1;
228 }
229
230 return 0;
231 }
232
233 /* Used to check if the directory is still mounted */
test_dir(const char * dir,const char * file)234 static int test_dir(const char *dir, const char *file)
235 {
236 char path[strlen(dir) + strlen(file) + 2];
237 struct stat st;
238
239 sprintf(path, "%s/%s", dir, file);
240 return stat(path, &st) < 0 ? 0 : 1;
241 }
242
243 /**
244 * tracefs_tracing_dir - Get tracing directory
245 *
246 * Returns string containing the full path to the system's tracing directory.
247 * The returned string must *not* be freed.
248 */
tracefs_tracing_dir(void)249 const char *tracefs_tracing_dir(void)
250 {
251 static char *tracing_dir;
252
253 /* Do not check custom_tracing_dir */
254 if (custom_tracing_dir)
255 return custom_tracing_dir;
256
257 if (tracing_dir && test_dir(tracing_dir, "trace"))
258 return tracing_dir;
259
260 free(tracing_dir);
261 tracing_dir = find_tracing_dir(false, true);
262 return tracing_dir;
263 }
264
265 /**
266 * tracefs_debug_dir - Get debugfs directory path
267 *
268 * Returns string containing the full path to the system's debugfs directory.
269 *
270 * The returned string must *not* be freed.
271 */
tracefs_debug_dir(void)272 const char *tracefs_debug_dir(void)
273 {
274 static const char *debug_dir;
275
276 if (debug_dir && test_dir(debug_dir, "tracing"))
277 return debug_dir;
278
279 debug_dir = find_tracing_dir(true, true);
280 return debug_dir;
281 }
282
283 /**
284 * tracefs_get_tracing_file - Get tracing file
285 * @name: tracing file name
286 *
287 * Returns string containing the full path to a tracing file in
288 * the system's tracing directory.
289 *
290 * Must use tracefs_put_tracing_file() to free the returned string.
291 */
tracefs_get_tracing_file(const char * name)292 char *tracefs_get_tracing_file(const char *name)
293 {
294 const char *tracing;
295 char *file;
296 int ret;
297
298 if (!name)
299 return NULL;
300
301 tracing = tracefs_tracing_dir();
302 if (!tracing)
303 return NULL;
304
305 ret = asprintf(&file, "%s/%s", tracing, name);
306 if (ret < 0)
307 return NULL;
308
309 return file;
310 }
311
312 /**
313 * tracefs_put_tracing_file - Free tracing file or directory name
314 *
315 * Frees tracing file or directory, returned by
316 * tracefs_get_tracing_file()API.
317 */
tracefs_put_tracing_file(char * name)318 void tracefs_put_tracing_file(char *name)
319 {
320 free(name);
321 }
322
323 /* The function is copied from trace-cmd */
strstrip(char * str)324 __hidden char *strstrip(char *str)
325 {
326 char *s;
327
328 if (!str)
329 return NULL;
330
331 s = str + strlen(str) - 1;
332 while (s >= str && isspace(*s))
333 s--;
334 s++;
335 *s = '\0';
336
337 for (s = str; *s && isspace(*s); s++)
338 ;
339
340 return s;
341 }
342
str_read_file(const char * file,char ** buffer,bool warn)343 __hidden int str_read_file(const char *file, char **buffer, bool warn)
344 {
345 char stbuf[BUFSIZ];
346 char *buf = NULL;
347 int size = 0;
348 char *nbuf;
349 int fd;
350 int r;
351
352 fd = open(file, O_RDONLY);
353 if (fd < 0) {
354 if (warn)
355 tracefs_warning("File %s not found", file);
356 return -1;
357 }
358
359 do {
360 r = read(fd, stbuf, BUFSIZ);
361 if (r <= 0)
362 continue;
363 nbuf = realloc(buf, size+r+1);
364 if (!nbuf) {
365 if (warn)
366 tracefs_warning("Failed to allocate file buffer");
367 size = -1;
368 break;
369 }
370 buf = nbuf;
371 memcpy(buf+size, stbuf, r);
372 size += r;
373 } while (r > 0);
374
375 close(fd);
376 if (r == 0 && size > 0) {
377 buf[size] = '\0';
378 *buffer = buf;
379 } else
380 free(buf);
381
382 return size;
383 }
384
385 /**
386 * tracefs_error_all - return the content of the error log
387 * @instance: The instance to read the error log from (NULL for top level)
388 *
389 * Return NULL if the log is empty, or on error (where errno will be
390 * set. Otherwise the content of the entire log is returned in a string
391 * that must be freed with free().
392 */
tracefs_error_all(struct tracefs_instance * instance)393 char *tracefs_error_all(struct tracefs_instance *instance)
394 {
395 char *content;
396 char *path;
397 int size;
398
399 errno = 0;
400
401 path = tracefs_instance_get_file(instance, ERROR_LOG);
402 if (!path)
403 return NULL;
404 size = str_read_file(path, &content, false);
405 tracefs_put_tracing_file(path);
406
407 if (size <= 0)
408 return NULL;
409
410 return content;
411 }
412
413 enum line_states {
414 START,
415 CARROT,
416 };
417
418 /**
419 * tracefs_error_last - return the last error logged
420 * @instance: The instance to read the error log from (NULL for top level)
421 *
422 * Return NULL if the log is empty, or on error (where errno will be
423 * set. Otherwise a string containing the content of the last error shown
424 * in the log that must be freed with free().
425 */
tracefs_error_last(struct tracefs_instance * instance)426 char *tracefs_error_last(struct tracefs_instance *instance)
427 {
428 enum line_states state = START;
429 char *content;
430 char *ret;
431 bool done = false;
432 int size;
433 int i;
434
435 content = tracefs_error_all(instance);
436 if (!content)
437 return NULL;
438
439 size = strlen(content);
440 if (!size) /* Should never happen */
441 return content;
442
443 for (i = size - 1; i > 0; i--) {
444 switch (state) {
445 case START:
446 if (content[i] == '\n') {
447 /* Remove extra new lines */
448 content[i] = '\0';
449 break;
450 }
451 if (content[i] == '^')
452 state = CARROT;
453 break;
454 case CARROT:
455 if (content[i] == '\n') {
456 /* Remember last new line */
457 size = i;
458 break;
459 }
460 if (content[i] == '^') {
461 /* Go just passed the last newline */
462 i = size + 1;
463 done = true;
464 }
465 break;
466 }
467 if (done)
468 break;
469 }
470
471 if (i) {
472 ret = strdup(content + i);
473 free(content);
474 } else {
475 ret = content;
476 }
477
478 return ret;
479 }
480
481 /**
482 * tracefs_error_clear - clear the error log of an instance
483 * @instance: The instance to clear (NULL for top level)
484 *
485 * Clear the content of the error log.
486 *
487 * Returns 0 on success, -1 otherwise.
488 */
tracefs_error_clear(struct tracefs_instance * instance)489 int tracefs_error_clear(struct tracefs_instance *instance)
490 {
491 return tracefs_instance_file_clear(instance, ERROR_LOG);
492 }
493
494 /**
495 * tracefs_list_free - free list if strings, returned by APIs
496 * tracefs_event_systems()
497 * tracefs_system_events()
498 *
499 *@list pointer to a list of strings, the last one must be NULL
500 */
tracefs_list_free(char ** list)501 void tracefs_list_free(char **list)
502 {
503 int i;
504
505 if (!list)
506 return;
507
508 for (i = 0; list[i]; i++)
509 free(list[i]);
510
511 /* The allocated list is before the user visible portion */
512 list--;
513 free(list);
514 }
515
516
trace_list_create_empty(void)517 __hidden char ** trace_list_create_empty(void)
518 {
519 char **list;
520
521 list = calloc(2, sizeof(*list));
522
523 return list ? &list[1] : NULL;
524 }
525
526 /**
527 * tracefs_list_add - create or extend a string list
528 * @list: The list to add to (NULL to create a new one)
529 * @string: The string to append to @list.
530 *
531 * If @list is NULL, a new list is created with the first element
532 * a copy of @string, and the second element is NULL.
533 *
534 * If @list is not NULL, it is then reallocated to include
535 * a new element and a NULL terminator, and will return the new
536 * allocated array on success, and the one passed in should be
537 * ignored.
538 *
539 * Returns an allocated string array that must be freed with
540 * tracefs_list_free() on success. On failure, NULL is returned
541 * and the @list is untouched.
542 */
tracefs_list_add(char ** list,const char * string)543 char **tracefs_list_add(char **list, const char *string)
544 {
545 unsigned long size = 0;
546 char *str = strdup(string);
547 char **new_list;
548
549 if (!str)
550 return NULL;
551
552 /*
553 * The returned list is really the address of the
554 * second entry of the list (&list[1]), the first
555 * entry contains the number of strings in the list.
556 */
557 if (list) {
558 list--;
559 size = *(unsigned long *)list;
560 }
561
562 new_list = realloc(list, sizeof(*list) * (size + 3));
563 if (!new_list) {
564 free(str);
565 return NULL;
566 }
567
568 list = new_list;
569 list[0] = (char *)(size + 1);
570 list++;
571 list[size++] = str;
572 list[size] = NULL;
573
574 return list;
575 }
576
577 /*
578 * trace_list_pop - Removes the last string added
579 * @list: The list to remove the last event from
580 *
581 * Returns 0 on success, -1 on error.
582 * Returns 1 if the list is empty or NULL.
583 */
trace_list_pop(char ** list)584 __hidden int trace_list_pop(char **list)
585 {
586 unsigned long size;
587
588 if (!list || list[0])
589 return 1;
590
591 list--;
592 size = *(unsigned long *)list;
593 /* size must be greater than zero */
594 if (!size)
595 return -1;
596 size--;
597 *list = (char *)size;
598 list++;
599 list[size] = NULL;
600 return 0;
601 }
602
603 /**
604 * tracefs_list_size - Return the number of strings in the list
605 * @list: The list to determine the size.
606 *
607 * Returns the number of elements in the list.
608 * If @list is NULL, then zero is returned.
609 */
tracefs_list_size(char ** list)610 int tracefs_list_size(char **list)
611 {
612 if (!list)
613 return 0;
614
615 list--;
616 return (int)*(unsigned long *)list;
617 }
618
619 /**
620 * tracefs_tracer_available - test if a tracer is available
621 * @tracing_dir: The directory that contains the tracing directory
622 * @tracer: The name of the tracer
623 *
624 * Return true if the tracer is available
625 */
tracefs_tracer_available(const char * tracing_dir,const char * tracer)626 bool tracefs_tracer_available(const char *tracing_dir, const char *tracer)
627 {
628 bool ret = false;
629 char **tracers = NULL;
630 int i;
631
632 tracers = tracefs_tracers(tracing_dir);
633 if (!tracers)
634 return false;
635
636 for (i = 0; tracers[i]; i++) {
637 if (strcmp(tracer, tracers[i]) == 0) {
638 ret = true;
639 break;
640 }
641 }
642
643 tracefs_list_free(tracers);
644 return ret;
645 }
646
647 /**
648 * tracefs_instance_get_buffer_percent - get the instance buffer percent
649 * @instance: The instance to get from (NULL for toplevel)
650 *
651 * Returns the buffer percent setting of the given instance.
652 * (-1 if not found).
653 */
tracefs_instance_get_buffer_percent(struct tracefs_instance * instance)654 int tracefs_instance_get_buffer_percent(struct tracefs_instance *instance)
655 {
656 long long val;
657 int ret;
658
659 ret = tracefs_instance_file_read_number(instance, "buffer_percent", &val);
660 return !ret ? (int)val : ret;
661 }
662
663 /**
664 * tracefs_instance_set_buffer_percent - set the instance buffer percent
665 * @instance: The instance to set (NULL for toplevel)
666 *
667 * Returns zero on success or -1 on error
668 */
tracefs_instance_set_buffer_percent(struct tracefs_instance * instance,int val)669 int tracefs_instance_set_buffer_percent(struct tracefs_instance *instance, int val)
670 {
671 return tracefs_instance_file_write_number(instance, "buffer_percent", val);
672 }
673