Add powersave_analysis_script/main.c
This commit is contained in:
commit
c0b3637245
1 changed files with 246 additions and 0 deletions
246
powersave_analysis_script/main.c
Normal file
246
powersave_analysis_script/main.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in a new issue