[PATCH v6 1/5] ftrace: Add common library for C implementation

Praveen K Pandey <[email protected]>
Newsgroups gmane.linux.ltp
Message-ID <[email protected]>
Add ftrace_lib.c and ftrace_lib.h to provide common functionality
for ftrace test cases. The library handles:

- Debugfs mounting and detection using tst_is_mounted()
- Ftrace initialization and cleanup
- Reading and writing ftrace files
- Saving and restoring ftrace settings
- Getting available tracers
- Setting current tracer using SAFE_FILE_PRINTF()

The library uses LTP's SAFE_* macros for robust error handling
and SAFE_ASPRINTF() for memory allocation. Proper NULL checks
are included for all strdup() calls.

Also update Makefile and .gitignore to support building the new
C tests. This ensures each commit in the series can be built
independently (bisectability).

Signed-off-by: Praveen K Pandey <[email protected]>
Suggested-by: Cyril Hrubis <[email protected]>
---
 .../kernel/tracing/ftrace_test/.gitignore     |   4 +
 testcases/kernel/tracing/ftrace_test/Makefile |  19 +-
 .../kernel/tracing/ftrace_test/ftrace_lib.c   | 371 ++++++++++++++++++
 .../kernel/tracing/ftrace_test/ftrace_lib.h   | 136 +++++++
 4 files changed, 528 insertions(+), 2 deletions(-)
 create mode 100644 testcases/kernel/tracing/ftrace_test/.gitignore
 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/.gitignore b/testcases/kernel/tracing/ftrace_test/.gitignore
new file mode 100644
index 000000000..9e102074c
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/.gitignore
@@ -0,0 +1,4 @@
+ftrace_regression01
+ftrace_regression02
+ftrace_stress_test
+
diff --git a/testcases/kernel/tracing/ftrace_test/Makefile b/testcases/kernel/tracing/ftrace_test/Makefile
index e4a913a56..2528b9f15 100644
--- a/testcases/kernel/tracing/ftrace_test/Makefile
+++ b/testcases/kernel/tracing/ftrace_test/Makefile
@@ -1,7 +1,22 @@
-top_srcdir              ?= ../../../..
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2010 FUJITSU LIMITED
+# Copyright (c) 2024 Linux Test Project
+# Copyright (c) IBM, 2026
+# Converted to C by: Praveen K Pandey <[email protected]>
+
+top_srcdir		?= ../../../..
 
 include $(top_srcdir)/include/mk/testcases.mk
 
-INSTALL_TARGETS         := *.sh ftrace_stress/*
+# Filter out ftrace_lib from standalone build targets
+FILTER_OUT_MAKE_TARGETS := ftrace_lib
+
+# Only ftrace_stress_test needs pthread
+ftrace_stress_test: LDLIBS += -lpthread
 
 include $(top_srcdir)/include/mk/generic_leaf_target.mk
+
+# C test binaries with shared library dependencies
+ftrace_regression01:	ftrace_lib.o
+ftrace_regression02:	ftrace_lib.o
+ftrace_stress_test:	ftrace_lib.o
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..9aad863a9
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/ftrace_lib.c
@@ -0,0 +1,371 @@
+// 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 <[email protected]>
+ * Converted to C by: Praveen K Pandey <[email protected]>
+ */
+
+#define TST_NO_DEFAULT_MAIN
+#include "ftrace_lib.h"
+#include "tst_test.h"
+#include "tst_safe_macros.h"
+#include "tst_safe_stdio.h"
+#include "tst_safe_file_ops.h"
+#include "tst_device.h"
+
+char *tracing_path;
+char *debugfs_path;
+static int debugfs_mounted_by_us;
+
+struct ftrace_saved_state saved_state;
+
+void ftrace_initialize(void)
+{
+	if (tst_is_mounted("/sys/kernel/debug")) {
+		debugfs_path = strdup("/sys/kernel/debug");
+		if (!debugfs_path)
+			tst_brk(TBROK | TERRNO, "strdup failed");
+	} else {
+		char *tmpdir = tst_tmpdir_path();
+
+		SAFE_ASPRINTF(&debugfs_path, "%s/debugfs", tmpdir);
+		SAFE_MKDIR(debugfs_path, 0755);
+		SAFE_MOUNT("debugfs", debugfs_path, "debugfs", 0, NULL);
+		debugfs_mounted_by_us = 1;
+	}
+
+	SAFE_ASPRINTF(&tracing_path, "%s/tracing", debugfs_path);
+
+	if (access(tracing_path, F_OK) != 0)
+		tst_brk(TCONF, "Tracing is not supported");
+
+	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.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;
+
+	SAFE_ASPRINTF(&path, "%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;
+
+	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")) {
+		FILE *fp = fopen("/proc/sys/kernel/stack_tracer_enabled", "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);
+		}
+	}
+
+	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;
+
+	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);
+
+	if (saved_state.trace_options) {
+		char *options = strdup(saved_state.trace_options);
+
+		if (options) {
+			char *token = strtok(options, "\n");
+
+			while (token) {
+				ftrace_write_file("trace_options", token);
+				token = strtok(NULL, "\n");
+			}
+			free(options);
+		}
+	}
+
+	ftrace_clear_trace();
+
+	if (ftrace_file_exists("set_ftrace_filter"))
+		ftrace_write_file("set_ftrace_filter", "\n");
+}
+
+char **ftrace_get_available_tracers(int *count)
+{
+	char *path;
+	FILE *fp;
+	char **tracers = NULL;
+	char *line = NULL;
+	size_t len = 0;
+	int n = 0;
+	int capacity = 16;
+
+	path = ftrace_get_path("available_tracers");
+	if (!path) {
+		*count = 0;
+		return NULL;
+	}
+
+	fp = fopen(path, "r");
+	free(path);
+
+	if (!fp) {
+		*count = 0;
+		return NULL;
+	}
+
+	tracers = malloc(sizeof(char *) * capacity);
+	if (!tracers) {
+		fclose(fp);
+		*count = 0;
+		return NULL;
+	}
+
+	if (getline(&line, &len, fp) != -1) {
+		char *token;
+		char *saveptr;
+
+		token = strtok_r(line, " \n", &saveptr);
+		while (token) {
+			if (n >= capacity) {
+				capacity *= 2;
+				char **new_tracers = realloc(tracers, sizeof(char *) * capacity);
+
+				if (!new_tracers) {
+					while (n > 0)
+						free(tracers[--n]);
+					free(tracers);
+					free(line);
+					fclose(fp);
+					*count = 0;
+					return NULL;
+				}
+				tracers = new_tracers;
+			}
+
+			tracers[n] = strdup(token);
+			if (!tracers[n]) {
+				while (n > 0)
+					free(tracers[--n]);
+				free(tracers);
+				free(line);
+				fclose(fp);
+				*count = 0;
+				return NULL;
+			}
+			n++;
+			token = strtok_r(NULL, " \n", &saveptr);
+		}
+	}
+
+	free(line);
+	fclose(fp);
+
+	*count = n;
+	return tracers;
+}
+
+int ftrace_set_tracer(const char *tracer)
+{
+	char *path;
+
+	path = ftrace_get_path("current_tracer");
+	if (!path)
+		return -1;
+
+	SAFE_FILE_PRINTF(path, "%s", tracer);
+	free(path);
+
+	return 0;
+}
+
+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..79fdfee28
--- /dev/null
+++ b/testcases/kernel/tracing/ftrace_test/ftrace_lib.h
@@ -0,0 +1,136 @@
+/* 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 <[email protected]>
+ * Converted to C by: Praveen K Pandey <[email protected]>
+ */
+
+#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>
+
+extern char *tracing_path;
+extern char *debugfs_path;
+
+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
+ *
+ * Checks if debugfs is mounted, mounts it if needed, verifies tracing
+ * support, and 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
+ *
+ * Return: 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
+ *
+ * Return: 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
+ *
+ * Return: 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
+ *
+ * Return: 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
+ *
+ * Return: 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
+ *
+ * Return: 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

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.