#include #include #include /* * c_state_analysis - Einfaches CLI-Tool zur Analyse von C-State-Daten * * Zweck: * - Liest eine CSV-Datei mit C-State- und Clocksource-Event-Daten ein. * - Führt eine sehr einfache Auswertung im Sinne der Beschreibung durch: * * Median der C3-Residency (in Millisekunden) über alle Zeilen. * * Anzahl der clocksource_switch-Ereignisse (switch_events). * * Grobe Korrelation zwischen hoher C3-Residency und clocksource_switch- * Events, umgesetzt als Anteil der Zeilen, in denen gleichzeitig * (Residency > Schwellwert) und (clocksource_switch == 1) gilt. * * Erwartetes CSV-Format (mit Header-Zeile): * timestamp_ms,c3_residency_ms,clocksource_switch * 1234.5,5.9,1 * 1235.5,0.7,0 * ... * * Aufruf: * ./c_state_analysis * * Ausgabe: * JSON-Struktur im Stil des API-Contracts: * { * "analysis_results": { * "median_c3_residency": , * "switch_events": , * "correlation": * } * } * * Hinweise: * - Keine destruktiven Aktionen: Es wird nur gelesen. * - Keine Netzwerkoperationen. * - Implementiert eine einfache CSV-Parsing-Logik ohne externe Bibliotheken. */ #define LINE_BUF_SIZE 4096 /* Dynamisches Array für double-Werte (C3-Residencies) */ typedef struct { double *data; size_t size; size_t capacity; } DoubleArray; static void da_init(DoubleArray *a) { a->data = NULL; a->size = 0; a->capacity = 0; } static int da_reserve(DoubleArray *a, size_t new_cap) { if (new_cap <= a->capacity) return 1; double *tmp = (double *)realloc(a->data, new_cap * sizeof(double)); if (!tmp) return 0; a->data = tmp; a->capacity = new_cap; return 1; } static int da_push_back(DoubleArray *a, double v) { if (a->size == a->capacity) { size_t new_cap = a->capacity == 0 ? 128 : a->capacity * 2; if (!da_reserve(a, new_cap)) return 0; } a->data[a->size++] = v; return 1; } static void da_free(DoubleArray *a) { free(a->data); a->data = NULL; a->size = 0; a->capacity = 0; } /* Vergleichsfunktion für qsort (aufsteigend) */ static int cmp_double(const void *a, const void *b) { double da = *(const double *)a; double db = *(const double *)b; if (da < db) return -1; if (da > db) return 1; return 0; } /* Berechnet den Median eines Double-Arrays; liefert 0.0 bei size == 0 */ static double median(DoubleArray *a) { if (a->size == 0) return 0.0; qsort(a->data, a->size, sizeof(double), cmp_double); if (a->size % 2 == 1) { return a->data[a->size / 2]; } else { size_t i = a->size / 2; return (a->data[i - 1] + a->data[i]) / 2.0; } } /* * Einfache CSV-Zeile zerlegen. * Erwartet eine Zeile mit mindestens 3 Spalten: * timestamp_ms,c3_residency_ms,clocksource_switch * Timestamp wird ignoriert; wir parsen nur Spalten 2 und 3. */ static int parse_csv_line(const char *line, double *c3_residency, int *switch_flag) { char buf[LINE_BUF_SIZE]; strncpy(buf, line, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; char *saveptr = NULL; char *token = NULL; int col = 0; double c3 = 0.0; int sw = 0; token = strtok_r(buf, ",", &saveptr); while (token != NULL) { if (col == 1) { c3 = atof(token); } else if (col == 2) { sw = atoi(token); } col++; token = strtok_r(NULL, ",", &saveptr); } if (col < 3) { return 0; /* Zu wenige Spalten */ } *c3_residency = c3; *switch_flag = sw; return 1; } int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } const char *path = argv[1]; FILE *f = fopen(path, "r"); if (!f) { perror("fopen"); return 1; } char line[LINE_BUF_SIZE]; int is_header = 1; DoubleArray residencies; da_init(&residencies); unsigned long switch_events = 0; /* Gesamtzahl clocksource_switch==1 */ unsigned long total_rows = 0; /* Gesamtzahl gültiger Datenzeilen */ unsigned long joint_high = 0; /* Zeilen mit hoher C3-Residency UND switch */ /* Schwellwert für "hohe" C3-Residency in ms (heuristisch): */ const double HIGH_C3_THRESHOLD_MS = 2.0; while (fgets(line, sizeof(line), f)) { /* Header-Zeile überspringen (angenommen erste Zeile ist Header) */ if (is_header) { is_header = 0; continue; } /* Leere oder sehr kurze Zeilen ignorieren */ if (line[0] == '\n' || line[0] == '\0') { continue; } double c3 = 0.0; int sw = 0; if (!parse_csv_line(line, &c3, &sw)) { continue; /* Zeilen mit falschem Format überspringen */ } if (!da_push_back(&residencies, c3)) { fprintf(stderr, "Out of memory while storing data\n"); da_free(&residencies); fclose(f); return 1; } total_rows++; if (sw != 0) { switch_events++; } if (c3 > HIGH_C3_THRESHOLD_MS && sw != 0) { joint_high++; } } fclose(f); double med_c3 = median(&residencies); /* "Korrelation" als Anteil der Zeilen mit hoher C3-Residency UND switch */ double corr = 0.0; if (total_rows > 0) { corr = (double)joint_high / (double)total_rows; } da_free(&residencies); /* JSON-Ausgabe entsprechend dem API-Contract */ printf("{\n"); printf(" \"analysis_results\": {\n"); printf(" \"median_c3_residency\": %.6f,\n", med_c3); printf(" \"switch_events\": %lu,\n", switch_events); printf(" \"correlation\": %.6f\n", corr); printf(" }\n"); printf("}\n"); return 0; }