aboutsummaryrefslogtreecommitdiff
path: root/lib/trace-cmd/trace-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/trace-cmd/trace-util.c')
-rw-r--r--lib/trace-cmd/trace-util.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/lib/trace-cmd/trace-util.c b/lib/trace-cmd/trace-util.c
new file mode 100644
index 00000000..9564c81a
--- /dev/null
+++ b/lib/trace-cmd/trace-util.c
@@ -0,0 +1,692 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <limits.h>
+#include <libgen.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <time.h>
+#include <event-parse.h>
+#include <event-utils.h>
+
+#include "trace-cmd-private.h"
+#include "trace-cmd-local.h"
+
+#define LOCAL_PLUGIN_DIR ".trace-cmd/plugins"
+#define PROC_STACK_FILE "/proc/sys/kernel/stack_tracer_enabled"
+
+static bool debug;
+static int log_level = TEP_LOG_INFO;
+static FILE *logfp;
+
+const static struct {
+ const char *clock_str;
+ enum tracecmd_clocks clock_id;
+} trace_clocks[] = {
+ {"local", TRACECMD_CLOCK_LOCAL},
+ {"global", TRACECMD_CLOCK_GLOBAL},
+ {"counter", TRACECMD_CLOCK_COUNTER},
+ {"uptime", TRACECMD_CLOCK_UPTIME},
+ {"perf", TRACECMD_CLOCK_PERF},
+ {"mono", TRACECMD_CLOCK_MONO},
+ {"mono_raw", TRACECMD_CLOCK_MONO_RAW},
+ {"boot", TRACECMD_CLOCK_BOOT},
+ {"x86-tsc", TRACECMD_CLOCK_X86_TSC},
+ {NULL, -1}
+};
+
+/**
+ * tracecmd_clock_str2id - Convert ftrace clock name to clock ID
+ * @clock: Ftrace clock name
+ * Returns ID of the ftrace clock
+ */
+enum tracecmd_clocks tracecmd_clock_str2id(const char *clock)
+{
+ int i;
+
+ if (!clock)
+ return TRACECMD_CLOCK_UNKNOWN;
+
+ for (i = 0; trace_clocks[i].clock_str; i++) {
+ if (!strncmp(clock, trace_clocks[i].clock_str,
+ strlen(trace_clocks[i].clock_str)))
+ return trace_clocks[i].clock_id;
+ }
+ return TRACECMD_CLOCK_UNKNOWN;
+}
+
+/**
+ * tracecmd_clock_id2str - Convert clock ID to ftare clock name
+ * @clock: Clock ID
+ * Returns name of a ftrace clock
+ */
+const char *tracecmd_clock_id2str(enum tracecmd_clocks clock)
+{
+ int i;
+
+ for (i = 0; trace_clocks[i].clock_str; i++) {
+ if (trace_clocks[i].clock_id == clock)
+ return trace_clocks[i].clock_str;
+ }
+ return NULL;
+}
+
+/**
+ * tracecmd_set_debug - Set debug mode of the tracecmd library
+ * @set_debug: The new "debug" mode. If true, the tracecmd library is
+ * in "debug" mode
+ */
+void tracecmd_set_debug(bool set_debug)
+{
+ debug = set_debug;
+
+ if (set_debug)
+ tracecmd_set_loglevel(TEP_LOG_DEBUG);
+ else
+ tracecmd_set_loglevel(TEP_LOG_CRITICAL);
+}
+
+/**
+ * tracecmd_get_debug - Get debug mode of tracecmd library
+ * Returns true, if the tracecmd library is in debug mode.
+ *
+ */
+bool tracecmd_get_debug(void)
+{
+ return debug;
+}
+
+void tracecmd_parse_cmdlines(struct tep_handle *pevent,
+ char *file, int size __maybe_unused)
+{
+ char *comm;
+ char *line;
+ char *next = NULL;
+ int pid;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ sscanf(line, "%d %m[^\n]s", &pid, &comm);
+ tep_register_comm(pevent, comm, pid);
+ free(comm);
+ line = strtok_r(NULL, "\n", &next);
+ }
+}
+
+void tracecmd_parse_proc_kallsyms(struct tep_handle *pevent,
+ char *file, unsigned int size __maybe_unused)
+{
+ unsigned long long addr;
+ int sav_errno;
+ char *func;
+ char *line;
+ char *next = NULL;
+ char *mod;
+ char ch;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ int func_start, func_end = 0;
+ int mod_start, mod_end = 0;
+ int n;
+
+ mod = NULL;
+ sav_errno = errno;
+ errno = 0;
+ n = sscanf(line, "%16llx %c %n%*s%n%*1[\t][%n%*s%n",
+ &addr, &ch, &func_start, &func_end, &mod_start, &mod_end);
+ if (errno)
+ return;
+ errno = sav_errno;
+
+ if (n != 2 || !func_end)
+ return;
+
+ func = line + func_start;
+ /*
+ * Hacks for
+ * - arm arch that adds a lot of bogus '$a' functions
+ * - x86-64 that reports per-cpu variable offsets as absolute
+ */
+ if (func[0] != '$' && ch != 'A' && ch != 'a') {
+ line[func_end] = 0;
+ if (mod_end) {
+ mod = line + mod_start;
+ /* truncate the extra ']' */
+ line[mod_end - 1] = 0;
+ }
+ tep_register_function(pevent, func, addr, mod);
+ }
+
+ line = strtok_r(NULL, "\n", &next);
+ }
+}
+
+void tracecmd_parse_ftrace_printk(struct tep_handle *pevent,
+ char *file, unsigned int size __maybe_unused)
+{
+ unsigned long long addr;
+ char *printk;
+ char *line;
+ char *next = NULL;
+ char *addr_str;
+ char *fmt;
+
+ line = strtok_r(file, "\n", &next);
+ while (line) {
+ addr_str = strtok_r(line, ":", &fmt);
+ if (!addr_str) {
+ tracecmd_warning("printk format with empty entry");
+ break;
+ }
+ addr = strtoull(addr_str, NULL, 16);
+ /* fmt still has a space, skip it */
+ printk = strdup(fmt+1);
+ line = strtok_r(NULL, "\n", &next);
+ tep_register_print_string(pevent, printk, addr);
+ free(printk);
+ }
+}
+
+/**
+ * tracecmd_add_id - add an int to the event id list
+ * @list: list to add the id to
+ * @id: id to add
+ * @len: current length of list of ids.
+ *
+ * The typical usage is:
+ *
+ * events = tracecmd_add_id(events, id, len++);
+ *
+ * Returns the new allocated list with the id included.
+ * the list will contain a '-1' at the end.
+ *
+ * The returned list should be freed with free().
+ */
+int *tracecmd_add_id(int *list, int id, int len)
+{
+ if (!list)
+ list = malloc(sizeof(*list) * 2);
+ else
+ list = realloc(list, sizeof(*list) * (len + 2));
+ if (!list)
+ return NULL;
+
+ list[len++] = id;
+ list[len] = -1;
+
+ return list;
+}
+
+struct add_plugin_data {
+ int ret;
+ int index;
+ char **files;
+};
+
+static void add_plugin_file(struct tep_handle *pevent, const char *path,
+ const char *name, void *data)
+{
+ struct add_plugin_data *pdata = data;
+ char **ptr;
+ int size;
+ int i;
+
+ if (pdata->ret)
+ return;
+
+ size = pdata->index + 2;
+ ptr = realloc(pdata->files, sizeof(char *) * size);
+ if (!ptr)
+ goto out_free;
+
+ ptr[pdata->index] = strdup(name);
+ if (!ptr[pdata->index])
+ goto out_free;
+
+ pdata->files = ptr;
+ pdata->index++;
+ pdata->files[pdata->index] = NULL;
+ return;
+
+ out_free:
+ for (i = 0; i < pdata->index; i++)
+ free(pdata->files[i]);
+ free(pdata->files);
+ pdata->files = NULL;
+ pdata->ret = errno;
+}
+
+/**
+ * trace_util_find_plugin_files - find list of possible plugin files
+ * @suffix: The suffix of the plugin files to find
+ *
+ * Searches the plugin directory for files that end in @suffix, and
+ * will return an allocated array of file names, or NULL if none is
+ * found.
+ *
+ * Must check against TRACECMD_ISERR(ret) as if an error happens
+ * the errno will be returned with the TRACECMD_ERR_MSK to denote
+ * such an error occurred.
+ *
+ * Use trace_util_free_plugin_files() to free the result.
+ */
+__hidden char **trace_util_find_plugin_files(const char *suffix)
+{
+ struct add_plugin_data pdata;
+
+ memset(&pdata, 0, sizeof(pdata));
+
+ tep_load_plugins_hook(NULL, suffix, add_plugin_file, &pdata);
+
+ if (pdata.ret)
+ return TRACECMD_ERROR(pdata.ret);
+
+ return pdata.files;
+}
+
+/**
+ * trace_util_free_plugin_files - free the result of trace_util_find_plugin_files()
+ * @files: The result from trace_util_find_plugin_files()
+ *
+ * Frees the contents that were allocated by trace_util_find_plugin_files().
+ */
+void __hidden trace_util_free_plugin_files(char **files)
+{
+ int i;
+
+ if (!files || TRACECMD_ISERR(files))
+ return;
+
+ for (i = 0; files[i]; i++) {
+ free(files[i]);
+ }
+ free(files);
+}
+
+static char *get_source_plugins_dir(void)
+{
+ char *p, path[PATH_MAX+1];
+ int ret;
+
+ ret = readlink("/proc/self/exe", path, PATH_MAX);
+ if (ret > PATH_MAX || ret < 0)
+ return NULL;
+
+ path[ret] = 0;
+ dirname(path);
+ p = strrchr(path, '/');
+ if (!p)
+ return NULL;
+ /* Check if we are in the the source tree */
+ if (strcmp(p, "/tracecmd") != 0)
+ return NULL;
+
+ strcpy(p, "/lib/traceevent/plugins");
+ return strdup(path);
+}
+
+__hidden struct tep_plugin_list *
+trace_load_plugins(struct tep_handle *tep, int flags)
+{
+ struct tep_plugin_list *list;
+ char *path;
+
+ if (flags & TRACECMD_FL_LOAD_NO_PLUGINS)
+ tep_set_flag(tep, TEP_DISABLE_PLUGINS);
+ if (flags & TRACECMD_FL_LOAD_NO_SYSTEM_PLUGINS)
+ tep_set_flag(tep, TEP_DISABLE_SYS_PLUGINS);
+
+ path = get_source_plugins_dir();
+ if (path)
+ tep_add_plugin_path(tep, path, TEP_PLUGIN_LAST);
+ free(path);
+
+ list = tep_load_plugins(tep);
+
+ return list;
+}
+
+/**
+ * tracecmd_set_loglevel - set log level of the library
+ * @level: desired level of the library messages
+ */
+void tracecmd_set_loglevel(enum tep_loglevel level)
+{
+ log_level = level;
+ tracefs_set_loglevel(level);
+ tep_set_loglevel(level);
+}
+
+void __weak tracecmd_warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (log_level < TEP_LOG_WARNING)
+ return;
+
+ va_start(ap, fmt);
+ tep_vprint("libtracecmd", TEP_LOG_WARNING, true, fmt, ap);
+ va_end(ap);
+}
+
+void __weak tracecmd_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (log_level < TEP_LOG_INFO)
+ return;
+
+ va_start(ap, fmt);
+ tep_vprint("libtracecmd", TEP_LOG_INFO, false, fmt, ap);
+ va_end(ap);
+}
+
+void __weak tracecmd_critical(const char *fmt, ...)
+{
+ int ret;
+ va_list ap;
+
+ if (log_level < TEP_LOG_CRITICAL)
+ return;
+
+ va_start(ap, fmt);
+ ret = tep_vprint("libtracecmd", TEP_LOG_CRITICAL, true, fmt, ap);
+ va_end(ap);
+
+ if (debug) {
+ if (!ret)
+ ret = -1;
+ exit(ret);
+ }
+}
+
+void __weak tracecmd_debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!tracecmd_get_debug())
+ return;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+#define LOG_BUF_SIZE 1024
+static void __plog(const char *prefix, const char *fmt, va_list ap, FILE *fp)
+{
+ static int newline = 1;
+ char buf[LOG_BUF_SIZE];
+ int r;
+
+ r = vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
+
+ if (r > LOG_BUF_SIZE)
+ r = LOG_BUF_SIZE;
+
+ if (logfp) {
+ if (newline)
+ fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
+ else
+ fprintf(logfp, "[%d]%s%.*s", getpid(), prefix, r, buf);
+ newline = buf[r - 1] == '\n';
+ fflush(logfp);
+ return;
+ }
+
+ fprintf(fp, "%.*s", r, buf);
+}
+
+void tracecmd_plog(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ __plog("", fmt, ap, stdout);
+ va_end(ap);
+ /* Make sure it gets to the screen, in case we crash afterward */
+ fflush(stdout);
+}
+
+void tracecmd_plog_error(const char *fmt, ...)
+{
+ va_list ap;
+ char *str = "";
+
+ va_start(ap, fmt);
+ __plog("Error: ", fmt, ap, stderr);
+ va_end(ap);
+ if (errno)
+ str = strerror(errno);
+ if (logfp)
+ fprintf(logfp, "\n%s\n", str);
+ else
+ fprintf(stderr, "\n%s\n", str);
+}
+
+/**
+ * tracecmd_set_logfile - Set file for logging
+ * @logfile: Name of the log file
+ *
+ * Returns 0 on successful completion or -1 in case of error
+ */
+int tracecmd_set_logfile(char *logfile)
+{
+ if (logfp)
+ fclose(logfp);
+ logfp = fopen(logfile, "w");
+ if (!logfp)
+ return -1;
+ return 0;
+}
+
+/**
+ * tracecmd_stack_tracer_status - Check stack trace status
+ * @status: Returned stack trace status:
+ * 0 - not configured, disabled
+ * non 0 - enabled
+ *
+ * Returns -1 in case of an error, 0 if file does not exist
+ * (stack tracer not configured in kernel) or 1 on successful completion.
+ */
+int tracecmd_stack_tracer_status(int *status)
+{
+ struct stat stat_buf;
+ char buf[64];
+ long num;
+ int fd;
+ int n;
+
+ if (stat(PROC_STACK_FILE, &stat_buf) < 0) {
+ /* stack tracer not configured on running kernel */
+ *status = 0; /* not configured means disabled */
+ return 0;
+ }
+
+ fd = open(PROC_STACK_FILE, O_RDONLY);
+
+ if (fd < 0)
+ return -1;
+
+ n = read(fd, buf, sizeof(buf));
+ close(fd);
+
+ if (n <= 0)
+ return -1;
+
+ if (n >= sizeof(buf))
+ return -1;
+
+ buf[n] = 0;
+
+ num = strtol(buf, NULL, 10);
+
+ /* Check for various possible errors */
+ if (num > INT_MAX || num < INT_MIN || (!num && errno))
+ return -1;
+
+ *status = num;
+ return 1; /* full success */
+}
+
+/**
+ * tracecmd_count_cpus - Get the number of CPUs in the system
+ *
+ * Returns the number of CPUs in the system, or 0 in case of an error
+ */
+int tracecmd_count_cpus(void)
+{
+ static int once;
+ char buf[1024];
+ int cpus = 0;
+ char *pbuf;
+ size_t *pn;
+ FILE *fp;
+ size_t n;
+ int r;
+
+ cpus = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpus > 0)
+ return cpus;
+
+ if (!once) {
+ once++;
+ tracecmd_warning("sysconf could not determine number of CPUS");
+ }
+
+ /* Do the hack to figure out # of CPUS */
+ n = 1024;
+ pn = &n;
+ pbuf = buf;
+
+ fp = fopen("/proc/cpuinfo", "r");
+ if (!fp) {
+ tracecmd_critical("Can not read cpuinfo");
+ return 0;
+ }
+
+ while ((r = getline(&pbuf, pn, fp)) >= 0) {
+ char *p;
+
+ if (strncmp(buf, "processor", 9) != 0)
+ continue;
+ for (p = buf+9; isspace(*p); p++)
+ ;
+ if (*p == ':')
+ cpus++;
+ }
+ fclose(fp);
+
+ return cpus;
+}
+
+#define FNV_64_PRIME 0x100000001b3ULL
+/*
+ * tracecmd_generate_traceid - Generate a unique ID, used to identify
+ * the current tracing session
+ *
+ * Returns unique ID
+ */
+unsigned long long tracecmd_generate_traceid(void)
+{
+ unsigned long long hash = 0;
+ unsigned char *ustr;
+ struct sysinfo sinfo;
+ struct timespec ts;
+ char *str = NULL;
+
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+ sysinfo(&sinfo);
+ asprintf(&str, "%ld %ld %ld %ld %ld %ld %ld %ld %d",
+ ts.tv_sec, ts.tv_nsec,
+ sinfo.loads[0], sinfo.loads[1], sinfo.loads[2],
+ sinfo.freeram, sinfo.sharedram, sinfo.freeswap,
+ sinfo.procs);
+ if (!str)
+ return 0;
+ ustr = (unsigned char *)str;
+ hash = 0;
+ while (*ustr) {
+ hash ^= (unsigned long long)*ustr++;
+ hash *= FNV_64_PRIME;
+ }
+
+ free(str);
+ return hash;
+}
+
+/*
+ * tracecmd_default_file_version - Get default trace file version of the library
+ *
+ * Returns the default trace file version
+ */
+int tracecmd_default_file_version(void)
+{
+ return FILE_VERSION_DEFAULT;
+}
+
+bool tracecmd_is_version_supported(unsigned int version)
+{
+ if (version <= FILE_VERSION_MAX)
+ return true;
+ return false;
+}
+
+static void __attribute__ ((constructor)) tracecmd_lib_init(void)
+{
+ tracecmd_compress_init();
+}
+
+static void __attribute__((destructor)) tracecmd_lib_free(void)
+{
+ tracecmd_compress_free();
+}
+
+__hidden bool check_file_state(unsigned long file_version, int current_state, int new_state)
+{
+ if (file_version >= FILE_VERSION_SECTIONS) {
+ if (current_state < TRACECMD_FILE_INIT)
+ return false;
+
+ return true;
+ }
+
+ switch (new_state) {
+ case TRACECMD_FILE_HEADERS:
+ case TRACECMD_FILE_FTRACE_EVENTS:
+ case TRACECMD_FILE_ALL_EVENTS:
+ case TRACECMD_FILE_KALLSYMS:
+ case TRACECMD_FILE_PRINTK:
+ case TRACECMD_FILE_CMD_LINES:
+ case TRACECMD_FILE_CPU_COUNT:
+ if (current_state == (new_state - 1))
+ return true;
+ break;
+ case TRACECMD_FILE_OPTIONS:
+ if (file_version < FILE_VERSION_SECTIONS && current_state == TRACECMD_FILE_CPU_COUNT)
+ return true;
+ break;
+ case TRACECMD_FILE_CPU_LATENCY:
+ case TRACECMD_FILE_CPU_FLYRECORD:
+ if (current_state == TRACECMD_FILE_OPTIONS)
+ return true;
+ break;
+ }
+
+ return false;
+}