mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 17:14:08 +01:00
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
114 lines
2.5 KiB
C
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);
|
|
}
|