Files
MontaukOS/programs/src/crashpad/main.cpp
T

306 lines
8.7 KiB
C++

/*
* main.cpp
* MontaukOS Crashpad - renders process crash information
* Copyright (c) 2026 Daniel Hammer
*/
#include <montauk/syscall.h>
#include <montauk/heap.h>
#include <montauk/string.h>
#include <gui/standalone.hpp>
#include <gui/gui.hpp>
#include <gui/font.hpp>
using namespace gui;
static bool is_desktop_running() {
static montauk::abi::ProcInfo procs[256];
int count = montauk::proclist(procs, 256);
if (count <= 0) return false;
int self_pid = montauk::getpid();
for (int i = 0; i < count; i++) {
if (montauk::streq(procs[i].name, "0:/os/desktop.elf")) {
return true;
}
}
return false;
}
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::abi::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();
}
// ==== Console (no-desktop) crash report ====
// When no desktop is running, the crashpad is spawned with its stdout
// redirected to the same out-stream as the crashed process (wired up by the
// kernel in SpawnCrashPad), so montauk::print() lands in the console terminal.
static void emit(const char* s) { montauk::print(s); }
static void emit_kv_hex(const char* key, uint64_t val) {
char buf[32];
emit(" ");
emit(key);
emit(": ");
format_hex16(buf, val);
emit(buf);
emit("\n");
}
static void render_console() {
char num[16];
emit("\n\033[1;31m*** Process crashed ***\033[0m\n");
emit("Process: ");
emit(g_report.processName);
emit(" (pid ");
format_decimal(num, g_report.pid);
emit(num);
emit(")\n");
emit("Exception: ");
emit(g_report.exceptionName);
emit("\n");
emit_kv_hex("RIP", g_report.instructionPointer);
emit_kv_hex("RSP", g_report.stackPointer);
emit_kv_hex("RFLAGS", g_report.flags);
if (g_report.exceptionVector == 0x0E)
emit_kv_hex("CR2 (fault addr)", g_report.faultingAddress);
emit("\n");
}
extern "C" void _start() {
fonts::init();
// Poll for crash report
int available = montauk::crash_report(&g_report);
if (available <= 0) montauk::exit(1);
if (is_desktop_running()) {
if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1);
render();
while (true) {
montauk::abi::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();
} else {
// No desktop: print a textual report to the redirected console.
render_console();
}
montauk::exit(0);
}