• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3  * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4  *
5  */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <dirent.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <dlfcn.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <ctype.h>
16 #include <limits.h>
17 #include <libgen.h>
18 #include <sys/mount.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/sysinfo.h>
22 #include <time.h>
23 #include <event-parse.h>
24 #include <event-utils.h>
25 
26 #include "trace-cmd-private.h"
27 #include "trace-cmd-local.h"
28 
29 #define LOCAL_PLUGIN_DIR ".trace-cmd/plugins"
30 #define PROC_STACK_FILE "/proc/sys/kernel/stack_tracer_enabled"
31 
32 static bool debug;
33 static bool notimeout;
34 static int log_level = TEP_LOG_WARNING;
35 static FILE *logfp;
36 
37 const static struct {
38 	const char *clock_str;
39 	enum tracecmd_clocks clock_id;
40 } trace_clocks[] = {
41 	{"local", TRACECMD_CLOCK_LOCAL},
42 	{"global", TRACECMD_CLOCK_GLOBAL},
43 	{"counter", TRACECMD_CLOCK_COUNTER},
44 	{"uptime", TRACECMD_CLOCK_UPTIME},
45 	{"perf", TRACECMD_CLOCK_PERF},
46 	{"mono", TRACECMD_CLOCK_MONO},
47 	{"mono_raw", TRACECMD_CLOCK_MONO_RAW},
48 	{"boot", TRACECMD_CLOCK_BOOT},
49 	{"x86-tsc", TRACECMD_CLOCK_X86_TSC},
50 	{NULL, -1}
51 };
52 
53 /**
54  * tracecmd_clock_str2id - Convert ftrace clock name to clock ID
55  * @clock: Ftrace clock name
56  * Returns ID of the ftrace clock
57  */
tracecmd_clock_str2id(const char * clock)58 enum tracecmd_clocks tracecmd_clock_str2id(const char *clock)
59 {
60 	int i;
61 
62 	if (!clock)
63 		return TRACECMD_CLOCK_UNKNOWN;
64 
65 	for (i = 0; trace_clocks[i].clock_str; i++) {
66 		if (!strncmp(clock, trace_clocks[i].clock_str,
67 		    strlen(trace_clocks[i].clock_str)))
68 			return trace_clocks[i].clock_id;
69 	}
70 	return TRACECMD_CLOCK_UNKNOWN;
71 }
72 
73 /**
74  * tracecmd_clock_id2str - Convert clock ID to ftare clock name
75  * @clock: Clock ID
76  * Returns name of a ftrace clock
77  */
tracecmd_clock_id2str(enum tracecmd_clocks clock)78 const char *tracecmd_clock_id2str(enum tracecmd_clocks clock)
79 {
80 	int i;
81 
82 	for (i = 0; trace_clocks[i].clock_str; i++) {
83 		if (trace_clocks[i].clock_id == clock)
84 			return trace_clocks[i].clock_str;
85 	}
86 	return NULL;
87 }
88 
89 /**
90  * tracecmd_set_debug - Set debug mode of the tracecmd library
91  * @set_debug: The new "debug" mode. If true, the tracecmd library is
92  * in "debug" mode
93  */
tracecmd_set_debug(bool set_debug)94 void tracecmd_set_debug(bool set_debug)
95 {
96 	debug = set_debug;
97 
98 	if (set_debug)
99 		tracecmd_set_loglevel(TEP_LOG_DEBUG);
100 	else
101 		tracecmd_set_loglevel(TEP_LOG_CRITICAL);
102 }
103 
104 /**
105  * tracecmd_get_debug - Get debug mode of tracecmd library
106  * Returns true, if the tracecmd library is in debug mode.
107  *
108  */
tracecmd_get_debug(void)109 bool tracecmd_get_debug(void)
110 {
111 	return debug;
112 }
113 
114 /**
115  * tracecmd_set_notimeout - Do not timeout waiting for responses
116  * @set_notimeout: True or false to set notimeout mode.
117  *
118  * If @set_notimeout is true, then the library will not fail waiting for
119  * responses. This is useful when running the code under gdb.
120  * Note, if debug is set, then this makes no difference as it will always
121  * not timeout.
122  */
tracecmd_set_notimeout(bool set_notimeout)123 void tracecmd_set_notimeout(bool set_notimeout)
124 {
125 	notimeout = set_notimeout;
126 }
127 
128 /**
129  * tracecmd_get_notimeout - Get setting of notimeout of tracecmd library
130  * Returns true, if the tracecmd library has notimeout set.
131  *
132  */
tracecmd_get_notimeout(void)133 bool tracecmd_get_notimeout(void)
134 {
135 	return notimeout || debug;
136 }
137 
tracecmd_parse_proc_kallsyms(struct tep_handle * pevent,char * file,unsigned int size __maybe_unused)138 void tracecmd_parse_proc_kallsyms(struct tep_handle *pevent,
139 			 char *file, unsigned int size __maybe_unused)
140 {
141 	unsigned long long addr;
142 	int sav_errno;
143 	char *func;
144 	char *line;
145 	char *next = NULL;
146 	char *mod;
147 	char ch;
148 
149 	line = strtok_r(file, "\n", &next);
150 	while (line) {
151 		int func_start, func_end = 0;
152 		int mod_start, mod_end = 0;
153 		int n;
154 
155 		mod = NULL;
156 		sav_errno = errno;
157 		errno = 0;
158 		n = sscanf(line, "%16llx %c %n%*s%n%*1[\t][%n%*s%n",
159 			   &addr, &ch, &func_start, &func_end, &mod_start, &mod_end);
160 		if (errno)
161 			return;
162 		errno = sav_errno;
163 
164 		if (n != 2 || !func_end)
165 			return;
166 
167 		func = line + func_start;
168 		/*
169 		 * Hacks for
170 		 *  - arm arch that adds a lot of bogus '$a' functions
171 		 *  - x86-64 that reports per-cpu variable offsets as absolute
172 		 */
173 		if (func[0] != '$' && ch != 'A' && ch != 'a') {
174 			line[func_end] = 0;
175 			if (mod_end) {
176 				mod = line + mod_start;
177 				/* truncate the extra ']' */
178 				line[mod_end - 1] = 0;
179 			}
180 			tep_register_function(pevent, func, addr, mod);
181 		}
182 
183 		line = strtok_r(NULL, "\n", &next);
184 	}
185 }
186 
tracecmd_parse_ftrace_printk(struct tep_handle * pevent,char * file,unsigned int size __maybe_unused)187 void tracecmd_parse_ftrace_printk(struct tep_handle *pevent,
188 			 char *file, unsigned int size __maybe_unused)
189 {
190 	unsigned long long addr;
191 	char *printk;
192 	char *line;
193 	char *next = NULL;
194 	char *addr_str;
195 	char *fmt;
196 
197 	line = strtok_r(file, "\n", &next);
198 	while (line) {
199 		addr_str = strtok_r(line, ":", &fmt);
200 		if (!addr_str) {
201 			tracecmd_warning("printk format with empty entry");
202 			break;
203 		}
204 		addr = strtoull(addr_str, NULL, 16);
205 		/* fmt still has a space, skip it */
206 		printk = strdup(fmt+1);
207 		line = strtok_r(NULL, "\n", &next);
208 		tep_register_print_string(pevent, printk, addr);
209 		free(printk);
210 	}
211 }
212 
213 /**
214  * tracecmd_add_id - add an int to the event id list
215  * @list: list to add the id to
216  * @id: id to add
217  * @len: current length of list of ids.
218  *
219  * The typical usage is:
220  *
221  *    events = tracecmd_add_id(events, id, len++);
222  *
223  * Returns the new allocated list with the id included.
224  * the list will contain a '-1' at the end.
225  *
226  * The returned list should be freed with free().
227  */
tracecmd_add_id(int * list,int id,int len)228 int *tracecmd_add_id(int *list, int id, int len)
229 {
230 	if (!list)
231 		list = malloc(sizeof(*list) * 2);
232 	else
233 		list = realloc(list, sizeof(*list) * (len + 2));
234 	if (!list)
235 		return NULL;
236 
237 	list[len++] = id;
238 	list[len] = -1;
239 
240 	return list;
241 }
242 
243 struct add_plugin_data {
244 	int ret;
245 	int index;
246 	char **files;
247 };
248 
add_plugin_file(struct tep_handle * pevent,const char * path,const char * name,void * data)249 static void add_plugin_file(struct tep_handle *pevent, const char *path,
250 			   const char *name, void *data)
251 {
252 	struct add_plugin_data *pdata = data;
253 	char **ptr;
254 	int size;
255 	int i;
256 
257 	if (pdata->ret)
258 		return;
259 
260 	size = pdata->index + 2;
261 	ptr = realloc(pdata->files, sizeof(char *) * size);
262 	if (!ptr)
263 		goto out_free;
264 
265 	pdata->files = ptr;
266 	ptr[pdata->index] = strdup(name);
267 	if (!ptr[pdata->index])
268 		goto out_free;
269 
270 	pdata->index++;
271 	pdata->files[pdata->index] = NULL;
272 	return;
273 
274  out_free:
275 	for (i = 0; i < pdata->index; i++)
276 		free(pdata->files[i]);
277 	free(pdata->files);
278 	pdata->files = NULL;
279 	pdata->ret = errno;
280 }
281 
282 /**
283  * trace_util_find_plugin_files - find list of possible plugin files
284  * @suffix: The suffix of the plugin files to find
285  *
286  * Searches the plugin directory for files that end in @suffix, and
287  * will return an allocated array of file names, or NULL if none is
288  * found.
289  *
290  * Must check against TRACECMD_ISERR(ret) as if an error happens
291  * the errno will be returned with the TRACECMD_ERR_MSK to denote
292  * such an error occurred.
293  *
294  * Use trace_util_free_plugin_files() to free the result.
295  */
trace_util_find_plugin_files(const char * suffix)296 __hidden char **trace_util_find_plugin_files(const char *suffix)
297 {
298 	struct add_plugin_data pdata;
299 
300 	memset(&pdata, 0, sizeof(pdata));
301 
302 	tep_load_plugins_hook(NULL, suffix, add_plugin_file, &pdata);
303 
304 	if (pdata.ret)
305 		return TRACECMD_ERROR(pdata.ret);
306 
307 	return pdata.files;
308 }
309 
310 /**
311  * trace_util_free_plugin_files - free the result of trace_util_find_plugin_files()
312  * @files: The result from trace_util_find_plugin_files()
313  *
314  * Frees the contents that were allocated by trace_util_find_plugin_files().
315  */
trace_util_free_plugin_files(char ** files)316 void __hidden trace_util_free_plugin_files(char **files)
317 {
318 	int i;
319 
320 	if (!files || TRACECMD_ISERR(files))
321 		return;
322 
323 	for (i = 0; files[i]; i++) {
324 		free(files[i]);
325 	}
326 	free(files);
327 }
328 
get_source_plugins_dir(void)329 static char *get_source_plugins_dir(void)
330 {
331 	char *p, path[PATH_MAX+1];
332 	int ret;
333 
334 	ret = readlink("/proc/self/exe", path, PATH_MAX);
335 	if (ret > PATH_MAX || ret < 0)
336 		return NULL;
337 
338 	path[ret] = 0;
339 	dirname(path);
340 	p = strrchr(path, '/');
341 	if (!p)
342 		return NULL;
343 	/* Check if we are in the the source tree */
344 	if (strcmp(p, "/tracecmd") != 0)
345 		return NULL;
346 
347 	strcpy(p, "/lib/traceevent/plugins");
348 	return strdup(path);
349 }
350 
351 __hidden struct tep_plugin_list *
trace_load_plugins(struct tep_handle * tep,int flags)352 trace_load_plugins(struct tep_handle *tep, int flags)
353 {
354 	struct tep_plugin_list *list;
355 	char *path;
356 
357 	if (flags & TRACECMD_FL_LOAD_NO_PLUGINS)
358 		tep_set_flag(tep, TEP_DISABLE_PLUGINS);
359 	if (flags & TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS)
360 		tep_set_flag(tep, TEP_DISABLE_SYS_PLUGINS);
361 
362 	path = get_source_plugins_dir();
363 	if (path)
364 		tep_add_plugin_path(tep, path, TEP_PLUGIN_LAST);
365 	free(path);
366 
367 	list = tep_load_plugins(tep);
368 
369 	return list;
370 }
371 
372 /**
373  * tracecmd_set_loglevel - set log level of the library
374  * @level: desired level of the library messages
375  */
tracecmd_set_loglevel(enum tep_loglevel level)376 void tracecmd_set_loglevel(enum tep_loglevel level)
377 {
378 	log_level = level;
379 }
380 
tracecmd_warning(const char * fmt,...)381 void __weak tracecmd_warning(const char *fmt, ...)
382 {
383 	va_list ap;
384 
385 	if (log_level < TEP_LOG_WARNING)
386 		return;
387 
388 	va_start(ap, fmt);
389 	tep_vprint("libtracecmd", TEP_LOG_WARNING, true, fmt, ap);
390 	va_end(ap);
391 }
392 
tracecmd_info(const char * fmt,...)393 void __weak tracecmd_info(const char *fmt, ...)
394 {
395 	va_list ap;
396 
397 	if (log_level < TEP_LOG_INFO)
398 		return;
399 
400 	va_start(ap, fmt);
401 	tep_vprint("libtracecmd", TEP_LOG_INFO, false, fmt, ap);
402 	va_end(ap);
403 }
404 
tracecmd_critical(const char * fmt,...)405 void __weak tracecmd_critical(const char *fmt, ...)
406 {
407 	int ret;
408 	va_list ap;
409 
410 	if (log_level < TEP_LOG_CRITICAL)
411 		return;
412 
413 	va_start(ap, fmt);
414 	ret = tep_vprint("libtracecmd", TEP_LOG_CRITICAL, true, fmt, ap);
415 	va_end(ap);
416 
417 	if (debug) {
418 		if (!ret)
419 			ret = -1;
420 		exit(ret);
421 	}
422 }
423 
tracecmd_debug(const char * fmt,...)424 void __weak tracecmd_debug(const char *fmt, ...)
425 {
426 	va_list ap;
427 
428 	if (!tracecmd_get_debug())
429 		return;
430 
431 	va_start(ap, fmt);
432 	vprintf(fmt, ap);
433 	va_end(ap);
434 }
435 
436 #define LOG_BUF_SIZE 1024
__plog(const char * prefix,const char * fmt,va_list ap,FILE * fp)437 static void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp)
438 {
439 	char buf[LOG_BUF_SIZE];
440 	int r;
441 
442 	r = vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
443 
444 	if (r > LOG_BUF_SIZE)
445 		r = LOG_BUF_SIZE;
446 
447 	if (logfp) {
448 		fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
449 		fflush(logfp);
450 		return;
451 	}
452 
453 	fprintf(fp, "%.*s", r, buf);
454 }
455 
tracecmd_plog(const char * fmt,...)456 void tracecmd_plog(const char *fmt, ...)
457 {
458 	va_list ap;
459 
460 	va_start(ap, fmt);
461 	__plog("", fmt, ap, stdout);
462 	va_end(ap);
463 	/* Make sure it gets to the screen, in case we crash afterward */
464 	fflush(stdout);
465 }
466 
tracecmd_plog_error(const char * fmt,...)467 void tracecmd_plog_error(const char *fmt, ...)
468 {
469 	va_list ap;
470 	char *str = "";
471 
472 	va_start(ap, fmt);
473 	__plog("Error: ", fmt, ap, stderr);
474 	va_end(ap);
475 	if (errno)
476 		str = strerror(errno);
477 	if (logfp)
478 		fprintf(logfp, "\n%s\n", str);
479 	else
480 		fprintf(stderr, "\n%s\n", str);
481 }
482 
483 /**
484  * tracecmd_set_logfile - Set file for logging
485  * @logfile: Name of the log file
486  *
487  * Returns 0 on successful completion or -1 in case of error
488  */
tracecmd_set_logfile(char * logfile)489 int tracecmd_set_logfile(char *logfile)
490 {
491 	if (logfp)
492 		fclose(logfp);
493 	logfp = fopen(logfile, "w");
494 	if (!logfp)
495 		return -1;
496 	return 0;
497 }
498 
499 /**
500  * tracecmd_stack_tracer_status - Check stack trace status
501  * @status: Returned stack trace status:
502  *             0 - not configured, disabled
503  *             non 0 - enabled
504  *
505  * Returns -1 in case of an error, 0 if file does not exist
506  * (stack tracer not configured in kernel) or 1 on successful completion.
507  */
tracecmd_stack_tracer_status(int * status)508 int tracecmd_stack_tracer_status(int *status)
509 {
510 	struct stat stat_buf;
511 	char buf[64];
512 	long num;
513 	int fd;
514 	int n;
515 
516 	if (stat(PROC_STACK_FILE, &stat_buf) < 0) {
517 		/* stack tracer not configured on running kernel */
518 		*status = 0; /* not configured means disabled */
519 		return 0;
520 	}
521 
522 	fd = open(PROC_STACK_FILE, O_RDONLY);
523 
524 	if (fd < 0)
525 		return -1;
526 
527 	n = read(fd, buf, sizeof(buf));
528 	close(fd);
529 
530 	if (n <= 0)
531 		return -1;
532 
533 	if (n >= sizeof(buf))
534 		return -1;
535 
536 	buf[n] = 0;
537 
538 	errno = 0;
539 	num = strtol(buf, NULL, 10);
540 
541 	/* Check for various possible errors */
542 	if (num > INT_MAX || num < INT_MIN || (!num && errno))
543 		return -1;
544 
545 	*status = num;
546 	return 1; /* full success */
547 }
548 
549 /**
550  * tracecmd_count_cpus - Get the number of CPUs in the system
551  *
552  * Returns the number of CPUs in the system, or 0 in case of an error
553  */
tracecmd_count_cpus(void)554 int tracecmd_count_cpus(void)
555 {
556 	static int once;
557 	char buf[1024];
558 	int cpus = 0;
559 	char *pbuf;
560 	size_t *pn;
561 	FILE *fp;
562 	size_t n;
563 	int r;
564 
565 	cpus = sysconf(_SC_NPROCESSORS_CONF);
566 	if (cpus > 0)
567 		return cpus;
568 
569 	if (!once) {
570 		once++;
571 		tracecmd_warning("sysconf could not determine number of CPUS");
572 	}
573 
574 	/* Do the hack to figure out # of CPUS */
575 	n = 1024;
576 	pn = &n;
577 	pbuf = buf;
578 
579 	fp = fopen("/proc/cpuinfo", "r");
580 	if (!fp) {
581 		tracecmd_critical("Can not read cpuinfo");
582 		return 0;
583 	}
584 
585 	while ((r = getline(&pbuf, pn, fp)) >= 0) {
586 		char *p;
587 
588 		if (strncmp(buf, "processor", 9) != 0)
589 			continue;
590 		for (p = buf+9; isspace(*p); p++)
591 			;
592 		if (*p == ':')
593 			cpus++;
594 	}
595 	fclose(fp);
596 
597 	return cpus;
598 }
599 
600 #define FNV_64_PRIME 0x100000001b3ULL
601 /*
602  * tracecmd_generate_traceid - Generate a unique ID, used to identify
603  *			       the current tracing session
604  *
605  * Returns unique ID
606  */
tracecmd_generate_traceid(void)607 unsigned long long tracecmd_generate_traceid(void)
608 {
609 	unsigned long long hash = 0;
610 	unsigned char *ustr;
611 	struct sysinfo sinfo;
612 	struct timespec ts;
613 	char *str = NULL;
614 
615 	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
616 	sysinfo(&sinfo);
617 	asprintf(&str, "%ld %ld %ld %ld %ld %ld %ld %ld %d",
618 		 ts.tv_sec, ts.tv_nsec,
619 		 sinfo.loads[0], sinfo.loads[1], sinfo.loads[2],
620 		 sinfo.freeram, sinfo.sharedram, sinfo.freeswap,
621 		 sinfo.procs);
622 	if (!str)
623 		return 0;
624 	ustr = (unsigned char *)str;
625 	hash = 0;
626 	while (*ustr) {
627 		hash ^= (unsigned long long)*ustr++;
628 		hash *= FNV_64_PRIME;
629 	}
630 
631 	free(str);
632 	return hash;
633 }
634 
635 /*
636  * tracecmd_default_file_version - Get default trace file version of the library
637  *
638  * Returns the default trace file version
639  */
tracecmd_default_file_version(void)640 int tracecmd_default_file_version(void)
641 {
642 	return FILE_VERSION_DEFAULT;
643 }
644 
tracecmd_is_version_supported(unsigned int version)645 bool tracecmd_is_version_supported(unsigned int version)
646 {
647 	if (version <= FILE_VERSION_MAX)
648 		return true;
649 	return false;
650 }
651 
tracecmd_lib_init(void)652 static void __attribute__ ((constructor)) tracecmd_lib_init(void)
653 {
654 	tracecmd_compress_init();
655 }
656 
tracecmd_lib_free(void)657 static void __attribute__((destructor)) tracecmd_lib_free(void)
658 {
659 	tracecmd_compress_free();
660 }
661 
check_file_state(unsigned long file_version,int current_state,int new_state)662 __hidden bool check_file_state(unsigned long file_version, int current_state, int new_state)
663 {
664 	if (file_version >= FILE_VERSION_SECTIONS) {
665 		if (current_state < TRACECMD_FILE_INIT)
666 			return false;
667 
668 		return true;
669 	}
670 
671 	switch (new_state) {
672 	case TRACECMD_FILE_HEADERS:
673 	case TRACECMD_FILE_FTRACE_EVENTS:
674 	case TRACECMD_FILE_ALL_EVENTS:
675 	case TRACECMD_FILE_KALLSYMS:
676 	case TRACECMD_FILE_PRINTK:
677 	case TRACECMD_FILE_CMD_LINES:
678 	case TRACECMD_FILE_CPU_COUNT:
679 		if (current_state == (new_state - 1))
680 			return true;
681 		break;
682 	case TRACECMD_FILE_OPTIONS:
683 		if (file_version < FILE_VERSION_SECTIONS && current_state == TRACECMD_FILE_CPU_COUNT)
684 			return true;
685 		break;
686 	case TRACECMD_FILE_CPU_LATENCY:
687 	case TRACECMD_FILE_CPU_FLYRECORD:
688 		if (current_state == TRACECMD_FILE_OPTIONS)
689 			return true;
690 		break;
691 	}
692 
693 	return false;
694 }
695