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