• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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