[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