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