diff --git a/kernel/src/doe_log.c b/kernel/src/doe_log.c index ac3f84ae..2ec82e03 100644 --- a/kernel/src/doe_log.c +++ b/kernel/src/doe_log.c @@ -36,9 +36,9 @@ * 1. tick_number uint32 — monotonic heartbeat counter * 2. elapsed_ns uint64 — ns since run start * 3. tick_interval_ns uint64 — actual interval from prior tick - * 4. cache_hits_delta uint32 — hot-words cache hits this tick (TODO: 0) - * 5. bucket_hits_delta uint32 — bucket hits this tick (TODO: 0) - * 6. word_executions_delta uint32 — words executed this tick (TODO: 0) + * 4. cache_hits_delta uint32 — hot-words cache hits this tick (measured since #142) + * 5. bucket_hits_delta uint32 — bucket hits this tick (measured since #142) + * 6. word_executions_delta uint32 — words executed this tick (measured; the "TODO: 0" here was stale) * 7. hot_word_count uint64 — words with heat >= threshold * 8. avg_word_heat_q48 uint64 — mean execution heat as raw Q48.16 integer * 9. window_width uint32 — rolling window effective size diff --git a/kernel/tests/Makefile b/kernel/tests/Makefile index 351896bc..fccd8ce5 100644 --- a/kernel/tests/Makefile +++ b/kernel/tests/Makefile @@ -19,7 +19,8 @@ CC ?= cc CFLAGS := -std=c99 -Wall -Wextra -Werror -O2 -g -I$(HERE) -I$(ROOT)/kernel/include \ -I$(ROOT)/kernel/include/starkernel -I$(ROOT)/v3/include -TESTS := test_gpt test_usb_config test_sk_v4_stop test_sk_v4_slots test_sk_v4_mint test_sk_v4_identity +TESTS := test_gpt test_usb_config test_sk_v4_stop test_sk_v4_slots test_sk_v4_mint test_sk_v4_identity test_heartbeat_export +SRC_test_heartbeat_export := $(ROOT)/v3/src/heartbeat_export.c SRC_test_sk_v4_identity := $(ROOT)/kernel/src/v4/sk_v4_identity.c $(ROOT)/kernel/src/v4/sk_v4_slots.c $(ROOT)/kernel/src/gpt.c EXTRA_test_sk_v4_identity := -D__STARKERNEL__ -I$(ROOT)/v4/include -DSTICK_GROW_MIN_BYTES=8000000000ULL -DSTICK_ARTEMIS_CAP_GIB=256ULL SRC_test_sk_v4_mint := $(ROOT)/kernel/src/v4/sk_v4_mint.c $(ROOT)/kernel/src/gpt.c diff --git a/kernel/tests/test_heartbeat_export.c b/kernel/tests/test_heartbeat_export.c new file mode 100644 index 00000000..43c99c67 --- /dev/null +++ b/kernel/tests/test_heartbeat_export.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LicenseRef-Starship-1.0 + * Copyright (c) 2023-2026 Robert A. James + */ +/* test_heartbeat_export.c -- v3/src/heartbeat_export.c on the host (#142): the DoE row's per-tick + * deltas -- cache_hits_delta and bucket_hits_delta from the VM's hot-words cache, beside + * word_executions_delta -- are measured, not written as 0. A VM is built in memory with a cache + * whose hit counters the test sets, and heartbeat_capture_tick_snapshot is run tick by tick. + */ +#include "harness.h" +#include "vm.h" +#include "physics_hotwords_cache.h" +#include +#include + +int main(void) +{ + VM *vm = (VM *)calloc(1, sizeof *vm); + HotwordsCache *cache = (HotwordsCache *)calloc(1, sizeof *cache); + HeartbeatTickSnapshot s; + + vm->hotwords_cache = cache; + + cache->stats.cache_hits = 5; cache->stats.bucket_hits = 3; vm->heartbeat.words_executed = 40; + memset(&s, 0, sizeof s); heartbeat_capture_tick_snapshot(vm, &s); + CHECK(s.cache_hits_delta == 5 && s.bucket_hits_delta == 3 && s.word_executions_delta == 40, + "#142: the first tick: 5 cache hits, 3 bucket hits, 40 words (%u, %u, %u)", s.cache_hits_delta, s.bucket_hits_delta, s.word_executions_delta); + + cache->stats.cache_hits = 12; cache->stats.bucket_hits = 10; vm->heartbeat.words_executed = 100; + memset(&s, 0, sizeof s); heartbeat_capture_tick_snapshot(vm, &s); + CHECK(s.cache_hits_delta == 7 && s.bucket_hits_delta == 7 && s.word_executions_delta == 60, + "#142: the next tick: the deltas, 7, 7 and 60 (%u, %u, %u)", s.cache_hits_delta, s.bucket_hits_delta, s.word_executions_delta); + + cache->stats.cache_hits = 2; cache->stats.bucket_hits = 1; /* the cache's stats were reset */ + memset(&s, 0, sizeof s); heartbeat_capture_tick_snapshot(vm, &s); + CHECK(s.cache_hits_delta == 2 && s.bucket_hits_delta == 1, + "#142: after the stats are reset, the count since then, as words_executed is (%u, %u)", s.cache_hits_delta, s.bucket_hits_delta); + + vm->hotwords_cache = 0; + memset(&s, 0xFF, sizeof s); heartbeat_capture_tick_snapshot(vm, &s); + CHECK(s.cache_hits_delta == 0 && s.bucket_hits_delta == 0, "#142: a VM with no cache: 0 hits, not garbage"); + + free(cache); free(vm); + return DONE(); +} diff --git a/logs/20261112-185530/kernel-tests/test_heartbeat_export.log b/logs/20261112-185530/kernel-tests/test_heartbeat_export.log new file mode 100644 index 00000000..bb809d28 --- /dev/null +++ b/logs/20261112-185530/kernel-tests/test_heartbeat_export.log @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ecf31ab591f47562d888415c19a4ad06a827fe66a665d58df99c91653a99d07 +size 662 diff --git a/v3/src/heartbeat_export.c b/v3/src/heartbeat_export.c index dd11dffd..52d02841 100644 --- a/v3/src/heartbeat_export.c +++ b/v3/src/heartbeat_export.c @@ -41,6 +41,7 @@ */ #include "vm.h" +#include "physics_hotwords_cache.h" #include "log.h" #include "platform_time.h" #include @@ -78,9 +79,19 @@ void heartbeat_capture_tick_snapshot(VM* vm, HeartbeatTickSnapshot* snapshot) /* Delta metrics — computed from per-VM counters using static last-value tracking. * On VM reset (current < last), treat current value as the full delta. */ - /* Cache metrics from hotwords cache */ - snapshot->cache_hits_delta = 0; /* TODO: wire hotwords cache hit counter */ - snapshot->bucket_hits_delta = 0; /* TODO: wire bucket hit counter */ + /* Cache metrics from the hot-words cache's own counters (physics_hotwords_cache.c counts every + * cache hit and bucket hit), as deltas since the previous tick -- the same way as + * word_executions_delta below. They were written as 0, unmeasured (#142). A VM with no cache + * has none. */ + { + static uint64_t last_cache_hits = 0, last_bucket_hits = 0; + uint64_t ch = vm->hotwords_cache ? vm->hotwords_cache->stats.cache_hits : 0; + uint64_t bh = vm->hotwords_cache ? vm->hotwords_cache->stats.bucket_hits : 0; + snapshot->cache_hits_delta = (uint32_t)(ch >= last_cache_hits ? ch - last_cache_hits : ch); + snapshot->bucket_hits_delta = (uint32_t)(bh >= last_bucket_hits ? bh - last_bucket_hits : bh); + last_cache_hits = ch; + last_bucket_hits = bh; + } /* Word execution delta — words executed since the previous heartbeat tick. * vm->heartbeat.words_executed is incremented by vm_core.c on every dispatch. */