feat: add support for console sessions to crashpad and crash handling in kernel
This commit is contained in:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 112
|
#define MONTAUK_BUILD_NUMBER 115
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ namespace Hal {
|
|||||||
rep.timestampTick = Timekeeping::GetTicks();
|
rep.timestampTick = Timekeeping::GetTicks();
|
||||||
|
|
||||||
CrashReport::AddReport(rep);
|
CrashReport::AddReport(rep);
|
||||||
Sched::SpawnCrashPad();
|
Sched::SpawnCrashPad(proc->pid);
|
||||||
|
|
||||||
Sched::ExitProcess();
|
Sched::ExitProcess();
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
|
|||||||
@@ -1388,11 +1388,57 @@ namespace Sched {
|
|||||||
return &processTable[slot];
|
return &processTable[slot];
|
||||||
}
|
}
|
||||||
|
|
||||||
int SpawnCrashPad() {
|
int SpawnCrashPad(int crasherPid) {
|
||||||
// Spawn crashpad as a child of init (slot 0), not the crashing process.
|
// 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.
|
// The crasher is still alive here (we run before its ExitProcess), but
|
||||||
|
// it is about to die, so we must not parent the crashpad to it.
|
||||||
static constexpr const char* crashpadPath = "0:/apps/crashpad/crashpad.elf";
|
static constexpr const char* crashpadPath = "0:/apps/crashpad/crashpad.elf";
|
||||||
return Spawn(crashpadPath, nullptr);
|
|
||||||
|
// If the crashing process had a redirected console (i.e. it was running
|
||||||
|
// under terminal.elf in a console session), hand the crashpad a write
|
||||||
|
// handle to that same out-stream so it can surface a textual crash
|
||||||
|
// report to the console user when no desktop is running.
|
||||||
|
Process* crasher = GetProcessByPid(crasherPid);
|
||||||
|
int crasherSlot = Ipc::SlotForPid(crasherPid);
|
||||||
|
bool inheritRedir = crasher && crasher->redirected &&
|
||||||
|
crasher->ioOutHandle >= 0 && crasherSlot >= 0;
|
||||||
|
|
||||||
|
Ipc::Object* outObj = nullptr;
|
||||||
|
Ipc::HandleType outType = Ipc::HandleType::None;
|
||||||
|
uint32_t outRights = 0;
|
||||||
|
if (inheritRedir) {
|
||||||
|
if (!Ipc::SnapshotHandleForSlot(crasherSlot, crasher->ioOutHandle,
|
||||||
|
outType, outObj, outRights) ||
|
||||||
|
outType != Ipc::HandleType::Stream || outObj == nullptr) {
|
||||||
|
inheritRedir = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deferred start when inheriting, so we can wire ioOutHandle before _start.
|
||||||
|
int childPid = Spawn(crashpadPath, nullptr, /*startReady=*/!inheritRedir);
|
||||||
|
if (childPid < 0 || !inheritRedir) return childPid;
|
||||||
|
|
||||||
|
Process* child = GetProcessByPid(childPid);
|
||||||
|
int childSlot = Ipc::SlotForPid(childPid);
|
||||||
|
if (child && childSlot >= 0) {
|
||||||
|
// Installing with RightWrite retains the stream (writerRefs++/refs++),
|
||||||
|
// so it outlives the crasher's imminent ExitProcess teardown. The
|
||||||
|
// snapshot above and this install run back-to-back with no blocking
|
||||||
|
// call between, while the crasher's handle still pins the stream.
|
||||||
|
int h = Ipc::InstallHandleForSlot(childSlot, outObj, Ipc::HandleType::Stream,
|
||||||
|
Ipc::RightWrite | Ipc::RightWait | Ipc::RightDup);
|
||||||
|
if (h >= 0) {
|
||||||
|
child->ioOutHandle = h;
|
||||||
|
child->redirected = true;
|
||||||
|
child->parentPid = crasherPid;
|
||||||
|
child->termCols = crasher->termCols;
|
||||||
|
child->termRows = crasher->termRows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always start it (even if wiring failed -> it just falls back to silent).
|
||||||
|
StartProcess(childPid);
|
||||||
|
return childPid;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,9 +190,11 @@ namespace Sched {
|
|||||||
// Find a process by PID (returns nullptr if not found or not alive)
|
// Find a process by PID (returns nullptr if not found or not alive)
|
||||||
Process* GetProcessByPid(int pid);
|
Process* GetProcessByPid(int pid);
|
||||||
|
|
||||||
// Spawn a crashpad process with the given crash file path as argument.
|
// Spawn the crashpad to report on a faulted process (pid `crasherPid`).
|
||||||
// Called by the exception handler when a process faults.
|
// Called by the exception handler when a process faults. If the crasher had
|
||||||
int SpawnCrashPad();
|
// a redirected console, the crashpad inherits its out-stream so it can print
|
||||||
|
// a textual report to a console session with no desktop.
|
||||||
|
int SpawnCrashPad(int crasherPid);
|
||||||
|
|
||||||
// Get a pointer to slot i in the process table (for enumeration)
|
// Get a pointer to slot i in the process table (for enumeration)
|
||||||
Process* GetProcessSlot(int slot);
|
Process* GetProcessSlot(int slot);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* main.cpp
|
* main.cpp
|
||||||
* MontaukOS Crashpad — displays crash reports in a popup window
|
* MontaukOS Crashpad - renders process crash information
|
||||||
* Copyright (c) 2026 Daniel Hammer
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -13,6 +13,20 @@
|
|||||||
|
|
||||||
using namespace gui;
|
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_W = 460;
|
||||||
static constexpr int WINDOW_H = 480;
|
static constexpr int WINDOW_H = 480;
|
||||||
static constexpr int PADDING = 12;
|
static constexpr int PADDING = 12;
|
||||||
@@ -201,6 +215,42 @@ static void render() {
|
|||||||
g_win.present();
|
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() {
|
extern "C" void _start() {
|
||||||
fonts::init();
|
fonts::init();
|
||||||
|
|
||||||
@@ -208,42 +258,48 @@ extern "C" void _start() {
|
|||||||
int available = montauk::crash_report(&g_report);
|
int available = montauk::crash_report(&g_report);
|
||||||
if (available <= 0) montauk::exit(1);
|
if (available <= 0) montauk::exit(1);
|
||||||
|
|
||||||
if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1);
|
if (is_desktop_running()) {
|
||||||
|
if (!g_win.create("Process Crashed", WINDOW_W, WINDOW_H)) montauk::exit(1);
|
||||||
|
|
||||||
render();
|
render();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
montauk::abi::WinEvent ev;
|
montauk::abi::WinEvent ev;
|
||||||
int r = g_win.poll(&ev);
|
int r = g_win.poll(&ev);
|
||||||
if (r < 0) break;
|
if (r < 0) break;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
montauk::sleep_ms(16);
|
montauk::sleep_ms(16);
|
||||||
continue;
|
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
|
if (ev.type == 3) break; // close
|
||||||
g_scroll -= 60;
|
|
||||||
if (g_scroll < 0) g_scroll = 0;
|
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();
|
render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.type == 2) {
|
g_win.destroy();
|
||||||
// Resize
|
} else {
|
||||||
render();
|
// No desktop: print a textual report to the redirected console.
|
||||||
}
|
render_console();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_win.destroy();
|
|
||||||
montauk::exit(0);
|
montauk::exit(0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user