• 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 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