commit c0b3637245b55c221c099f09ba9e82dd3ce74085 Author: Mika Date: Sun Dec 7 16:32:04 2025 +0000 Add powersave_analysis_script/main.c diff --git a/powersave_analysis_script/main.c b/powersave_analysis_script/main.c new file mode 100644 index 0000000..169f809 --- /dev/null +++ b/powersave_analysis_script/main.c @@ -0,0 +1,246 @@ +#include +#include +#include + +/* + * powersave_agg - kleines CLI-Tool zur Aggregation von Powersave-Zuständen + * + * Zweck: + * Liest eine einfache CSV-Datei mit Spalten fuer C-State und Dauer ein, + * fasst die Verweilzeiten pro State zusammen (insbesondere C0/C1) und + * gibt eine kompakte Aggregation auf stdout aus. + * + * Erwartetes Eingabeformat (CSV, Headerzeile optional): + * state,duration + * C0,123.45 + * C1,67.89 + * C3,10.0 + * + * - "state" : String ohne Komma (z. B. C0, C1, C2, C3 ...) + * - "duration": Gleitkommazahl (double), z. B. Mikro- oder Millisekunden + * + * Falls eine Headerzeile vorhanden ist, wird sie automatisch erkannt, + * sofern die erste Spalte das Wort "state" enthaelt (case-insensitive). + * + * Aufruf: + * ./powersave_agg + * + * Ausgabeformat (CSV, ohne weitere Kommentare): + * state,total_duration + * C0,xxx.x + * C1,yyy.y + * ... + * __ALL__,zzz.z + * + * Die Summenzeile __ALL__ enthaelt die Summe aller eingelesenen Durations. + */ + +/* Eintrag fuer einen aggregierten Powersave-Zustand */ +typedef struct PowersaveState { + char state[32]; /* Name des Zustands, z. B. "C0" */ + double duration; /* aufsummierte Dauer fuer diesen Zustand */ +} PowersaveState; + +/* Dynamische Liste zum Speichern der aggregierten Zustaende */ +typedef struct StateList { + PowersaveState *items; + size_t count; + size_t capacity; +} StateList; + +/* Initialisiert eine leere StateList */ +static void init_state_list(StateList *list) { + list->items = NULL; + list->count = 0; + list->capacity = 0; +} + +/* Gibt Speicher der StateList frei */ +static void free_state_list(StateList *list) { + free(list->items); + list->items = NULL; + list->count = 0; + list->capacity = 0; +} + +/* Findet Index eines States in der Liste, oder -1 falls nicht vorhanden */ +static int find_state_index(const StateList *list, const char *state) { + for (size_t i = 0; i < list->count; ++i) { + if (strcmp(list->items[i].state, state) == 0) { + return (int)i; + } + } + return -1; +} + +/* Fuegt einen neuen State hinzu oder akkumuliert, falls er schon existiert */ +static int add_or_accumulate_state(StateList *list, const char *state, double duration) { + int idx = find_state_index(list, state); + if (idx >= 0) { + list->items[idx].duration += duration; + return 0; + } + + if (list->count == list->capacity) { + size_t new_cap = (list->capacity == 0) ? 8 : list->capacity * 2; + PowersaveState *new_items = (PowersaveState *)realloc(list->items, new_cap * sizeof(PowersaveState)); + if (!new_items) { + fprintf(stderr, "Fehler: Speicherallokation fehlgeschlagen.\n"); + return -1; + } + list->items = new_items; + list->capacity = new_cap; + } + + strncpy(list->items[list->count].state, state, sizeof(list->items[list->count].state) - 1); + list->items[list->count].state[sizeof(list->items[list->count].state) - 1] = '\0'; + list->items[list->count].duration = duration; + list->count++; + return 0; +} + +/* Trimmt fuehrende und nachfolgende Whitespaces von einem String in-place */ +static void trim(char *s) { + char *end; + while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r' || *s == '\f' || *s == '\v') { + s++; + } + if (*s == '\0') { + return; + } + end = s + strlen(s) - 1; + while (end > s && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r' || *end == '\f' || *end == '\v')) { + *end = '\0'; + end--; + } +} + +/* Konvertiert einen String in Kleinbuchstaben in-place (ASCII) */ +static void to_lowercase(char *s) { + for (; *s; ++s) { + if (*s >= 'A' && *s <= 'Z') { + *s = (char)(*s - 'A' + 'a'); + } + } +} + +/* Ermittelt, ob eine Zeile vermutlich eine Headerzeile ist */ +static int is_header_line(const char *line) { + /* einfache Heuristik: beginnt die erste Spalte (bis Komma) mit "state"? */ + char buf[128]; + size_t i = 0; + + while (line[i] != '\0' && line[i] != ',' && i < sizeof(buf) - 1) { + buf[i] = line[i]; + i++; + } + buf[i] = '\0'; + + trim(buf); + to_lowercase(buf); + + if (strcmp(buf, "state") == 0) { + return 1; + } + return 0; +} + +/* Liest CSV-Datei ein und schreibt Aggregation in die uebergebene StateList */ +static int aggregate_powersave_states(const char *data_file, StateList *out_list, double *total_duration) { + FILE *fp = fopen(data_file, "r"); + if (!fp) { + fprintf(stderr, "Fehler: Kann Datei '%s' nicht oeffnen.\n", data_file); + return -1; + } + + char line[512]; + int first_line = 1; + *total_duration = 0.0; + + while (fgets(line, sizeof(line), fp) != NULL) { + /* Zeilenende entfernen */ + size_t len = strlen(line); + while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) { + line[--len] = '\0'; + } + + /* Zeilen, die nur aus Whitespace bestehen, ueberspringen */ + char tmp[512]; + strncpy(tmp, line, sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + trim(tmp); + if (tmp[0] == '\0') { + continue; + } + + if (first_line) { + first_line = 0; + if (is_header_line(line)) { + /* Headerzeile ueberspringen */ + continue; + } + } + + /* CSV: state,duration */ + char *state_str = strtok(line, ","); + char *duration_str = strtok(NULL, ","); + + if (!state_str || !duration_str) { + fprintf(stderr, "Warnung: Ungueltige Zeile wird uebersprungen.\n"); + continue; + } + + trim(state_str); + trim(duration_str); + + if (state_str[0] == '\0' || duration_str[0] == '\0') { + fprintf(stderr, "Warnung: Leeres Feld in Zeile, wird uebersprungen.\n"); + continue; + } + + char *endptr = NULL; + double duration = strtod(duration_str, &endptr); + if (endptr == duration_str) { + fprintf(stderr, "Warnung: Konnte Dauer '%s' nicht parsen, Zeile wird uebersprungen.\n", duration_str); + continue; + } + + if (add_or_accumulate_state(out_list, state_str, duration) != 0) { + fclose(fp); + return -1; + } + *total_duration += duration; + } + + fclose(fp); + return 0; +} + +int main(int argc, char **argv) { + if (argc != 2) { + fprintf(stderr, "Nutzung: %s \n", argv[0]); + fprintf(stderr, "Erwartetes Format: state,duration pro Zeile.\n"); + return 1; + } + + const char *data_file = argv[1]; + StateList list; + double total_duration = 0.0; + + init_state_list(&list); + + if (aggregate_powersave_states(data_file, &list, &total_duration) != 0) { + free_state_list(&list); + return 1; + } + + /* Aggregation als CSV auf stdout ausgeben */ + printf("state,total_duration\n"); + for (size_t i = 0; i < list.count; ++i) { + printf("%s,%.9f\n", list.items[i].state, list.items[i].duration); + } + printf("__ALL__,%.9f\n", total_duration); + + free_state_list(&list); + return 0; +}