From 2c36c6b035ee3c836a65f9afa04c216e5d53d559 Mon Sep 17 00:00:00 2001 From: Mika Date: Sat, 6 Dec 2025 13:41:17 +0000 Subject: [PATCH] Add c_state_analysis/main.c --- c_state_analysis/main.c | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 c_state_analysis/main.c diff --git a/c_state_analysis/main.c b/c_state_analysis/main.c new file mode 100644 index 0000000..96e86da --- /dev/null +++ b/c_state_analysis/main.c @@ -0,0 +1,221 @@ +#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; +}