[LTP] [LTP PATCH v4 1/5] ftrace: Add common library for C implementation
Praveen K Pandey
praveen@linux.ibm.com
Mon Jun 8 07:49:56 CEST 2026
Add ftrace_lib.c and ftrace_lib.h to provide common functionality for
ftrace C test implementations. This library handles:
- Debugfs mounting and tracing path management
- Reading and writing ftrace files
- Saving and restoring ftrace settings
- Tracer enumeration and management
The library uses TST_NO_DEFAULT_MAIN to allow linking with LTP test
framework without defining multiple main() functions.
Signed-off-by: Praveen K Pandey <praveen@linux.ibm.com>
---
.../kernel/tracing/ftrace_test/ftrace_lib.c | 358 ++++++++++++++++++
.../kernel/tracing/ftrace_test/ftrace_lib.h | 139 +++++++
2 files changed, 497 insertions(+)
create mode 100644 testcases/kernel/tracing/ftrace_test/ftrace_lib.c
create mode 100644 testcases/kernel/tracing/ftrace_test/ftrace_lib.h
diff --git a/testcases/kernel/tracing/ftrace_test/ftrace_lib.c b/testcases/kernel/tracing/ftrace_test/ftrace_lib.c
new file mode 100644
index 000000000..8b93b9849
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/ftrace_lib.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2010 FUJITSU LIMITED
+ * Copyright (c) 2024 Linux Test Project
+ * Copyright (c) IBM, 2026
+ *
+ * Author: Li Zefan <lizf@cn.fujitsu.com>
+ * Converted to C by: Praveen K Pandey <praveen@linux.ibm.com>
+ */
+
+#define TST_NO_DEFAULT_MAIN
+#include "ftrace_lib.h"
+#include "tst_test.h"
+#include "tst_safe_macros.h"
+#include "tst_safe_stdio.h"
+
+char *tracing_path = NULL;
+char *debugfs_path = NULL;
+static int debugfs_mounted_by_us = 0;
+
+struct ftrace_saved_state saved_state = {0};
+
+void ftrace_initialize(void)
+{
+ FILE *fp;
+ char line[PATH_MAX];
+ int found = 0;
+
+ /* Check if debugfs is already mounted */
+ fp = SAFE_FOPEN("/proc/mounts", "r");
+ while (fgets(line, sizeof(line), fp)) {
+ if (strstr(line, "debugfs")) {
+ debugfs_path = strdup("/sys/kernel/debug");
+ found = 1;
+ break;
+ }
+ }
+ SAFE_FCLOSE(fp);
+
+ /* Mount debugfs if not already mounted */
+ if (!found) {
+ char *tmpdir = tst_tmpdir_path();
+ debugfs_path = malloc(PATH_MAX);
+ if (!debugfs_path)
+ tst_brk(TBROK | TERRNO, "malloc failed");
+ snprintf(debugfs_path, PATH_MAX, "%s/debugfs", tmpdir);
+ SAFE_MKDIR(debugfs_path, 0755);
+ SAFE_MOUNT("debugfs", debugfs_path, "debugfs", 0, NULL);
+ debugfs_mounted_by_us = 1;
+ }
+
+ /* Set tracing path */
+ tracing_path = malloc(PATH_MAX);
+ if (!tracing_path)
+ tst_brk(TBROK | TERRNO, "malloc failed");
+
+ snprintf(tracing_path, PATH_MAX, "%s/tracing", debugfs_path);
+
+ /* Check if tracing is supported */
+ if (access(tracing_path, F_OK) != 0)
+ tst_brk(TCONF, "Tracing is not supported");
+
+ /* Save current settings */
+ ftrace_save_settings();
+}
+
+void ftrace_cleanup(void)
+{
+ ftrace_restore_settings();
+
+ if (debugfs_mounted_by_us && debugfs_path) {
+ tst_umount(debugfs_path);
+ rmdir(debugfs_path);
+ }
+
+ free(tracing_path);
+ free(debugfs_path);
+
+ /* Free saved state */
+ free(saved_state.trace_options);
+ free(saved_state.tracing_on);
+ free(saved_state.buffer_size);
+ free(saved_state.tracing_cpumask);
+ free(saved_state.tracing_enabled);
+ free(saved_state.stack_tracer_enabled);
+ free(saved_state.ftrace_enabled);
+ free(saved_state.function_profile_enabled);
+}
+
+char *ftrace_get_path(const char *filename)
+{
+ char *path;
+
+ path = malloc(PATH_MAX);
+ if (!path)
+ return NULL;
+
+ snprintf(path, PATH_MAX, "%s/%s", tracing_path, filename);
+ return path;
+}
+
+char *ftrace_read_file(const char *filename)
+{
+ char *path;
+ FILE *fp;
+ char *content = NULL;
+ size_t len = 0;
+ ssize_t read;
+ char *line = NULL;
+ size_t total_size = 0;
+
+ path = ftrace_get_path(filename);
+ if (!path)
+ return NULL;
+
+ fp = fopen(path, "r");
+ free(path);
+
+ if (!fp)
+ return NULL;
+
+ /* Read entire file */
+ while ((read = getline(&line, &len, fp)) != -1) {
+ char *new_content = realloc(content, total_size + read + 1);
+ if (!new_content) {
+ free(content);
+ free(line);
+ fclose(fp);
+ return NULL;
+ }
+ content = new_content;
+ memcpy(content + total_size, line, read);
+ total_size += read;
+ }
+
+ if (content)
+ content[total_size] = '\0';
+
+ free(line);
+ fclose(fp);
+
+ return content;
+}
+
+int ftrace_write_file(const char *filename, const char *content)
+{
+ char *path;
+ FILE *fp;
+ int ret;
+
+ path = ftrace_get_path(filename);
+ if (!path)
+ return -1;
+
+ fp = fopen(path, "w");
+ free(path);
+
+ if (!fp)
+ return -1;
+
+ ret = fprintf(fp, "%s", content);
+ fclose(fp);
+
+ return (ret > 0) ? 0 : -1;
+}
+
+int ftrace_file_exists(const char *filename)
+{
+ char *path;
+ int ret;
+
+ path = ftrace_get_path(filename);
+ if (!path)
+ return 0;
+
+ ret = (access(path, F_OK) == 0);
+ free(path);
+
+ return ret;
+}
+
+void ftrace_save_settings(void)
+{
+ if (saved_state.saved)
+ return;
+
+ saved_state.trace_options = ftrace_read_file("trace_options");
+ saved_state.tracing_on = ftrace_read_file("tracing_on");
+ saved_state.buffer_size = ftrace_read_file("buffer_size_kb");
+
+ if (ftrace_file_exists("tracing_cpumask"))
+ saved_state.tracing_cpumask = ftrace_read_file("tracing_cpumask");
+
+ if (ftrace_file_exists("tracing_enabled"))
+ saved_state.tracing_enabled = ftrace_read_file("tracing_enabled");
+
+ if (ftrace_file_exists("stack_max_size")) {
+ char *path = strdup("/proc/sys/kernel/stack_tracer_enabled");
+ FILE *fp = fopen(path, "r");
+
+ if (fp) {
+ saved_state.stack_tracer_enabled = malloc(32);
+ if (saved_state.stack_tracer_enabled)
+ fgets(saved_state.stack_tracer_enabled, 32, fp);
+ fclose(fp);
+ }
+ free(path);
+ }
+
+ if (access("/proc/sys/kernel/ftrace_enabled", F_OK) == 0) {
+ FILE *fp = fopen("/proc/sys/kernel/ftrace_enabled", "r");
+
+ if (fp) {
+ saved_state.ftrace_enabled = malloc(32);
+ if (saved_state.ftrace_enabled)
+ fgets(saved_state.ftrace_enabled, 32, fp);
+ fclose(fp);
+ }
+ }
+
+ if (ftrace_file_exists("function_profile_enabled"))
+ saved_state.function_profile_enabled = ftrace_read_file("function_profile_enabled");
+
+ saved_state.saved = 1;
+}
+
+void ftrace_restore_settings(void)
+{
+ if (!saved_state.saved)
+ return;
+
+ /* Reset tracer and events */
+ ftrace_write_file("current_tracer", "nop\n");
+ ftrace_write_file("events/enable", "0\n");
+
+ if (ftrace_file_exists("tracing_max_latency"))
+ ftrace_write_file("tracing_max_latency", "0\n");
+
+ if (saved_state.tracing_cpumask)
+ ftrace_write_file("tracing_cpumask", saved_state.tracing_cpumask);
+
+ if (ftrace_file_exists("trace_clock"))
+ ftrace_write_file("trace_clock", "local\n");
+
+ if (saved_state.function_profile_enabled)
+ ftrace_write_file("function_profile_enabled", saved_state.function_profile_enabled);
+
+ if (saved_state.ftrace_enabled) {
+ FILE *fp = fopen("/proc/sys/kernel/ftrace_enabled", "w");
+ if (fp) {
+ fprintf(fp, "%s", saved_state.ftrace_enabled);
+ fclose(fp);
+ }
+ }
+
+ if (saved_state.stack_tracer_enabled && ftrace_file_exists("stack_max_size")) {
+ FILE *fp = fopen("/proc/sys/kernel/stack_tracer_enabled", "w");
+ if (fp) {
+ fprintf(fp, "%s", saved_state.stack_tracer_enabled);
+ fclose(fp);
+ }
+ ftrace_write_file("stack_max_size", "0\n");
+ }
+
+ if (saved_state.buffer_size)
+ ftrace_write_file("buffer_size_kb", saved_state.buffer_size);
+
+ if (saved_state.tracing_on)
+ ftrace_write_file("tracing_on", saved_state.tracing_on);
+
+ if (saved_state.tracing_enabled)
+ ftrace_write_file("tracing_enabled", saved_state.tracing_enabled);
+
+ /* Restore trace options */
+ if (saved_state.trace_options) {
+ char *options = strdup(saved_state.trace_options);
+ char *token = strtok(options, "\n");
+ while (token) {
+ ftrace_write_file("trace_options", token);
+ token = strtok(NULL, "\n");
+ }
+ free(options);
+ }
+
+ /* Clear trace */
+ ftrace_clear_trace();
+
+ /* Clear filter */
+ if (ftrace_file_exists("set_ftrace_filter"))
+ ftrace_write_file("set_ftrace_filter", "\n");
+}
+
+char **ftrace_get_available_tracers(int *count)
+{
+ char *content;
+ char **tracers = NULL;
+ int n = 0;
+ char *token;
+ char *saveptr;
+
+ content = ftrace_read_file("available_tracers");
+ if (!content) {
+ *count = 0;
+ return NULL;
+ }
+
+ /* Count tracers */
+ char *tmp = strdup(content);
+ token = strtok_r(tmp, " \n", &saveptr);
+ while (token) {
+ n++;
+ token = strtok_r(NULL, " \n", &saveptr);
+ }
+ free(tmp);
+
+ /* Allocate array */
+ tracers = malloc(sizeof(char *) * n);
+ if (!tracers) {
+ free(content);
+ *count = 0;
+ return NULL;
+ }
+
+ /* Fill array */
+ n = 0;
+ token = strtok_r(content, " \n", &saveptr);
+ while (token) {
+ tracers[n++] = strdup(token);
+ token = strtok_r(NULL, " \n", &saveptr);
+ }
+
+ free(content);
+ *count = n;
+ return tracers;
+}
+
+int ftrace_set_tracer(const char *tracer)
+{
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s\n", tracer);
+ return ftrace_write_file("current_tracer", buf);
+}
+
+void ftrace_clear_trace(void)
+{
+ ftrace_write_file("trace", "\n");
+}
+
+void ftrace_enable_tracing(void)
+{
+ ftrace_write_file("tracing_on", "1\n");
+}
+
+void ftrace_disable_tracing(void)
+{
+ ftrace_write_file("tracing_on", "0\n");
+}
+
diff --git a/testcases/kernel/tracing/ftrace_test/ftrace_lib.h b/testcases/kernel/tracing/ftrace_test/ftrace_lib.h
new file mode 100644
index 000000000..b40877ed1
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/ftrace_lib.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2010 FUJITSU LIMITED
+ * Copyright (c) 2024 Linux Test Project
+ * Copyright (c) IBM, 2026
+ *
+ * Author: Li Zefan <lizf@cn.fujitsu.com>
+ * Converted to C by: Praveen K Pandey <praveen@linux.ibm.com>
+ */
+
+#ifndef FTRACE_LIB_H
+#define FTRACE_LIB_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <limits.h>
+
+/* Global paths */
+extern char *tracing_path;
+extern char *debugfs_path;
+
+/* Saved settings for cleanup */
+struct ftrace_saved_state {
+ char *trace_options;
+ char *tracing_on;
+ char *buffer_size;
+ char *tracing_cpumask;
+ char *tracing_enabled;
+ char *stack_tracer_enabled;
+ char *ftrace_enabled;
+ char *function_profile_enabled;
+ int saved;
+};
+
+extern struct ftrace_saved_state saved_state;
+
+/**
+ * ftrace_initialize() - Initialize ftrace test environment
+ *
+ * This function:
+ * - Checks if debugfs is mounted, mounts it if needed
+ * - Verifies tracing support
+ * - Saves current ftrace settings
+ */
+void ftrace_initialize(void);
+
+/**
+ * ftrace_cleanup() - Cleanup and restore ftrace settings
+ *
+ * Restores all saved ftrace settings and unmounts debugfs if needed
+ */
+void ftrace_cleanup(void);
+
+/**
+ * ftrace_save_settings() - Save current ftrace settings
+ *
+ * Saves all ftrace configuration to restore later
+ */
+void ftrace_save_settings(void);
+
+/**
+ * ftrace_restore_settings() - Restore saved ftrace settings
+ *
+ * Restores ftrace configuration saved by ftrace_save_settings()
+ */
+void ftrace_restore_settings(void);
+
+/**
+ * ftrace_get_path() - Get full path to a tracing file
+ * @filename: Name of the file in tracing directory
+ *
+ * Returns: Allocated string with full path (caller must free)
+ */
+char *ftrace_get_path(const char *filename);
+
+/**
+ * ftrace_read_file() - Read content from a tracing file
+ * @filename: Name of the file in tracing directory
+ *
+ * Returns: Allocated string with file content (caller must free)
+ */
+char *ftrace_read_file(const char *filename);
+
+/**
+ * ftrace_write_file() - Write content to a tracing file
+ * @filename: Name of the file in tracing directory
+ * @content: Content to write
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int ftrace_write_file(const char *filename, const char *content);
+
+/**
+ * ftrace_file_exists() - Check if a tracing file exists
+ * @filename: Name of the file in tracing directory
+ *
+ * Returns: 1 if exists, 0 otherwise
+ */
+int ftrace_file_exists(const char *filename);
+
+/**
+ * ftrace_get_available_tracers() - Get list of available tracers
+ * @count: Pointer to store number of tracers
+ *
+ * Returns: Array of tracer names (caller must free)
+ */
+char **ftrace_get_available_tracers(int *count);
+
+/**
+ * ftrace_set_tracer() - Set current tracer
+ * @tracer: Name of the tracer to set
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+int ftrace_set_tracer(const char *tracer);
+
+/**
+ * ftrace_clear_trace() - Clear the trace buffer
+ */
+void ftrace_clear_trace(void);
+
+/**
+ * ftrace_enable_tracing() - Enable tracing
+ */
+void ftrace_enable_tracing(void);
+
+/**
+ * ftrace_disable_tracing() - Disable tracing
+ */
+void ftrace_disable_tracing(void);
+
+#endif /* FTRACE_LIB_H */
--
2.50.1
More information about the ltp
mailing list