From 886b181bfe5e8aecd1474a1f56d6b0016a819789 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Tue, 7 Apr 2026 20:15:56 +0200 Subject: [PATCH] feat: add process crash popups --- kernel/src/Api/CrashReportSyscall.hpp | 48 ++++ kernel/src/Api/Syscall.cpp | 3 + kernel/src/Api/Syscall.hpp | 26 ++ kernel/src/CrashReport.cpp | 21 ++ kernel/src/CrashReport.hpp | 50 ++++ kernel/src/Hal/IDT.cpp | 46 ++++ kernel/src/Sched/Scheduler.cpp | 7 + kernel/src/Sched/Scheduler.hpp | 4 + programs/GNUmakefile | 13 +- programs/include/Api/Syscall.hpp | 26 ++ programs/include/montauk/syscall.h | 5 + programs/libs/libhello/src/libhello.o | Bin 103512 -> 103568 bytes programs/src/crashpad/Makefile | 79 +++++++ programs/src/crashpad/font_data.cpp | 1 + programs/src/crashpad/main.cpp | 249 ++++++++++++++++++++ programs/src/crashpad/manifest.toml | 8 + programs/src/crashpad/stb_truetype_impl.cpp | 33 +++ 17 files changed, 615 insertions(+), 4 deletions(-) create mode 100644 kernel/src/Api/CrashReportSyscall.hpp create mode 100644 kernel/src/CrashReport.cpp create mode 100644 kernel/src/CrashReport.hpp create mode 100644 programs/src/crashpad/Makefile create mode 100644 programs/src/crashpad/font_data.cpp create mode 100644 programs/src/crashpad/main.cpp create mode 100644 programs/src/crashpad/manifest.toml create mode 100644 programs/src/crashpad/stb_truetype_impl.cpp diff --git a/kernel/src/Api/CrashReportSyscall.hpp b/kernel/src/Api/CrashReportSyscall.hpp new file mode 100644 index 0000000..618c74e --- /dev/null +++ b/kernel/src/Api/CrashReportSyscall.hpp @@ -0,0 +1,48 @@ +/* + * CrashReportSyscall.hpp + * SYS_CRASH_REPORT + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include "Syscall.hpp" +#include "../CrashReport.hpp" + +namespace Montauk { + + static int64_t Sys_CrashReport(CrashReportInfo* out) { + if (!out) return 0; + if (CrashReport::g_reportCount == 0) return 0; + + int oldestIdx = (CrashReport::g_reportHead - CrashReport::g_reportCount + CrashReport::MaxReports) % CrashReport::MaxReports; + auto* rep = &CrashReport::g_reports[oldestIdx]; + + out->pid = rep->pid; + for (int i = 0; i < 64 && rep->processName[i]; i++) out->processName[i] = rep->processName[i]; + out->processName[63] = '\0'; + out->exceptionVector = rep->exceptionVector; + for (int i = 0; i < 32 && rep->exceptionName[i]; i++) out->exceptionName[i] = rep->exceptionName[i]; + out->exceptionName[31] = '\0'; + out->faultingAddress = rep->faultingAddress; + out->instructionPointer = rep->instructionPointer; + out->stackPointer = rep->stackPointer; + out->codeSegment = rep->codeSegment; + out->flags = rep->flags; + out->stackSegment = rep->stackSegment; + out->pfPresent = rep->pfPresent; + out->pfWrite = rep->pfWrite; + out->pfUser = rep->pfUser; + out->pfReservedWrite = rep->pfReservedWrite; + out->pfInstructionFetch = rep->pfInstructionFetch; + out->pfProtectionKey = rep->pfProtectionKey; + out->pfShadowStack = rep->pfShadowStack; + out->pfSGX = rep->pfSGX; + out->timestampTick = rep->timestampTick; + rep->valid = false; + CrashReport::g_reportCount--; + + return CrashReport::g_reportCount + 1; + } + +} diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 5d1f14f..0eb6b74 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -34,6 +34,7 @@ #include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO #include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE #include "LibSyscall.hpp" // SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM +#include "CrashReportSyscall.hpp" // SYS_CRASH_REPORT // Assembly entry point extern "C" void SyscallEntry(); @@ -419,6 +420,8 @@ namespace Montauk { return (int64_t)Sys_DLSym(frame->arg1, frame->arg2); case SYS_GETLIBBASE: return (int64_t)Sys_GetLibBase(frame->arg1); + case SYS_CRASH_REPORT: + return Sys_CrashReport((CrashReportInfo*)frame->arg1); default: return -1; } diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index ce27865..a45edea 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -212,6 +212,9 @@ namespace Montauk { static constexpr uint64_t SYS_DLSYM = 116; static constexpr uint64_t SYS_GETLIBBASE = 117; + /* CrashReport.hpp */ + static constexpr uint64_t SYS_CRASH_REPORT = 118; + static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0; static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1; static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2; @@ -422,6 +425,29 @@ namespace Montauk { uint32_t _pad; }; + // Crash report (filled by kernel on process fault, returned via SYS_CRASH_REPORT) + struct CrashReportInfo { + int pid; + char processName[64]; + uint8_t exceptionVector; + char exceptionName[32]; + uint64_t faultingAddress; + uint64_t instructionPointer; + uint64_t stackPointer; + uint64_t codeSegment; + uint64_t flags; + uint64_t stackSegment; + uint8_t pfPresent : 1; + uint8_t pfWrite : 1; + uint8_t pfUser : 1; + uint8_t pfReservedWrite : 1; + uint8_t pfInstructionFetch : 1; + uint8_t pfProtectionKey : 1; + uint8_t pfShadowStack : 1; + uint8_t pfSGX : 1; + uint64_t timestampTick; + }; + // Stack frame pushed by SyscallEntry.asm struct SyscallFrame { uint64_t r15, r14, r13, r12, rbp, rbx; // callee-saved diff --git a/kernel/src/CrashReport.cpp b/kernel/src/CrashReport.cpp new file mode 100644 index 0000000..6613e92 --- /dev/null +++ b/kernel/src/CrashReport.cpp @@ -0,0 +1,21 @@ +/* + * CrashReport.cpp + * Structured crash report implementation + * Copyright (c) 2026 Daniel Hammer +*/ + +#include "CrashReport.hpp" + +namespace CrashReport { + +Report g_reports[MaxReports]; +int g_reportHead = 0; +int g_reportCount = 0; + +void AddReport(const Report& report) { + g_reports[g_reportHead] = report; + g_reportHead = (g_reportHead + 1) % MaxReports; + if (g_reportCount < MaxReports) g_reportCount++; +} + +} // namespace CrashReport diff --git a/kernel/src/CrashReport.hpp b/kernel/src/CrashReport.hpp new file mode 100644 index 0000000..20afb01 --- /dev/null +++ b/kernel/src/CrashReport.hpp @@ -0,0 +1,50 @@ +/* + * CrashReport.hpp + * Structured crash report for userspace process faults + * Copyright (c) 2026 Daniel Hammer +*/ + +#pragma once +#include +#include + +namespace CrashReport { + +static constexpr int MaxReports = 8; + +struct Report { + int pid; + char processName[64]; + uint8_t exceptionVector; + char exceptionName[32]; + + uint64_t faultingAddress; + uint64_t instructionPointer; + uint64_t stackPointer; + uint64_t codeSegment; + uint64_t flags; + uint64_t stackSegment; + + // Page fault error code bits (vector 0xE / page fault only) + uint8_t pfPresent : 1; + uint8_t pfWrite : 1; + uint8_t pfUser : 1; + uint8_t pfReservedWrite : 1; + uint8_t pfInstructionFetch : 1; + uint8_t pfProtectionKey : 1; + uint8_t pfShadowStack : 1; + uint8_t pfSGX : 1; + + uint64_t timestampTick; + bool valid; +}; + +// Kernel-side ring buffer (written by exception handlers, read by SYS_CRASH_REPORT) +extern Report g_reports[MaxReports]; +extern int g_reportHead; +extern int g_reportCount; + +// Add a report to the kernel ring buffer +void AddReport(const Report& report); + +} // namespace CrashReport diff --git a/kernel/src/Hal/IDT.cpp b/kernel/src/Hal/IDT.cpp index 44961ed..65164e3 100644 --- a/kernel/src/Hal/IDT.cpp +++ b/kernel/src/Hal/IDT.cpp @@ -12,6 +12,8 @@ #include #include #include +#include "CrashReport.hpp" +#include namespace Hal { constexpr auto InterruptGate = 0x8E; @@ -87,6 +89,50 @@ namespace Hal { << ExceptionStrings[i] << " in process \"" << proc->name << "\" (pid " << proc->pid << ") - process terminated"; + + // Capture crash report + CrashReport::Report rep; + rep.valid = true; + rep.pid = proc->pid; + for (int j = 0; j < 64 && proc->name[j]; j++) rep.processName[j] = proc->name[j]; + rep.processName[63] = '\0'; + rep.exceptionVector = i; + for (int j = 0; j < 32 && ExceptionStrings[i][j]; j++) rep.exceptionName[j] = ExceptionStrings[i][j]; + rep.exceptionName[31] = '\0'; + rep.instructionPointer = frame->IP; + rep.codeSegment = frame->CS; + rep.flags = frame->Flags; + rep.stackPointer = frame->SP; + rep.stackSegment = frame->SS; + + if (i == 0x0E) { + // Page fault: read CR2 for faulting address + asm volatile("mov %%cr2, %0" : "=r"(rep.faultingAddress)); + auto* pf = (System::PageFaultPanicFrame*)frame; + rep.pfPresent = pf->PageFaultError.Present; + rep.pfWrite = pf->PageFaultError.Write; + rep.pfUser = pf->PageFaultError.User; + rep.pfReservedWrite = pf->PageFaultError.ReservedWrite; + rep.pfInstructionFetch = pf->PageFaultError.InstructionFetch; + rep.pfProtectionKey = pf->PageFaultError.ProtectionKey; + rep.pfShadowStack = pf->PageFaultError.ShadowStack; + rep.pfSGX = pf->PageFaultError.SGX; + } else { + rep.faultingAddress = 0; + rep.pfPresent = 0; + rep.pfWrite = 0; + rep.pfUser = 0; + rep.pfReservedWrite = 0; + rep.pfInstructionFetch = 0; + rep.pfProtectionKey = 0; + rep.pfShadowStack = 0; + rep.pfSGX = 0; + } + rep.timestampTick = Timekeeping::GetTicks(); + + CrashReport::AddReport(rep); + Sched::SpawnCrashPad(); + Sched::ExitProcess(); __builtin_unreachable(); } else { diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index 09fe990..c2eca79 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -902,4 +902,11 @@ namespace Sched { return &processTable[slot]; } + int SpawnCrashPad() { + // Spawn crashpad as a child of init (slot 0), not the crashing process. + // We can't use the current process context since we're in ExitProcess. + static constexpr const char* crashpadPath = "0:/apps/crashpad/crashpad.elf"; + return Spawn(crashpadPath, nullptr); + } + } diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index 7ebb684..bfe74c8 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -122,6 +122,10 @@ namespace Sched { // Find a process by PID (returns nullptr if not found or not alive) Process* GetProcessByPid(int pid); + // Spawn a crashpad process with the given crash file path as argument. + // Called by the exception handler when a process faults. + int SpawnCrashPad(); + // Get a pointer to slot i in the process table (for enumeration) Process* GetProcessSlot(int slot); diff --git a/programs/GNUmakefile b/programs/GNUmakefile index 290e5b9..1502823 100644 --- a/programs/GNUmakefile +++ b/programs/GNUmakefile @@ -59,7 +59,7 @@ BINDIR := bin PROGRAMS := $(notdir $(wildcard src/*)) # Programs with custom Makefiles (built separately). -CUSTOM_BUILDS := doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator desktop login shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs test_dl libloader libhello +CUSTOM_BUILDS := doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator desktop login shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs test_dl libloader libhello crashpad SYSTEM_PROGRAMS := $(filter-out $(CUSTOM_BUILDS),$(PROGRAMS)) # Build targets: system programs go to bin/os/, apps go to bin/apps//. @@ -81,9 +81,9 @@ CA_CERTS := $(BINDIR)/etc/ca-certificates.crt # Common shared assets (wallpapers, etc.) COMMONKEEP := $(BINDIR)/common/.keep -.PHONY: all clean doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs icons fonts bearssl libc tls libjpeg libjpegwrite install-apps libloader libs libhello test_dl +.PHONY: all clean doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs icons fonts bearssl libc tls libjpeg libjpegwrite install-apps libloader libs libhello test_dl crashpad -all: bearssl libc libjpeg libjpegwrite tls libloader libs libhello $(TARGETS) fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator doom rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs login desktop shell icons fonts install-apps test_dl $(MANDST) $(WWWDST) $(CA_CERTS) $(COMMONKEEP) +all: bearssl libc libjpeg libjpegwrite tls libloader libs libhello $(TARGETS) fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator doom rpgdemo paint tcc lua screenshot texteditor mandelbrot printers printd printctl dialogs login desktop shell icons fonts install-apps test_dl crashpad $(MANDST) $(WWWDST) $(CA_CERTS) $(COMMONKEEP) # Build BearSSL static library (cross-compiled for freestanding x86_64). BEARSSL_INCLUDES := -isystem $(shell cd .. && pwd)/kernel/freestnd-c-hdrs/x86_64/include -isystem $(abspath include/libc) @@ -277,12 +277,16 @@ printctl: bearssl libc tls dialogs: bearssl libc tls $(MAKE) -C src/dialogs +# Build crashpad (crash reporter popup, depends on libc). +crashpad: libc + $(MAKE) -C src/crashpad + # Build test_dl (dynamic loading test app, depends on libloader). test_dl: libloader libs $(MAKE) -C src/test_dl # Install app bundles (manifests, icons, data files) into bin/apps//. -install-apps: doom rpgdemo paint spreadsheet wordprocessor weather wikipedia imageviewer fontpreview pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator screenshot texteditor mandelbrot printers dialogs +install-apps: doom rpgdemo paint spreadsheet wordprocessor weather wikipedia imageviewer fontpreview pdfviewer disks devexplorer installer volume music video bluetooth terminal klog procmgr calculator screenshot texteditor mandelbrot printers dialogs crashpad ../scripts/install_apps.sh # Copy man pages into bin/man/ so mkramdisk.sh picks them up. @@ -350,6 +354,7 @@ clean: $(MAKE) -C src/printd clean $(MAKE) -C src/printctl clean $(MAKE) -C src/dialogs clean + $(MAKE) -C src/crashpad clean $(MAKE) -C src/libloader clean $(MAKE) -C libs/libmath clean $(MAKE) -C libs/libhello clean diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index b1d1222..b3af18c 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -150,6 +150,9 @@ namespace Montauk { static constexpr uint64_t SYS_DLSYM = 116; static constexpr uint64_t SYS_GETLIBBASE = 117; + /* CrashReport */ + static constexpr uint64_t SYS_CRASH_REPORT = 118; + static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0; static constexpr uint32_t IPC_SIGNAL_WRITABLE = 1u << 1; static constexpr uint32_t IPC_SIGNAL_PEER_CLOSED = 1u << 2; @@ -369,4 +372,27 @@ namespace Montauk { uint64_t pageSize; }; + // Crash report (filled by kernel on process fault, returned via SYS_CRASH_REPORT) + struct CrashReportInfo { + int pid; + char processName[64]; + uint8_t exceptionVector; + char exceptionName[32]; + uint64_t faultingAddress; + uint64_t instructionPointer; + uint64_t stackPointer; + uint64_t codeSegment; + uint64_t flags; + uint64_t stackSegment; + uint8_t pfPresent : 1; + uint8_t pfWrite : 1; + uint8_t pfUser : 1; + uint8_t pfReservedWrite : 1; + uint8_t pfInstructionFetch : 1; + uint8_t pfProtectionKey : 1; + uint8_t pfShadowStack : 1; + uint8_t pfSGX : 1; + uint64_t timestampTick; + }; + } diff --git a/programs/include/montauk/syscall.h b/programs/include/montauk/syscall.h index 9e7c126..14afb21 100644 --- a/programs/include/montauk/syscall.h +++ b/programs/include/montauk/syscall.h @@ -296,6 +296,11 @@ namespace montauk { return (uint64_t)syscall1(Montauk::SYS_GETLIBBASE, (uint64_t)handle); } + // Crash report retrieval + inline int crash_report(Montauk::CrashReportInfo* out) { + return (int)syscall1(Montauk::SYS_CRASH_REPORT, (uint64_t)out); + } + // Process management inline void waitpid(int pid) { syscall1(Montauk::SYS_WAITPID, (uint64_t)pid); } diff --git a/programs/libs/libhello/src/libhello.o b/programs/libs/libhello/src/libhello.o index 9f911d5bc5be02c7bad5e3b0100c770bec7f86f6..74abc1c458fded44e0202cf23d9a09f7e090de12 100644 GIT binary patch delta 32984 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 KO8D;AQ~n>o3L@SB diff --git a/programs/src/crashpad/Makefile b/programs/src/crashpad/Makefile new file mode 100644 index 0000000..553c90c --- /dev/null +++ b/programs/src/crashpad/Makefile @@ -0,0 +1,79 @@ +# Makefile for crashpad (standalone Window Server app) on MontaukOS +# Copyright (c) 2026 Daniel Hammer + +MAKEFLAGS += -rR +.SUFFIXES: + +TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf- +ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),) + CXX := $(TOOLCHAIN_PREFIX)g++ +else + CXX := g++ +endif + +PROG_INC := ../../include +LINK_LD := ../../link.ld +BINDIR := ../../bin +OBJDIR := obj +LIBDIR := ../../lib + +CXXFLAGS := \ + -std=gnu++20 \ + -g -O2 -pipe \ + -Wall \ + -Wextra \ + -Wno-unused-parameter \ + -Wno-unused-function \ + -nostdinc \ + -ffreestanding \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-PIC \ + -fno-rtti \ + -fno-exceptions \ + -ffunction-sections \ + -fdata-sections \ + -m64 \ + -march=x86-64 \ + -msse \ + -msse2 \ + -mno-red-zone \ + -mcmodel=small \ + -MMD -MP \ + -I $(PROG_INC) \ + -isystem $(PROG_INC)/libc \ + -I ../../lib/bearssl/inc \ + -isystem ../../../kernel/freestnd-c-hdrs/x86_64/include \ + -isystem ../../../kernel/freestnd-cxx-hdrs/x86_64/include + +LDFLAGS := \ + -nostdlib \ + -static \ + -Wl,--build-id=none \ + -Wl,--gc-sections \ + -Wl,-m,elf_x86_64 \ + -z max-page-size=0x1000 \ + -T $(LINK_LD) + +SRCS := main.cpp stb_truetype_impl.cpp font_data.cpp +OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o)) + +TARGET := $(BINDIR)/apps/crashpad/crashpad.elf +LIBS := $(LIBDIR)/libc/liblibc.a + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(OBJS) $(LINK_LD) Makefile $(LIBS) + mkdir -p $(BINDIR)/apps/crashpad + $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +$(OBJDIR)/%.o: %.cpp Makefile + mkdir -p $(OBJDIR) + $(CXX) $(CXXFLAGS) -c $< -o $@ + +-include $(OBJS:.o=.d) + +clean: + rm -rf $(OBJDIR) $(TARGET) diff --git a/programs/src/crashpad/font_data.cpp b/programs/src/crashpad/font_data.cpp new file mode 100644 index 0000000..a591f69 --- /dev/null +++ b/programs/src/crashpad/font_data.cpp @@ -0,0 +1 @@ +#include "../desktop/font_data.cpp" diff --git a/programs/src/crashpad/main.cpp b/programs/src/crashpad/main.cpp new file mode 100644 index 0000000..ace24ce --- /dev/null +++ b/programs/src/crashpad/main.cpp @@ -0,0 +1,249 @@ +/* + * main.cpp + * MontaukOS Crashpad — displays crash reports in a popup window + * Copyright (c) 2026 Daniel Hammer +*/ + +#include +#include +#include +#include +#include +#include + +using namespace gui; + +static constexpr int WINDOW_W = 460; +static constexpr int WINDOW_H = 480; +static constexpr int PADDING = 12; +static constexpr int HEADER_H = 48; +static constexpr int COL1_W = 200; +static constexpr int SECTION_GAP = 10; + +WsWindow g_win; +Montauk::CrashReportInfo g_report = {}; +int g_scroll = 0; + +// Colors - light theme matching DiskDetail +static constexpr Color BG = Color::from_rgb(0xFF, 0xFF, 0xFF); +static constexpr Color ROW_EVEN = Color::from_rgb(0xF7, 0xF7, 0xF7); +static constexpr Color ACCENT = Color::from_rgb(0x33, 0x66, 0xCC); +static constexpr Color BORDER = Color::from_rgb(0xE0, 0xE0, 0xE0); +static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22); +static constexpr Color DIM_TEXT = Color::from_rgb(0x66, 0x66, 0x66); +static constexpr Color VALUE_COL = Color::from_rgb(0x22, 0x22, 0x22); + +// Custom hex formatter (avoids snprintf which isn't in freestanding libc) +static void format_hex16(char* out, uint64_t val) { + out[0] = '0'; + out[1] = 'x'; + static const char hex[] = "0123456789ABCDEF"; + for (int i = 0; i < 16; i++) { + out[2 + i] = hex[(val >> (60 - i * 4)) & 0xF]; + } + out[18] = '\0'; +} + +static void format_decimal(char* out, int val) { + if (val == 0) { + out[0] = '0'; + out[1] = '\0'; + return; + } + if (val < 0) { + *out++ = '-'; + val = -val; + } + char tmp[16]; + int i = 0; + while (val > 0) { + tmp[i++] = '0' + (val % 10); + val /= 10; + } + while (i > 0) { + *out++ = tmp[--i]; + } + *out = '\0'; +} + +// Draw a section header label + horizontal rule +static void section_header(Canvas& c, int& y, int w, int fh, const char* label) { + if (y < -fh - 8 || y > c.h + fh + 8) { + y += fh + 8; + return; + } + c.text(PADDING, y, label, ACCENT); + y += fh; + c.hline(PADDING, y, w - PADDING * 2, BORDER); + y += 2; +} + +// Draw one alternating table row (key left, value right) +static void table_row(Canvas& c, int& y, int row_h, int col1_w, int w, + int rowIdx, const char* key, const char* val, Color val_col) { + if (y > c.h + row_h || y < -row_h) { + y += row_h; + return; + } + if (rowIdx % 2 == 0) + c.fill_rect(PADDING, y, w - PADDING * 2, row_h, ROW_EVEN); + int ty = y + (row_h - system_font_height()) / 2; + c.text(PADDING + 8, ty, key, DIM_TEXT); + c.text(PADDING + col1_w + 8, ty, val, val_col); + y += row_h; +} + +static void render() { + if (g_win.id < 0) return; + Canvas c = g_win.canvas(); + c.fill(BG); + + int fh = system_font_height(); + int row_h = fh + 8; + int w = g_win.width; + int y = PADDING - g_scroll; + int rowIdx = 0; + + // Process info header (outside table) + if (y >= -fh * 2 && y < c.h + fh) { + char buf[128]; + char* p = buf; + + // Process name (large, white) + const char* name = g_report.processName; + while (*name) *p++ = *name++; + *p++ = ' '; + *p++ = '('; + *p++ = 'P'; + *p++ = 'I'; + *p++ = 'D'; + *p++ = ' '; + format_decimal(p, g_report.pid); + while (*p) p++; + *p++ = ')'; + *p = '\0'; + + c.text(PADDING, y, buf, TEXT_COLOR); + y += fh + 4; + + // Exception name below + char excbuf[64]; + p = excbuf; + const char* exc = g_report.exceptionName; + while (*exc) *p++ = *exc++; + *p++ = ' '; + *p++ = '('; + *p++ = 'v'; + *p++ = 'e'; + *p++ = 'c'; + *p++ = 't'; + *p++ = 'o'; + *p++ = 'r'; + *p++ = ' '; + *p++ = '0'; + *p++ = 'x'; + static const char hex[] = "0123456789ABCDEF"; + *p++ = hex[(g_report.exceptionVector >> 4) & 0xF]; + *p++ = hex[g_report.exceptionVector & 0xF]; + *p++ = ')'; + *p++ = '\0'; + c.text(PADDING, y, excbuf, DIM_TEXT); + y += fh; + } else { + y += fh + 4 + fh; + } + + y += SECTION_GAP; + + // Registers section + section_header(c, y, w, fh, "Registers"); + + char hexbuf[32]; + auto draw_hex_row = [&](const char* key, uint64_t val) { + format_hex16(hexbuf, val); + table_row(c, y, row_h, COL1_W, w, rowIdx++, key, hexbuf, VALUE_COL); + }; + + draw_hex_row("Instruction Pointer", g_report.instructionPointer); + draw_hex_row("Code Segment", g_report.codeSegment); + draw_hex_row("Flags", g_report.flags); + draw_hex_row("Stack Pointer", g_report.stackPointer); + draw_hex_row("Stack Segment", g_report.stackSegment); + + y += SECTION_GAP; + + // Page Fault section (only for vector 0x0E) + if (g_report.exceptionVector == 0x0E) { + section_header(c, y, w, fh, "Page Fault"); + + format_hex16(hexbuf, g_report.faultingAddress); + table_row(c, y, row_h, COL1_W, w, rowIdx++, "Faulting Address (CR2)", hexbuf, + TEXT_COLOR); + + // Error code bits + auto draw_pf_bit = [&](const char* label, bool val) { + Color col = val ? Color::from_rgb(0x22, 0x88, 0x22) : Color::from_rgb(0xAA, 0xAA, 0xAA); + table_row(c, y, row_h, COL1_W, w, rowIdx++, label, val ? "1" : "0", col); + }; + + draw_pf_bit("Present", g_report.pfPresent); + draw_pf_bit("Write", g_report.pfWrite); + draw_pf_bit("User", g_report.pfUser); + draw_pf_bit("Reserved Write", g_report.pfReservedWrite); + draw_pf_bit("Instruction Fetch", g_report.pfInstructionFetch); + draw_pf_bit("Protection Key", g_report.pfProtectionKey); + draw_pf_bit("Shadow Stack", g_report.pfShadowStack); + draw_pf_bit("SGX", g_report.pfSGX); + + y += SECTION_GAP; + } + + g_win.present(); +} + +extern "C" void _start() { + fonts::init(); + + // Poll for crash report + int available = montauk::crash_report(&g_report); + if (available <= 0) montauk::exit(1); + + if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1); + + render(); + + while (true) { + Montauk::WinEvent ev; + int r = g_win.poll(&ev); + if (r < 0) break; + if (r == 0) { + montauk::sleep_ms(16); + continue; + } + + if (ev.type == 3) break; // close + + if (ev.type == 0 && ev.key.pressed) { + if (ev.key.scancode == 0x01) break; // Escape + if (ev.key.scancode == 0x51) { + // Page Down: scroll down + g_scroll += 60; + render(); + } + if (ev.key.scancode == 0x49) { + // Page Up: scroll up + g_scroll -= 60; + if (g_scroll < 0) g_scroll = 0; + render(); + } + } + + if (ev.type == 2) { + // Resize + render(); + } + } + + g_win.destroy(); + montauk::exit(0); +} diff --git a/programs/src/crashpad/manifest.toml b/programs/src/crashpad/manifest.toml new file mode 100644 index 0000000..4ce8e67 --- /dev/null +++ b/programs/src/crashpad/manifest.toml @@ -0,0 +1,8 @@ +[app] +name = "Crashpad" +binary = "crashpad.elf" +icon = "" + +[menu] +category = "System" +visible = false diff --git a/programs/src/crashpad/stb_truetype_impl.cpp b/programs/src/crashpad/stb_truetype_impl.cpp new file mode 100644 index 0000000..dd91785 --- /dev/null +++ b/programs/src/crashpad/stb_truetype_impl.cpp @@ -0,0 +1,33 @@ +/* + * stb_truetype_impl.cpp + * Single compilation unit for stb_truetype in MontaukOS freestanding environment + * Copyright (c) 2026 Daniel Hammer + */ + +#include +#include +#include +#include +#include + +#define STBTT_ifloor(x) ((int) stb_floor(x)) +#define STBTT_iceil(x) ((int) stb_ceil(x)) +#define STBTT_sqrt(x) stb_sqrt(x) +#define STBTT_pow(x,y) stb_pow(x,y) +#define STBTT_fmod(x,y) stb_fmod(x,y) +#define STBTT_cos(x) stb_cos(x) +#define STBTT_acos(x) stb_acos(x) +#define STBTT_fabs(x) stb_fabs(x) + +#define STBTT_malloc(x,u) ((void)(u), montauk::malloc(x)) +#define STBTT_free(x,u) ((void)(u), montauk::mfree(x)) + +#define STBTT_memcpy(d,s,n) montauk::memcpy(d,s,n) +#define STBTT_memset(d,v,n) montauk::memset(d,v,n) + +#define STBTT_strlen(x) montauk::slen(x) + +#define STBTT_assert(x) ((void)(x)) + +#define STB_TRUETYPE_IMPLEMENTATION +#include