Add c_state_analysis/main.c
This commit is contained in:
parent
d394cdc32e
commit
2c36c6b035
1 changed files with 221 additions and 0 deletions
221
c_state_analysis/main.c
Normal file
221
c_state_analysis/main.c
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* 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 <pfad_zur_csv>
|
||||
*
|
||||
* Ausgabe:
|
||||
* JSON-Struktur im Stil des API-Contracts:
|
||||
* {
|
||||
* "analysis_results": {
|
||||
* "median_c3_residency": <double>,
|
||||
* "switch_events": <unsigned long>,
|
||||
* "correlation": <double>
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 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 <c_state_csv_file>\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;
|
||||
}
|
||||
Loading…
Reference in a new issue