aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>2023-04-26 07:11:22 +0300
committerSteven Rostedt (Google) <rostedt@goodmis.org>2023-04-26 13:05:48 -0400
commit789e82d70cd246f7fb1b05cff50d67433772d410 (patch)
treef5ce654452560f7af44f4522739cdc40b969a03d
parentaf49d832f46631597e0aabb2302bd0f56a9cad29 (diff)
downloadlibtracefs-789e82d70cd246f7fb1b05cff50d67433772d410.tar.gz
libtracefs: New API to reset ftrace instance
Resetting a ftrace instance to its default state is not a trivial task. A lot of trace files have to be modified, with different syntaxes and in strict order. Although there is such functionality in "trace-cmd reset" command, it will be good to have it in the tracefs library as well. A new API tracefs_instance_reset() is introduced, which resets given ftrace instance to its default state. The logic and most of the helper functions from "trace-cmd reset" command are copied in the tracefs library. Link: https://lore.kernel.org/linux-trace-devel/20230426041124.69544-2-tz.stoyanov@gmail.com Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
-rw-r--r--include/tracefs-local.h1
-rw-r--r--include/tracefs.h1
-rw-r--r--src/tracefs-instance.c207
-rw-r--r--src/tracefs-utils.c20
4 files changed, 229 insertions, 0 deletions
diff --git a/include/tracefs-local.h b/include/tracefs-local.h
index 2007d26..da99a30 100644
--- a/include/tracefs-local.h
+++ b/include/tracefs-local.h
@@ -64,6 +64,7 @@ int trace_get_instance(struct tracefs_instance *instance);
/* Can be overridden */
void tracefs_warning(const char *fmt, ...);
+char *strstrip(char *str);
int str_read_file(const char *file, char **buffer, bool warn);
char *trace_append_file(const char *dir, const char *name);
char *trace_find_tracing_dir(bool debugfs);
diff --git a/include/tracefs.h b/include/tracefs.h
index 3547b5a..5e9d84b 100644
--- a/include/tracefs.h
+++ b/include/tracefs.h
@@ -23,6 +23,7 @@ int tracefs_tracing_dir_is_mounted(bool mount, const char **path);
struct tracefs_instance;
void tracefs_instance_free(struct tracefs_instance *instance);
+void tracefs_instance_reset(struct tracefs_instance *instance);
struct tracefs_instance *tracefs_instance_create(const char *name);
struct tracefs_instance *tracefs_instance_alloc(const char *tracing_dir,
const char *name);
diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c
index 57f5c7f..b3ed983 100644
--- a/src/tracefs-instance.c
+++ b/src/tracefs-instance.c
@@ -1239,3 +1239,210 @@ char *tracefs_instance_get_affinity(struct tracefs_instance *instance)
return set;
}
+
+static int clear_trigger(const char *file)
+{
+ char trigger[BUFSIZ];
+ char *save = NULL;
+ char *line;
+ char *buf;
+ int size;
+ int len;
+ int ret;
+
+ size = str_read_file(file, &buf, true);
+ if (size < 1)
+ return 0;
+
+ trigger[0] = '!';
+
+ for (line = strtok_r(buf, "\n", &save); line; line = strtok_r(NULL, "\n", &save)) {
+ if (line[0] == '#')
+ continue;
+ len = strlen(line);
+ if (len > BUFSIZ - 2)
+ len = BUFSIZ - 2;
+ strncpy(trigger + 1, line, len);
+ trigger[len + 1] = '\0';
+ /* We don't want any filters or extra on the line */
+ strtok(trigger, " ");
+ write_file(file, trigger, O_WRONLY);
+ }
+
+ free(buf);
+
+ /*
+ * Some triggers have an order in removing them.
+ * They will not be removed if done in the wrong order.
+ */
+ size = str_read_file(file, &buf, true);
+ if (size < 1)
+ return 0;
+
+ ret = 0;
+ for (line = strtok(buf, "\n"); line; line = strtok(NULL, "\n")) {
+ if (line[0] == '#')
+ continue;
+ ret = 1;
+ break;
+ }
+ free(buf);
+ return ret;
+}
+
+static void disable_func_stack_trace_instance(struct tracefs_instance *instance)
+{
+ char *content;
+ char *cond;
+ int size;
+
+ content = tracefs_instance_file_read(instance, "current_tracer", &size);
+ if (!content)
+ return;
+ cond = strstrip(content);
+ if (memcmp(cond, "function", size - (cond - content)) != 0)
+ goto out;
+
+ tracefs_option_disable(instance, TRACEFS_OPTION_FUNC_STACKTRACE);
+ out:
+ free(content);
+}
+
+static void reset_cpu_mask(struct tracefs_instance *instance)
+{
+ int cpus = sysconf(_SC_NPROCESSORS_CONF);
+ int fullwords = (cpus - 1) / 32;
+ int bits = (cpus - 1) % 32 + 1;
+ int len = (fullwords + 1) * 9;
+ char buf[len + 1];
+
+ buf[0] = '\0';
+ sprintf(buf, "%x", (unsigned int)((1ULL << bits) - 1));
+ while (fullwords-- > 0)
+ strcat(buf, ",ffffffff");
+
+ tracefs_instance_file_write(instance, "tracing_cpumask", buf);
+}
+
+static void clear_func_filter(struct tracefs_instance *instance, const char *file)
+{
+ char filter[BUFSIZ];
+ char *line;
+ char *buf;
+ char *p;
+ int len;
+
+ buf = tracefs_instance_file_read(instance, file, NULL);
+ if (!buf)
+ return;
+
+ /* Now remove filters */
+ filter[0] = '!';
+
+ /*
+ * To delete a filter, we need to write a '!filter'
+ * to the file for each filter.
+ */
+ for (line = strtok(buf, "\n"); line; line = strtok(NULL, "\n")) {
+ if (line[0] == '#')
+ continue;
+ len = strlen(line);
+ if (len > BUFSIZ - 2)
+ len = BUFSIZ - 2;
+
+ strncpy(filter + 1, line, len);
+ filter[len + 1] = '\0';
+ /*
+ * To remove "unlimited" filters, we must remove
+ * the ":unlimited" from what we write.
+ */
+ p = strstr(filter, ":unlimited");
+ if (p) {
+ *p = '\0';
+ len = p - filter;
+ }
+ /*
+ * The write to this file expects white space
+ * at the end :-p
+ */
+ filter[len] = '\n';
+ filter[len+1] = '\0';
+ tracefs_instance_file_append(instance, file, filter);
+ }
+}
+
+static void clear_func_filters(struct tracefs_instance *instance)
+{
+ int i;
+ const char * const files[] = { "set_ftrace_filter",
+ "set_ftrace_notrace",
+ "set_graph_function",
+ "set_graph_notrace",
+ "stack_trace_filter",
+ NULL };
+
+ for (i = 0; files[i]; i++)
+ clear_func_filter(instance, files[i]);
+}
+
+/**
+ * tracefs_instance_reset - Reset a ftrace instance to its default state
+ * @instance - a ftrace instance to be reseted
+ *
+ * The main logic and the helper functions are copied from
+ * trace-cmd/tracecmd/trace-record.c, trace_reset()
+ */
+void tracefs_instance_reset(struct tracefs_instance *instance)
+{
+ int has_trigger = -1;
+ char **systems;
+ struct stat st;
+ char **events;
+ char *file;
+ int i, j;
+
+ tracefs_trace_off(instance);
+ disable_func_stack_trace_instance(instance);
+ tracefs_tracer_clear(instance);
+ tracefs_instance_file_write(instance, "events/enable", "0");
+ tracefs_instance_file_write(instance, "set_ftrace_pid", "");
+ tracefs_instance_file_clear(instance, "trace");
+
+ systems = tracefs_event_systems(NULL);
+ if (systems) {
+ for (i = 0; systems[i]; i++) {
+ events = tracefs_system_events(NULL, systems[i]);
+ if (!events)
+ continue;
+ for (j = 0; events[j]; j++) {
+ file = tracefs_event_get_file(instance, systems[i],
+ events[j], "filter");
+ write_file(file, "0", O_WRONLY | O_TRUNC);
+ tracefs_put_tracing_file(file);
+
+ file = tracefs_event_get_file(instance, systems[i],
+ events[j], "trigger");
+ if (has_trigger < 0) {
+ /* Check if the kernel is configured with triggers */
+ if (stat(file, &st) < 0)
+ has_trigger = 0;
+ else
+ has_trigger = 1;
+ }
+ if (has_trigger)
+ clear_trigger(file);
+ tracefs_put_tracing_file(file);
+ }
+ tracefs_list_free(events);
+ }
+ tracefs_list_free(systems);
+ }
+
+ tracefs_instance_file_write(instance, "error_log", " ");
+ tracefs_instance_file_write(instance, "trace_clock", "local");
+ tracefs_instance_file_write(instance, "set_event_pid", "");
+ reset_cpu_mask(instance);
+ clear_func_filters(instance);
+ tracefs_instance_file_write(instance, "tracing_max_latency", "0");
+ tracefs_trace_on(instance);
+}
diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c
index 9acf2ad..ef90677 100644
--- a/src/tracefs-utils.c
+++ b/src/tracefs-utils.c
@@ -319,6 +319,26 @@ void tracefs_put_tracing_file(char *name)
free(name);
}
+/* The function is copied from trace-cmd */
+__hidden char *strstrip(char *str)
+{
+ char *s;
+
+ if (!str)
+ return NULL;
+
+ s = str + strlen(str) - 1;
+ while (s >= str && isspace(*s))
+ s--;
+ s++;
+ *s = '\0';
+
+ for (s = str; *s && isspace(*s); s++)
+ ;
+
+ return s;
+}
+
__hidden int str_read_file(const char *file, char **buffer, bool warn)
{
char stbuf[BUFSIZ];