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