testsAndMisc/linux_configuration/C/atop_agg/atop_agg_state.c
Krzysztof kuhy Rudnicki da1e0d3588 atop_agg: split atop_agg.c 478 -> 204 + state + emit
Three units along the file's own concerns:

  atop_agg.c (204)        ingest -- tokenising atop output, folding lines, main
  atop_agg_state.c (113)  the per-PID hash tables, slot lookup, epochs, alloc
  atop_agg_emit.c (178)   the ranked, name-aggregated report

Table geometry and the four table functions that now cross a unit boundary go
in a private atop_agg_internal.h. Those four lose `static` -- the one
visibility change here.

The Makefile edit is the load-bearing part: SRC had to grow or the build would
have linked the old single unit and the suite would have passed with the new
files unused. Checked by reading the compile line, not the exit status.

I had written this file off as unverifiable earlier. Wrong: the Makefile has
had test and coverage targets all along. Coverage is 92.9% lines / 100%
functions on the same 211 source lines, before and after -- the best verifier
of anything left over the cap. Production binary builds at -O2 and runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 10:49:46 +02:00

114 lines
2.5 KiB
C

/*
* The per-PID hash tables and the State they live in: slot lookup by PID,
* epoch recording, and allocation/teardown.
*
* Split from atop_agg.c so the ingest half is just parsing. Open-addressed
* with linear probing; the geometry is in atop_agg_internal.h because the
* reporting half walks the same tables.
*/
#include "atop_agg.h"
#include "atop_agg_internal.h"
#include <stdlib.h>
#include <string.h>
unsigned int hash_pid(int pid)
{
unsigned int k = (unsigned int)pid;
return (k * 2654435761u) >> (32 - HASH_CAP_BITS);
}
PidCpu *cpu_slot(State *s, int pid)
{
unsigned int h = hash_pid(pid);
for (unsigned int probes = 0; probes < HASH_CAP; probes++, h++)
{
PidCpu *slot = &s->cpu[h & HASH_MASK];
if (slot->pid == pid)
{
return slot;
}
if (slot->pid == 0)
{
slot->pid = pid;
slot->first_ticks = -1;
slot->last_ticks = 0;
slot->samples = 0;
slot->name[0] = '\0';
return slot;
}
}
/* Table full — drop the sample rather than loop forever. */
return NULL;
}
PidRam *ram_slot(State *s, int pid)
{
unsigned int h = hash_pid(pid);
for (unsigned int probes = 0; probes < HASH_CAP; probes++, h++)
{
PidRam *slot = &s->ram[h & HASH_MASK];
if (slot->pid == pid)
{
return slot;
}
if (slot->pid == 0)
{
slot->pid = pid;
slot->peak_kb = 0;
slot->sum_kb = 0;
slot->samples = 0;
slot->name[0] = '\0';
return slot;
}
}
return NULL;
}
void add_epoch(State *s, long epoch)
{
/* Linear scan — there are only a few dozen distinct epochs per log. */
for (int i = 0; i < s->n_epochs; i++)
{
if (s->epochs[i] == epoch)
{
return;
}
}
if (s->n_epochs < MAX_EPOCHS)
{
s->epochs[s->n_epochs++] = epoch;
}
}
State *state_new(void)
{
State *s = calloc(1, sizeof(State));
if (!s)
{
return NULL;
}
s->cpu = calloc(HASH_CAP, sizeof(PidCpu));
s->ram = calloc(HASH_CAP, sizeof(PidRam));
s->epochs = calloc(MAX_EPOCHS, sizeof(long));
if (!s->cpu || !s->ram || !s->epochs)
{
state_free(s);
return NULL;
}
s->n_epochs = 0;
return s;
}
void state_free(State *s)
{
if (!s)
{
return;
}
free(s->cpu);
free(s->ram);
free(s->epochs);
free(s);
}