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 const 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 tracing_dir = find_tracing_dir(false, true);
261 return tracing_dir;
262 }
263
264 /**
265 * tracefs_debug_dir - Get debugfs directory path
266 *
267 * Returns string containing the full path to the system's debugfs directory.
268 *
269 * The returned string must *not* be freed.
270 */
tracefs_debug_dir(void)271 const char *tracefs_debug_dir(void)
272 {
273 static const char *debug_dir;
274
275 if (debug_dir && test_dir(debug_dir, "tracing"))
276 return debug_dir;
277
278 debug_dir = find_tracing_dir(true, true);
279 return debug_dir;
280 }
281
282 /**
283 * tracefs_get_tracing_file - Get tracing file
284 * @name: tracing file name
285 *
286 * Returns string containing the full path to a tracing file in
287 * the system's tracing directory.
288 *
289 * Must use tracefs_put_tracing_file() to free the returned string.
290 */
tracefs_get_tracing_file(const char * name)291 char *tracefs_get_tracing_file(const char *name)
292 {
293 const char *tracing;
294 char *file;
295 int ret;
296
297 if (!name)
298 return NULL;
299
300 tracing = tracefs_tracing_dir();
301 if (!tracing)
302 return NULL;
303
304 ret = asprintf(&file, "%s/%s", tracing, name);
305 if (ret < 0)
306 return NULL;
307
308 return file;
309 }
310
311 /**
312 * tracefs_put_tracing_file - Free tracing file or directory name
313 *
314 * Frees tracing file or directory, returned by
315 * tracefs_get_tracing_file()API.
316 */
tracefs_put_tracing_file(char * name)317 void tracefs_put_tracing_file(char *name)
318 {
319 free(name);
320 }
321
str_read_file(const char * file,char ** buffer,bool warn)322 __hidden int str_read_file(const char *file, char **buffer, bool warn)
323 {
324 char stbuf[BUFSIZ];
325 char *buf = NULL;
326 int size = 0;
327 char *nbuf;
328 int fd;
329 int r;
330
331 fd = open(file, O_RDONLY);
332 if (fd < 0) {
333 if (warn)
334 tracefs_warning("File %s not found", file);
335 return -1;
336 }
337
338 do {
339 r = read(fd, stbuf, BUFSIZ);
340 if (r <= 0)
341 continue;
342 nbuf = realloc(buf, size+r+1);
343 if (!nbuf) {
344 if (warn)
345 tracefs_warning("Failed to allocate file buffer");
346 size = -1;
347 break;
348 }
349 buf = nbuf;
350 memcpy(buf+size, stbuf, r);
351 size += r;
352 } while (r > 0);
353
354 close(fd);
355 if (r == 0 && size > 0) {
356 buf[size] = '\0';
357 *buffer = buf;
358 } else
359 free(buf);
360
361 return size;
362 }
363
364 /**
365 * tracefs_error_all - return the content of the error log
366 * @instance: The instance to read the error log from (NULL for top level)
367 *
368 * Return NULL if the log is empty, or on error (where errno will be
369 * set. Otherwise the content of the entire log is returned in a string
370 * that must be freed with free().
371 */
tracefs_error_all(struct tracefs_instance * instance)372 char *tracefs_error_all(struct tracefs_instance *instance)
373 {
374 char *content;
375 char *path;
376 int size;
377
378 errno = 0;
379
380 path = tracefs_instance_get_file(instance, ERROR_LOG);
381 if (!path)
382 return NULL;
383 size = str_read_file(path, &content, false);
384 tracefs_put_tracing_file(path);
385
386 if (size <= 0)
387 return NULL;
388
389 return content;
390 }
391
392 enum line_states {
393 START,
394 CARROT,
395 };
396
397 /**
398 * tracefs_error_last - return the last error logged
399 * @instance: The instance to read the error log from (NULL for top level)
400 *
401 * Return NULL if the log is empty, or on error (where errno will be
402 * set. Otherwise a string containing the content of the last error shown
403 * in the log that must be freed with free().
404 */
tracefs_error_last(struct tracefs_instance * instance)405 char *tracefs_error_last(struct tracefs_instance *instance)
406 {
407 enum line_states state = START;
408 char *content;
409 char *ret;
410 bool done = false;
411 int size;
412 int i;
413
414 content = tracefs_error_all(instance);
415 if (!content)
416 return NULL;
417
418 size = strlen(content);
419 if (!size) /* Should never happen */
420 return content;
421
422 for (i = size - 1; i > 0; i--) {
423 switch (state) {
424 case START:
425 if (content[i] == '\n') {
426 /* Remove extra new lines */
427 content[i] = '\0';
428 break;
429 }
430 if (content[i] == '^')
431 state = CARROT;
432 break;
433 case CARROT:
434 if (content[i] == '\n') {
435 /* Remember last new line */
436 size = i;
437 break;
438 }
439 if (content[i] == '^') {
440 /* Go just passed the last newline */
441 i = size + 1;
442 done = true;
443 }
444 break;
445 }
446 if (done)
447 break;
448 }
449
450 if (i) {
451 ret = strdup(content + i);
452 free(content);
453 } else {
454 ret = content;
455 }
456
457 return ret;
458 }
459
460 /**
461 * tracefs_error_clear - clear the error log of an instance
462 * @instance: The instance to clear (NULL for top level)
463 *
464 * Clear the content of the error log.
465 *
466 * Returns 0 on success, -1 otherwise.
467 */
tracefs_error_clear(struct tracefs_instance * instance)468 int tracefs_error_clear(struct tracefs_instance *instance)
469 {
470 return tracefs_instance_file_clear(instance, ERROR_LOG);
471 }
472
473 /**
474 * tracefs_list_free - free list if strings, returned by APIs
475 * tracefs_event_systems()
476 * tracefs_system_events()
477 *
478 *@list pointer to a list of strings, the last one must be NULL
479 */
tracefs_list_free(char ** list)480 void tracefs_list_free(char **list)
481 {
482 int i;
483
484 if (!list)
485 return;
486
487 for (i = 0; list[i]; i++)
488 free(list[i]);
489
490 /* The allocated list is before the user visible portion */
491 list--;
492 free(list);
493 }
494
495
trace_list_create_empty(void)496 __hidden char ** trace_list_create_empty(void)
497 {
498 char **list;
499
500 list = calloc(2, sizeof(*list));
501
502 return list ? &list[1] : NULL;
503 }
504
505 /**
506 * tracefs_list_add - create or extend a string list
507 * @list: The list to add to (NULL to create a new one)
508 * @string: The string to append to @list.
509 *
510 * If @list is NULL, a new list is created with the first element
511 * a copy of @string, and the second element is NULL.
512 *
513 * If @list is not NULL, it is then reallocated to include
514 * a new element and a NULL terminator, and will return the new
515 * allocated array on success, and the one passed in should be
516 * ignored.
517 *
518 * Returns an allocated string array that must be freed with
519 * tracefs_list_free() on success. On failure, NULL is returned
520 * and the @list is untouched.
521 */
tracefs_list_add(char ** list,const char * string)522 char **tracefs_list_add(char **list, const char *string)
523 {
524 unsigned long size = 0;
525 char *str = strdup(string);
526 char **new_list;
527
528 if (!str)
529 return NULL;
530
531 /*
532 * The returned list is really the address of the
533 * second entry of the list (&list[1]), the first
534 * entry contains the number of strings in the list.
535 */
536 if (list) {
537 list--;
538 size = *(unsigned long *)list;
539 }
540
541 new_list = realloc(list, sizeof(*list) * (size + 3));
542 if (!new_list) {
543 free(str);
544 return NULL;
545 }
546
547 list = new_list;
548 list[0] = (char *)(size + 1);
549 list++;
550 list[size++] = str;
551 list[size] = NULL;
552
553 return list;
554 }
555
556 /*
557 * trace_list_pop - Removes the last string added
558 * @list: The list to remove the last event from
559 *
560 * Returns 0 on success, -1 on error.
561 * Returns 1 if the list is empty or NULL.
562 */
trace_list_pop(char ** list)563 __hidden int trace_list_pop(char **list)
564 {
565 unsigned long size;
566
567 if (!list || list[0])
568 return 1;
569
570 list--;
571 size = *(unsigned long *)list;
572 /* size must be greater than zero */
573 if (!size)
574 return -1;
575 size--;
576 *list = (char *)size;
577 list++;
578 list[size] = NULL;
579 return 0;
580 }
581
582 /**
583 * tracefs_list_size - Return the number of strings in the list
584 * @list: The list to determine the size.
585 *
586 * Returns the number of elements in the list.
587 * If @list is NULL, then zero is returned.
588 */
tracefs_list_size(char ** list)589 int tracefs_list_size(char **list)
590 {
591 if (!list)
592 return 0;
593
594 list--;
595 return (int)*(unsigned long *)list;
596 }
597
598 /**
599 * tracefs_tracer_available - test if a tracer is available
600 * @tracing_dir: The directory that contains the tracing directory
601 * @tracer: The name of the tracer
602 *
603 * Return true if the tracer is available
604 */
tracefs_tracer_available(const char * tracing_dir,const char * tracer)605 bool tracefs_tracer_available(const char *tracing_dir, const char *tracer)
606 {
607 bool ret = false;
608 char **tracers = NULL;
609 int i;
610
611 tracers = tracefs_tracers(tracing_dir);
612 if (!tracers)
613 return false;
614
615 for (i = 0; tracers[i]; i++) {
616 if (strcmp(tracer, tracers[i]) == 0) {
617 ret = true;
618 break;
619 }
620 }
621
622 tracefs_list_free(tracers);
623 return ret;
624 }
625