Files
zer0condition-GoodmansKernel/driver/trace_ring.c
T
zer0condition 00a3adf890 initial commit
2026-08-19 01:39:41 +05:30

138 lines
4.2 KiB
C

/* trace_ring.c - per-import call trace for wasm guest debugging.
* ring buffer holds recent host-import invocations, export calls, and traps.
* GUI polls via ioctl for live view.
*/
#include "inc/gvm.h"
#include "../shared/goodmans_ioctl.h"
static gvm_trace_entry g_ring[GVM_TRACE_ENTRIES];
static ULONG64 g_seq = 0;
static ULONG g_head = 0;
static KSPIN_LOCK g_lock;
static BOOLEAN g_init = FALSE;
static volatile LONG g_mode = GVM_TRACE_OFF;
static volatile LONG g_only = 0; // module_id filter when mode==ON_MODULE
void
gvm_trace_init(void)
{
if (g_init) return;
RtlZeroMemory(g_ring, sizeof(g_ring));
KeInitializeSpinLock(&g_lock);
g_init = TRUE;
}
BOOLEAN
gvm_trace_enabled_for(unsigned int module_id)
{
LONG m = g_mode;
if (m == GVM_TRACE_OFF) return FALSE;
if (m == GVM_TRACE_ON_ALL) return TRUE;
return (LONG)module_id == g_only;
}
void
gvm_trace_push(unsigned int module_id, unsigned int kind, const char* name,
unsigned int argc, const ULONG64* argv, ULONG64 rv)
{
if (!g_init) return;
if (!gvm_trace_enabled_for(module_id)) return;
LARGE_INTEGER now;
KeQuerySystemTimePrecise(&now);
// capture pre-lock so lock IRQL doesn't confuse the value
unsigned int cap_irql = (unsigned int)KeGetCurrentIrql();
unsigned int cap_tid = (unsigned int)(ULONG_PTR)PsGetCurrentThreadId();
KIRQL irql;
KeAcquireSpinLock(&g_lock, &irql);
gvm_trace_entry* e = &g_ring[g_head];
RtlZeroMemory(e, sizeof(*e));
e->timestamp_100ns = (ULONG64)now.QuadPart;
e->module_id = module_id;
e->kind = kind;
e->thread_id = cap_tid;
e->irql = cap_irql;
e->argc = argc > 4 ? 4 : argc;
for (unsigned int i = 0; i < e->argc; i++) e->argv[i] = argv[i];
e->rv = rv;
if (name) RtlStringCbCopyA(e->name, sizeof(e->name), name);
g_seq++;
g_head = (g_head + 1) % GVM_TRACE_ENTRIES;
KeReleaseSpinLock(&g_lock, irql);
}
NTSTATUS
gvm_ioctl_tail_trace(PIRP irp, PIO_STACK_LOCATION sp)
{
ULONG in_len = sp->Parameters.DeviceIoControl.InputBufferLength;
ULONG out_len = sp->Parameters.DeviceIoControl.OutputBufferLength;
void* buf = irp->AssociatedIrp.SystemBuffer;
if (in_len < sizeof(gvm_trace_in) || out_len < sizeof(gvm_trace_out) || !buf) {
irp->IoStatus.Information = 0;
return STATUS_INVALID_PARAMETER;
}
gvm_trace_in in;
RtlCopyMemory(&in, buf, sizeof(in));
gvm_trace_out* out = (gvm_trace_out*)ExAllocatePoolWithTag(
NonPagedPoolNx, sizeof(gvm_trace_out), 'gTrc');
if (!out) return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(out, sizeof(*out));
KIRQL irql;
KeAcquireSpinLock(&g_lock, &irql);
ULONG64 seq_here = g_seq;
ULONG64 want_from = in.last_seq;
ULONG64 available = 0;
ULONG64 dropped = 0;
if (seq_here > want_from) {
available = seq_here - want_from;
if (available > GVM_TRACE_ENTRIES) {
dropped = available - GVM_TRACE_ENTRIES;
available = GVM_TRACE_ENTRIES;
}
ULONG start = (g_head + GVM_TRACE_ENTRIES - (ULONG)available) % GVM_TRACE_ENTRIES;
for (ULONG64 i = 0; i < available; i++) {
ULONG src = (start + (ULONG)i) % GVM_TRACE_ENTRIES;
out->entries[i] = g_ring[src];
}
}
out->next_seq = seq_here;
out->count = (unsigned int)available;
out->dropped = (unsigned int)dropped;
KeReleaseSpinLock(&g_lock, irql);
RtlCopyMemory(buf, out, sizeof(*out));
ExFreePoolWithTag(out, 'gTrc');
irp->IoStatus.Information = sizeof(gvm_trace_out);
return STATUS_SUCCESS;
}
NTSTATUS
gvm_ioctl_trace_ctl(PIRP irp, PIO_STACK_LOCATION sp)
{
ULONG in_len = sp->Parameters.DeviceIoControl.InputBufferLength;
void* buf = irp->AssociatedIrp.SystemBuffer;
if (in_len < sizeof(gvm_trace_ctl_in) || !buf) {
irp->IoStatus.Information = 0;
return STATUS_INVALID_PARAMETER;
}
gvm_trace_ctl_in in;
RtlCopyMemory(&in, buf, sizeof(in));
InterlockedExchange(&g_mode, (LONG)in.mode);
InterlockedExchange(&g_only, (LONG)in.module_id);
gvm_log_push("trace mode=%u module=%u", in.mode, in.module_id);
irp->IoStatus.Information = 0;
return STATUS_SUCCESS;
}