From 193c18223510b4f190058a1297d7b301502621a2 Mon Sep 17 00:00:00 2001 From: Andrew Fasano Date: Wed, 5 Jun 2024 14:05:24 -0400 Subject: [PATCH] contrib/plugins: Add syscalls_logger plugin Syscalls_logger consumes QPP events from the syscalls plugin and logs this information in a variety of formats. Signed-off-by: Andrew Fasano --- contrib/plugins/Makefile | 1 + contrib/plugins/syscalls_logger.c | 91 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 contrib/plugins/syscalls_logger.c diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index bedbe452e5857..89c9511605a20 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -33,6 +33,7 @@ NAMES += stoptrigger NAMES += cflow NAMES += stringsearch NAMES += syscalls +NAMES += syscalls_logger ifeq ($(CONFIG_WIN32),y) SO_SUFFIX := .dll diff --git a/contrib/plugins/syscalls_logger.c b/contrib/plugins/syscalls_logger.c new file mode 100644 index 0000000000000..8419d06686403 --- /dev/null +++ b/contrib/plugins/syscalls_logger.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include "syscalls.h" + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +QEMU_PLUGIN_EXPORT const char *qemu_plugin_name = "syscalls_logger"; + +typedef enum { CSV, TEXT } LogFormat; + +static FILE *log_file = NULL; +static LogFormat log_format = TEXT; + +static void log_syscall(gpointer evdata, gpointer udata) +{ + SyscallDetails *details = (SyscallDetails*)evdata; + g_autoptr(GString) report = g_string_new(NULL); + + if (log_format == CSV) { + g_string_append_printf(report, "%lx,%lx", details->pc, details->callno); + for (int i = 0; i < SYSCALLS_MAX_ARGS; i++) { + g_string_append_printf(report, ",%lx", details->args[i]); + } + g_string_append_c(report, '\n'); + } else { + g_string_append_printf(report, "PC %lx: syscall %lx(", details->pc, details->callno); + for (int i = 0; i < SYSCALLS_MAX_ARGS - 1; i++) { + g_string_append_printf(report, "%lx, ", details->args[i]); + } + g_string_append_printf(report, "%lx)\n", details->args[SYSCALLS_MAX_ARGS - 1]); + } + + fprintf(log_file, "%s", report->str); + fflush(log_file); +} + +static bool parse_arguments(int argc, char **argv) +{ + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + if (tokens[0] == NULL || tokens[1] == NULL) { + fprintf(stderr, "Invalid argument format: %s\n", opt); + return false; + } + + if (g_strcmp0(tokens[0], "filename") == 0) { + if (log_file != NULL && log_file != stdout) { + fclose(log_file); + } + log_file = fopen(tokens[1], "w"); + if (log_file == NULL) { + fprintf(stderr, "Failed to open log file: %s\n", tokens[1]); + return false; + } + } else if (g_strcmp0(tokens[0], "format") == 0) { + if (g_strcmp0(tokens[1], "csv") == 0) { + log_format = CSV; + } else if (g_strcmp0(tokens[1], "text") == 0) { + log_format = TEXT; + } else { + fprintf(stderr, "Invalid format option: %s\n", tokens[1]); + return false; + } + } else { + fprintf(stderr, "Unknown option: %s\n", tokens[0]); + fprintf(stderr, "Available options: filename, format\n"); + return false; + } + } + + if (log_file == NULL) { + log_file = stdout; + } + + return true; +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + if (!parse_arguments(argc, argv)) { + return -1; + } + + qemu_plugin_reg_callback("syscalls", "on_all_sys_enter", log_syscall); + return 0; +} \ No newline at end of file