feat: add crash stack snapshots and context menu annotations
This commit is contained in:
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 106
|
#define MONTAUK_BUILD_NUMBER 108
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace montauk::abi {
|
|||||||
out->codeSegment = rep.codeSegment;
|
out->codeSegment = rep.codeSegment;
|
||||||
out->flags = rep.flags;
|
out->flags = rep.flags;
|
||||||
out->stackSegment = rep.stackSegment;
|
out->stackSegment = rep.stackSegment;
|
||||||
|
out->stackWordCount = rep.stackWordCount;
|
||||||
|
for (int i = 0; i < rep.stackWordCount && i < CrashReport::StackSnapshotWords; i++)
|
||||||
|
out->stackWords[i] = rep.stackWords[i];
|
||||||
out->pfPresent = rep.pfPresent;
|
out->pfPresent = rep.pfPresent;
|
||||||
out->pfWrite = rep.pfWrite;
|
out->pfWrite = rep.pfWrite;
|
||||||
out->pfUser = rep.pfUser;
|
out->pfUser = rep.pfUser;
|
||||||
|
|||||||
@@ -780,6 +780,8 @@ namespace montauk::abi {
|
|||||||
uint64_t codeSegment;
|
uint64_t codeSegment;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
uint64_t stackSegment;
|
uint64_t stackSegment;
|
||||||
|
uint64_t stackWords[8];
|
||||||
|
uint8_t stackWordCount;
|
||||||
uint8_t pfPresent : 1;
|
uint8_t pfPresent : 1;
|
||||||
uint8_t pfWrite : 1;
|
uint8_t pfWrite : 1;
|
||||||
uint8_t pfUser : 1;
|
uint8_t pfUser : 1;
|
||||||
|
|||||||
@@ -127,6 +127,21 @@ namespace Hal {
|
|||||||
rep.stackPointer = regs->SP;
|
rep.stackPointer = regs->SP;
|
||||||
rep.stackSegment = regs->SS;
|
rep.stackSegment = regs->SS;
|
||||||
|
|
||||||
|
// An indirect CALL pushes its real continuation address before a
|
||||||
|
// bad target faults on instruction fetch. Preserve a small stack
|
||||||
|
// snapshot while the dying process address space is still active;
|
||||||
|
// stackWords[0] will usually identify the caller even when RIP is
|
||||||
|
// corrupted data and cannot itself be symbolicated.
|
||||||
|
constexpr uint64_t snapshotBytes =
|
||||||
|
CrashReport::StackSnapshotWords * sizeof(uint64_t);
|
||||||
|
if (Memory::VMM::Paging::IsUserRangeAccessible(
|
||||||
|
proc->pml4Phys, regs->SP, snapshotBytes, false)) {
|
||||||
|
auto* userStack = (const volatile uint64_t*)regs->SP;
|
||||||
|
for (int word = 0; word < CrashReport::StackSnapshotWords; word++)
|
||||||
|
rep.stackWords[word] = userStack[word];
|
||||||
|
rep.stackWordCount = CrashReport::StackSnapshotWords;
|
||||||
|
}
|
||||||
|
|
||||||
if (i == 0x0E) {
|
if (i == 0x0E) {
|
||||||
// Page fault: read CR2 for faulting address
|
// Page fault: read CR2 for faulting address
|
||||||
asm volatile("mov %%cr2, %0" : "=r"(rep.faultingAddress));
|
asm volatile("mov %%cr2, %0" : "=r"(rep.faultingAddress));
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
namespace CrashReport {
|
namespace CrashReport {
|
||||||
|
|
||||||
static constexpr int MaxReports = 8;
|
static constexpr int MaxReports = 8;
|
||||||
|
static constexpr int StackSnapshotWords = 8;
|
||||||
|
|
||||||
struct Report {
|
struct Report {
|
||||||
int pid;
|
int pid;
|
||||||
@@ -24,6 +25,8 @@ struct Report {
|
|||||||
uint64_t codeSegment;
|
uint64_t codeSegment;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
uint64_t stackSegment;
|
uint64_t stackSegment;
|
||||||
|
uint64_t stackWords[StackSnapshotWords];
|
||||||
|
uint8_t stackWordCount;
|
||||||
|
|
||||||
// Page fault error code bits (vector 0xE / page fault only)
|
// Page fault error code bits (vector 0xE / page fault only)
|
||||||
uint8_t pfPresent : 1;
|
uint8_t pfPresent : 1;
|
||||||
|
|||||||
@@ -718,6 +718,8 @@ namespace montauk::abi {
|
|||||||
uint64_t codeSegment;
|
uint64_t codeSegment;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
uint64_t stackSegment;
|
uint64_t stackSegment;
|
||||||
|
uint64_t stackWords[8];
|
||||||
|
uint8_t stackWordCount;
|
||||||
uint8_t pfPresent : 1;
|
uint8_t pfPresent : 1;
|
||||||
uint8_t pfWrite : 1;
|
uint8_t pfWrite : 1;
|
||||||
uint8_t pfUser : 1;
|
uint8_t pfUser : 1;
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ struct ContextMenuItem {
|
|||||||
const char* label;
|
const char* label;
|
||||||
int id;
|
int id;
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
const char* shortcut;
|
||||||
|
bool separator_after;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ContextMenuResult : int {
|
enum ContextMenuResult : int {
|
||||||
@@ -226,6 +228,19 @@ inline void draw_context_menu(Canvas& c,
|
|||||||
Color label_color = items[i].enabled ? theme.text : theme.text_muted;
|
Color label_color = items[i].enabled ? theme.text : theme.text_muted;
|
||||||
context_menu_draw_label(c, menu.x + 12, item.y + (item.h - fh) / 2,
|
context_menu_draw_label(c, menu.x + 12, item.y + (item.h - fh) / 2,
|
||||||
items[i].label, label_color, font, font_size);
|
items[i].label, label_color, font, font_size);
|
||||||
|
if (items[i].shortcut && items[i].shortcut[0]) {
|
||||||
|
int shortcut_w = font && font->valid && font_size > 0
|
||||||
|
? font->measure_text(items[i].shortcut, font_size)
|
||||||
|
: text_width(items[i].shortcut);
|
||||||
|
context_menu_draw_label(c, menu.x + menu.w - 12 - shortcut_w,
|
||||||
|
item.y + (item.h - fh) / 2,
|
||||||
|
items[i].shortcut, theme.text_muted,
|
||||||
|
font, font_size);
|
||||||
|
}
|
||||||
|
if (items[i].separator_after) {
|
||||||
|
c.hline(menu.x + 10, item.y + item.h - 1,
|
||||||
|
menu.w - 20, theme.border);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,6 +212,28 @@ static void render() {
|
|||||||
y += SECTION_GAP;
|
y += SECTION_GAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_report.stackWordCount != 0) {
|
||||||
|
section_header(c, y, w, fh, "Stack Snapshot");
|
||||||
|
for (int i = 0; i < g_report.stackWordCount && i < 8; i++) {
|
||||||
|
char label[24] = "Stack + 0x00";
|
||||||
|
static const char digits[] = "0123456789ABCDEF";
|
||||||
|
unsigned offset = (unsigned)i * 8;
|
||||||
|
label[10] = digits[(offset >> 4) & 0xF];
|
||||||
|
label[11] = digits[offset & 0xF];
|
||||||
|
if (i == 0) {
|
||||||
|
label[12] = ' ';
|
||||||
|
label[13] = '(';
|
||||||
|
label[14] = 'r';
|
||||||
|
label[15] = 'e';
|
||||||
|
label[16] = 't';
|
||||||
|
label[17] = ')';
|
||||||
|
label[18] = '\0';
|
||||||
|
}
|
||||||
|
format_hex16(hexbuf, g_report.stackWords[i]);
|
||||||
|
table_row(c, y, row_h, COL1_W, w, rowIdx++, label, hexbuf, VALUE_COL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g_win.present();
|
g_win.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,6 +270,14 @@ static void render_console() {
|
|||||||
emit_kv_hex("RFLAGS", g_report.flags);
|
emit_kv_hex("RFLAGS", g_report.flags);
|
||||||
if (g_report.exceptionVector == 0x0E)
|
if (g_report.exceptionVector == 0x0E)
|
||||||
emit_kv_hex("CR2 (fault addr)", g_report.faultingAddress);
|
emit_kv_hex("CR2 (fault addr)", g_report.faultingAddress);
|
||||||
|
for (int i = 0; i < g_report.stackWordCount && i < 8; i++) {
|
||||||
|
char key[] = "Stack + 0x00";
|
||||||
|
static const char digits[] = "0123456789ABCDEF";
|
||||||
|
unsigned offset = (unsigned)i * 8;
|
||||||
|
key[10] = digits[(offset >> 4) & 0xF];
|
||||||
|
key[11] = digits[offset & 0xF];
|
||||||
|
emit_kv_hex(key, g_report.stackWords[i]);
|
||||||
|
}
|
||||||
emit("\n");
|
emit("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user