#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; }