347 headers said "Licensed under the StarForth License, Version 1.0" -- a shortening (Captain Bob, 2026-11-08: "License file SL I think I shortened accidently"); LICENSE is, and has been since the first commit, the Starship License 1.0 (SL-1.0, SPDX LicenseRef-Starship-1.0). 349 files, 566 occurrences, now "Starship License, Version 1.0 (SL-1.0)". Left as they are, pending a ruling: docs/patent/ (the patent figures), docs/formal/ (the citable volumes) and the generated reports under docs/working/experiments/ (32 files). Comments and license text only: the amd64 kernel builds, every changed script parses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
144 lines
4.9 KiB
C
144 lines
4.9 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2026 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the Starship License, Version 1.0 (SL-1.0) (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
*/
|
||
|
||
/**
|
||
* vm_log_buffer.c - a VM's log records, on their way to Artemis's log ring.
|
||
* See the header for the contract (FABRIC-3.md §XXXVIII, 2026-10-14).
|
||
*/
|
||
|
||
#ifdef __STARKERNEL__
|
||
|
||
#include "starkernel/vm_log_buffer.h"
|
||
#include "vm.h"
|
||
#include "log.h" /* LOG_ERROR, LOG_WARN */
|
||
#include "starkernel/vm/kernel_hermes.h" /* sk_hermes_send_one(), SK_HERMES_MSG_TYPE_LOG_BATCH */
|
||
#include "starkernel/capsule_birth.h" /* capsule_vm_find_by_name_nocase(), capsule_vm_registry_get() */
|
||
#include "starkernel/log_region.h" /* log_region_append() */
|
||
#include "starkernel/console.h" /* console_println() -- never log_message() here */
|
||
#include "freestanding/stdio.h" /* snprintf() */
|
||
#include <string.h>
|
||
|
||
#define REC_HEAD 11u /* level 1 + timestamp 8 + length 2 */
|
||
|
||
typedef char vm_log_buf_fits[(VM_LOG_BUF_SIZE + 64 <= SK_HERMES_CHUNK_MAX_PAYLOAD) ? 1 : -1];
|
||
|
||
/* Set while a record is being added or a buffer sent: a log line made from
|
||
* inside that work (a kernel-Hermes refusal, say) must not re-enter it. */
|
||
static int g_busy;
|
||
|
||
static uint32_t put_record(uint8_t *at, uint8_t level, uint64_t ts,
|
||
const char *msg, uint16_t len) {
|
||
at[0] = level;
|
||
memcpy(at + 1, &ts, 8);
|
||
memcpy(at + 9, &len, 2);
|
||
memcpy(at + REC_HEAD, msg, len);
|
||
return REC_HEAD + len;
|
||
}
|
||
|
||
static uint32_t count_records(const uint8_t *buf, uint32_t len) {
|
||
uint32_t at = 0, n = 0;
|
||
while (at + REC_HEAD <= len) {
|
||
uint16_t l;
|
||
memcpy(&l, buf + at + 9, 2);
|
||
at += REC_HEAD + l;
|
||
n++;
|
||
}
|
||
return n;
|
||
}
|
||
|
||
static int send_buffer(VM *vm) {
|
||
uint8_t payload[SK_HERMES_CHUNK_MAX_PAYLOAD];
|
||
uint32_t len = 0, records = count_records(vm->log_buf, vm->log_buf_len);
|
||
VMRegistryEntry artemis;
|
||
|
||
if (vm->log_buf_len == 0 && vm->log_lost == 0) return 0;
|
||
|
||
if (vm->log_lost > 0) {
|
||
char note[64];
|
||
int n = snprintf(note, sizeof(note), "%u log records not sent: Artemis not ready",
|
||
(unsigned)vm->log_lost);
|
||
if (n > 0) len += put_record(payload, LOG_WARN, 0, note, (uint16_t)n);
|
||
}
|
||
memcpy(payload + len, vm->log_buf, vm->log_buf_len);
|
||
len += vm->log_buf_len;
|
||
|
||
if (capsule_vm_find_by_name_nocase("Artemis", &artemis) != 0 || !artemis.vm_ptr ||
|
||
(artemis.state != VM_STATE_LIVE && artemis.state != VM_STATE_SWITCHED_OUT) ||
|
||
sk_hermes_send_one(vm->stadium_vm_id, artemis.vm_id, SK_HERMES_MSG_TYPE_LOG_BATCH,
|
||
0, payload, len) != 0) {
|
||
vm->log_lost += records;
|
||
vm->log_buf_len = 0;
|
||
return -1;
|
||
}
|
||
vm->log_lost = 0;
|
||
vm->log_buf_len = 0;
|
||
return (int)records;
|
||
}
|
||
|
||
void vm_log_buffer_add(VM *vm, uint8_t level, uint64_t timestamp,
|
||
const char *msg, uint32_t len) {
|
||
if (!vm || !msg) return;
|
||
if (g_busy) { vm->log_lost++; return; }
|
||
g_busy = 1;
|
||
|
||
if (len > VM_LOG_BUF_SIZE - REC_HEAD) len = VM_LOG_BUF_SIZE - REC_HEAD;
|
||
if (vm->log_buf_len + REC_HEAD + len > VM_LOG_BUF_SIZE) (void)send_buffer(vm);
|
||
vm->log_buf_len = (uint16_t)(vm->log_buf_len +
|
||
put_record(vm->log_buf + vm->log_buf_len, level, timestamp, msg, (uint16_t)len));
|
||
if (level == LOG_ERROR) (void)send_buffer(vm);
|
||
|
||
g_busy = 0;
|
||
}
|
||
|
||
int vm_log_buffer_flush(VM *vm) {
|
||
int rc;
|
||
if (!vm || g_busy) return -1;
|
||
g_busy = 1;
|
||
rc = send_buffer(vm);
|
||
g_busy = 0;
|
||
return rc;
|
||
}
|
||
|
||
int vm_log_batch_deliver(VM *vm, VMUuid from, const uint8_t *payload, uint32_t payload_len) {
|
||
VMRegistryEntry artemis, sender;
|
||
const char *source = "?";
|
||
uint32_t at = 0;
|
||
int written = 0, failed = 0;
|
||
|
||
if (capsule_vm_find_by_name_nocase("Artemis", &artemis) != 0 || artemis.vm_ptr != (void *)vm) {
|
||
return -1;
|
||
}
|
||
/* The source is the sender the kernel stamped on the message: a VM
|
||
* cannot write a record under another's name. */
|
||
if (capsule_vm_registry_get(from, &sender) == 0) source = sender.name;
|
||
|
||
while (at + REC_HEAD <= payload_len) {
|
||
uint8_t level = payload[at];
|
||
uint64_t ts;
|
||
uint16_t len;
|
||
memcpy(&ts, payload + at + 1, 8);
|
||
memcpy(&len, payload + at + 9, 2);
|
||
if (at + REC_HEAD + len > payload_len) break; /* malformed tail: stop */
|
||
if (log_region_append(level, ts, source, (uint32_t)strlen(source),
|
||
(const char *)payload + at + REC_HEAD, len) < 0) {
|
||
failed++;
|
||
} else {
|
||
written++;
|
||
}
|
||
at += REC_HEAD + len;
|
||
}
|
||
if (failed) console_println("Artemis: log ring write failed -- records not kept");
|
||
return written;
|
||
}
|
||
|
||
#endif /* __STARKERNEL__ */
|