From a751ca787d5a8ac68269a2802d7a487b5de22eda Mon Sep 17 00:00:00 2001 From: Mika Date: Wed, 17 Dec 2025 12:03:31 +0000 Subject: [PATCH] Add trace_cmd_tool/main.c --- trace_cmd_tool/main.c | 253 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 trace_cmd_tool/main.c diff --git a/trace_cmd_tool/main.c b/trace_cmd_tool/main.c new file mode 100644 index 0000000..d282b8a --- /dev/null +++ b/trace_cmd_tool/main.c @@ -0,0 +1,253 @@ +#include +#include +#include +#include +#include +#include + +/* + * trace_cmd_tool + * -------------- + * Kleines Linux-CLI-Tool, das Kernel-Traces mit "trace-cmd" startet + * und ein sehr einfaches Analyse-Frontend bereitstellt. + * + * Ziel im Kontext des Experiments: + * - Traces zu einem gegebenen trace_id für eine Dauer (Sekunden) erfassen + * - Metadata zu Laufzeit und Trace-Datei ausgeben + * - Ausgabe ist textuell/human-readable; strukturierte Daten koennen + * leicht weiterverarbeitet werden (z. B. durch ein separates + * Analyse-Skript wie trace_agg.py). + * + * Vereinfachte API-Entsprechung zur Vorgabe: + * struct trace_result { + * char timestamp[64]; // ISO-8601 Startzeit des Runs + * char event_data[256]; // Kurzbeschreibung / Pfad zur Trace-Datei + * }; + * + * int run_trace(const char *trace_id, int duration, struct trace_result *out); + * + * Nutzung (Beispiele): + * ./trace_cmd_tool run --id offset_test --duration 5 + * ./trace_cmd_tool analyze --file trace_offset_test.dat + * + * Hinweis: + * - Das Programm ruft extern "trace-cmd" auf (muss im PATH vorhanden sein). + * - Es werden nur Dateien erzeugt (trace-*.dat); keine destruktiven Aktionen. + */ + +struct trace_result { + char timestamp[64]; + char event_data[256]; +}; + +static void print_usage(const char *prog) +{ + fprintf(stderr, + "Usage:\n" + " %s run --id --duration [--events ]\n" + " %s analyze --file \n\n" + "Options:\n" + " --id Logischer Name des Trace-Runs (z.B. offset_test)\n" + " --duration Dauer des Traces in Sekunden (integer > 0)\n" + " --events Optional: Komma-separierte Liste von ftrace-Events\n" + " (Standard: sched:sched_switch,irq:*,timer:*)\n" + " --file Vorhandene trace-cmd-Datei zur einfachen Analyse\n\n" + "Beispiele:\n" + " %s run --id offset_vm --duration 10\n" + " %s analyze --file trace_offset_vm.dat\n", + prog, prog, prog, prog); +} + +/* + * Hilfsfunktion: ISO-8601 Zeitstempel in Buffer schreiben. + */ +static void get_iso_timestamp(char *buf, size_t buflen) +{ + time_t now = time(NULL); + struct tm tm; + + if (now == (time_t)-1) { + snprintf(buf, buflen, "unknown-time"); + return; + } + + if (!gmtime_r(&now, &tm)) { + snprintf(buf, buflen, "unknown-time"); + return; + } + + strftime(buf, buflen, "%Y-%m-%dT%H:%M:%SZ", &tm); +} + +/* + * run_trace: Startet trace-cmd record mit angegebener Dauer. + * + * trace_id: Logischer Name; wird im Dateinamen verwendet. + * duration: Dauer des Traces in Sekunden. + * out: trace_result* fuer Metadaten (kann NULL sein). + * + * Rueckgabe: 0 bei Erfolg, !=0 bei Fehler. + */ +static int run_trace(const char *trace_id, int duration, const char *events, struct trace_result *out) +{ + if (!trace_id || duration <= 0) { + fprintf(stderr, "[run_trace] invalid arguments\n"); + return -1; + } + + char timestamp[64]; + get_iso_timestamp(timestamp, sizeof(timestamp)); + + char trace_file[256]; + snprintf(trace_file, sizeof(trace_file), "trace_%s.dat", trace_id); + + /* + * trace-cmd-Aufruf konstruieren. + * -p function (oder andere Profile) koennten ergänzt werden; + * hier bleiben wir bei einer simplen Event-Auswahl. + */ + const char *default_events = "sched:sched_switch,irq:*,timer:*"; + const char *ev = events && events[0] ? events : default_events; + + char cmd[1024]; + snprintf(cmd, sizeof(cmd), + "trace-cmd record -e %s -o %s sleep %d", + ev, trace_file, duration); + + printf("[info] Starting trace-cmd run...\n"); + printf("[info] trace_id : %s\n", trace_id); + printf("[info] duration : %d s\n", duration); + printf("[info] events : %s\n", ev); + printf("[info] output file : %s\n", trace_file); + printf("[info] start(UTC) : %s\n", timestamp); + + int ret = system(cmd); + if (ret == -1) { + fprintf(stderr, "[error] failed to execute trace-cmd (system error: %s)\n", strerror(errno)); + return -1; + } + + if (WIFEXITED(ret) && WEXITSTATUS(ret) == 0) { + printf("[info] trace-cmd finished successfully.\n"); + } else { + fprintf(stderr, "[error] trace-cmd failed (status=%d).\n", ret); + return -1; + } + + if (out) { + memset(out, 0, sizeof(*out)); + snprintf(out->timestamp, sizeof(out->timestamp), "%s", timestamp); + snprintf(out->event_data, sizeof(out->event_data), "trace_file=%s", trace_file); + } + + return 0; +} + +/* + * Sehr einfache Auswertung eines bestehenden trace-cmd-Files. + * Hier werden nur Grobinfos ausgegeben, so dass z.B. CI-Jobs + * eine minimale Laufzeitkontrolle haben, bevor eine tiefere + * Python-Analyse laeuft. + */ +static int analyze_trace_file(const char *filepath) +{ + if (!filepath || !filepath[0]) { + fprintf(stderr, "[analyze] no file specified\n"); + return -1; + } + + printf("[analyze] Trace file : %s\n", filepath); + + /* + * Wir rufen trace-cmd report --stat auf, um Basisstatistiken zu + * erhalten (Eventanzahl/Prozessinfos etc.). Dies ist bewusst + * leichtgewichtig und nicht destruktiv. + */ + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "trace-cmd report --stat %s", filepath); + + int ret = system(cmd); + if (ret == -1) { + fprintf(stderr, "[error] failed to execute trace-cmd report (system error: %s)\n", strerror(errno)); + return -1; + } + + if (!(WIFEXITED(ret) && WEXITSTATUS(ret) == 0)) { + fprintf(stderr, "[error] trace-cmd report failed (status=%d).\n", ret); + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + print_usage(argv[0]); + return 1; + } + + const char *mode = argv[1]; + + if (strcmp(mode, "run") == 0) { + const char *trace_id = NULL; + const char *duration_str = NULL; + const char *events = NULL; + + for (int i = 2; i < argc; ++i) { + if (strcmp(argv[i], "--id") == 0 && i + 1 < argc) { + trace_id = argv[++i]; + } else if (strcmp(argv[i], "--duration") == 0 && i + 1 < argc) { + duration_str = argv[++i]; + } else if (strcmp(argv[i], "--events") == 0 && i + 1 < argc) { + events = argv[++i]; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_usage(argv[0]); + return 0; + } + } + + if (!trace_id || !duration_str) { + fprintf(stderr, "[error] --id and --duration are required in run mode.\n\n"); + print_usage(argv[0]); + return 1; + } + + char *endptr = NULL; + long duration = strtol(duration_str, &endptr, 10); + if (!duration_str[0] || *endptr != '\0' || duration <= 0) { + fprintf(stderr, "[error] invalid duration: %s\n", duration_str); + return 1; + } + + struct trace_result result; + int rc = run_trace(trace_id, (int)duration, events, &result); + if (rc == 0) { + printf("\n[trace_result]\n"); + printf("timestamp = %s\n", result.timestamp); + printf("event_data = %s\n", result.event_data); + } + return rc == 0 ? 0 : 1; + + } else if (strcmp(mode, "analyze") == 0) { + const char *file = NULL; + for (int i = 2; i < argc; ++i) { + if (strcmp(argv[i], "--file") == 0 && i + 1 < argc) { + file = argv[++i]; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_usage(argv[0]); + return 0; + } + } + if (!file) { + fprintf(stderr, "[error] --file is required in analyze mode.\n\n"); + print_usage(argv[0]); + return 1; + } + return analyze_trace_file(file) == 0 ? 0 : 1; + } + + fprintf(stderr, "[error] unknown mode: %s\n\n", mode); + print_usage(argv[0]); + return 1; +}