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