Add powersave_analysis_script/main.c

This commit is contained in:
Mika 2025-12-07 16:32:04 +00:00
commit c0b3637245

View file

@ -0,0 +1,246 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* 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 <pfad_zur_csv_datei>
*
* 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 <csv_datei>\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;
}