feat: add process crash popups
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* CrashReportSyscall.hpp
|
||||
* SYS_CRASH_REPORT
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* CrashReport.hpp
|
||||
* Structured crash report for userspace process faults
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
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
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include "CrashReport.hpp"
|
||||
#include <Timekeeping/ApicTimer.hpp>
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user