Bool CheckPtr(U8 *ptr) {//Check if address is valid pointer. CZXE *zxe = mem_boot_base - sizeof(CZXE); if (mem_heap_base <= ptr <= mem_mapped_space) return *MemPageTable(ptr) & 1; else return mem_boot_base < ptr < mem_boot_base + zxe->file_size - 1 - sizeof(CZXE); } Bool CheckCodePtr(U8 *ptr) {//Check if address is valid code address. CZXE *zxe = mem_boot_base - sizeof(CZXE); if (mem_heap_base <= ptr <= mem_heap_limit) return *MemPageTable(ptr) & 1; else return mem_boot_base < ptr < mem_boot_base + zxe->file_size - 1 - sizeof(CZXE); } Bool CheckOnStack(U8 *ptr, CTask *task=NULL) {//Check if address is valid stack address. Bool res = FALSE; PUSHFD CLI if (task) { if (&task->stack->stack_base <= ptr <= (&task->stack->stack_base)(U8 *) + task->stack->stack_size) res = TRUE; } else if (mem_heap_base <= ptr <= mem_heap_limit) res = TRUE; POPFD return res; } I64 UnusedStack(CTask *task=NULL) {//Count of usused bytes in task's stack. I64 res; if (!task) task = Fs; PUSHFD CLI if (task == Fs) res = RSPGet()(U8 *) - (&task->stack->stack_base)(U8 *); else res = task->rsp(U8 *) - (&task->stack->stack_base)(U8 *); POPFD return res; } U8 *Caller(I64 num=1) {//Returns the address of the function which called this one, //or the caller of the caller, etc. U8 **rbp = RBPGet, **ptr; while (num--) { if (rbp >= *rbp) return NULL; rbp = *rbp; if (!CheckOnStack(rbp, Fs)) return NULL; } ptr = rbp + 1; return *ptr; } U8 *TaskCaller(CTask *task=NULL, I64 num=0, Bool saved_context=FALSE) {//Fetches address of Nth caller on task's stack. U8 **ptr, **rbp, **rsp; if (!task) task = Fs; if (!saved_context && task == Fs) return Caller(num + 1); if (!TaskValidate(task)) return NULL; rbp = task->rbp; rsp = task->rsp; if (num) { while (CheckOnStack(rbp, task)) { ptr = rbp + 1; if (! --num) return *ptr; if (rbp >= *rbp) break; rbp = *rbp; } return NULL; } else { if (task->rip == _RET) return *rsp; else return task->rip; } } #define STACK_REP_LEN 32 U0 StackRep(CTask *task=NULL) {//Reports whats on the stack. I64 i, j, addr, **rbp, **rsp, *my_rsp[STACK_REP_LEN]; CHashTable *old_hash = Fs->hash_table; if (!task) task = Fs; if (!TaskValidate(task)) return; PUSHFD CLI if (task == Fs) { rbp = RBPGet; rsp = rbp + 3; rbp = *rbp; } else { rsp = task->rsp; rbp = task->rbp; } if (task->rip == _RET) addr = *rsp; else addr = task->rip; MemCopy(my_rsp, rsp, STACK_REP_LEN * sizeof(U8 *)); POPFD Fs->hash_table = task->hash_table; if (!IsRaw) "$LTCYAN$"; "RSP RSP+Offset: Value Value/Link\n"; if (!IsRaw) "$FG$"; for (i = 0; i < STACK_REP_LEN; i++) { "%08X [RSP+%04X]: %016X ", rsp + i, i * sizeof(U8 *), my_rsp[i]; while (TRUE) { if (!(&task->stack->stack_base <= rbp < (&task->stack->stack_base)(U8 *) + task->stack->stack_size)) break; j = rbp - rsp; if (j >= i) break; addr = my_rsp[j + 1]; if (rbp >= my_rsp[j]) break; rbp = my_rsp[j]; } if (my_rsp[i] == addr && !IsRaw) "$BG,YELLOW$"; "%P", my_rsp[i]; if (!IsRaw) "$BG$"; '\n'; } '\n'; Fs->hash_table = old_hash; } U0 CallerRep(U8 **rbp=NULL, CTask *task=NULL) {//Prints a report of calling routines. I64 **ptr; if (!task) task = Fs; if (!rbp) { if (task == Fs) rbp = RBPGet; else rbp = task->rbp; } "CallerRep:\n"; if (!IsRaw) "$LTCYAN$RBP: *RBP: *RBP:$FG$\n"; else "RBP: *RBP: *RBP\n"; while (CheckOnStack(rbp, task)) { ptr = rbp + 1; "%08X: %08tX: %P\n", ptr, *ptr, *ptr; if (rbp >= *rbp) break; rbp = *rbp; } } U0 Dump(U8 *addr, I64 count=0x80, Bool show_offset=TRUE) {//Dump mem, showing offsets. //See DocDump() for a live dump. I64 i, j, ch; U8 *ptr = addr; while (count) { if (show_offset) "%08X ", ptr - addr; else "%010X ", ptr; if (count > 16) j = 16; else j = count; for (i = 0; i < j; i++) "%02X ", ptr[i]; for (; i < 16; i++) " "; for (i = 0; i < j; i++) { ch = ptr[i]; if (ch < CH_SPACE || ch == CH_BACKSPACE) ch = '.'; '' ch; if (ch == '$') '' ch; } '\n'; count -= j; ptr += j; } } U0 DumpMem(U8 *addr, I64 count=0x80) {//Show mem address, not offsets. Dump(addr, count, FALSE); } U0 DumpAddress(U8 **addr, I64 count=0x10) {//Dump mem, showing symbolic addresses. while (count-- > 0) { "%08X:%08X,%P\n", addr, *addr, *addr; addr++; } } U0 RawPrint(I64 mS=100, U8 *format, ...) {//Print using Raw screen output for a length of time. //Your heap must be good. U8 *buf = StrPrintJoin(NULL, format, argc, argv); Bool old_raw, old_input_filter; PUSHFD CLI old_raw = Raw(ON); old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK); "%s", buf; Busy(mS << 10); POPFD LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter); Raw(old_raw); Free(buf); } U0 RawDump(I64 mS=100, U8 *addr, I64 count=0x80) {//Dumps a block of mem using Raw //screen output for a fixed length //of time. Bool old_raw, old_input_filter; PUSHFD CLI old_raw = Raw(ON); old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK); Dump(addr, count); Busy(mS << 10); POPFD LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter); Raw(old_raw); } U0 RawDumpMem(I64 mS=100, U8 *addr, I64 count=0x80) {//Dumps a block of mem using Raw //screen output for a fixed length //of time. Bool old_raw, old_input_filter; PUSHFD CLI old_raw = Raw(ON); old_input_filter = LBtr(&Fs->task_flags, TASKf_INPUT_FILTER_TASK); DumpMem(addr, count); Busy(mS << 10); POPFD LBEqual(&Fs->task_flags, TASKf_INPUT_FILTER_TASK, old_input_filter); Raw(old_raw); } I64 *TaskRegAddr(CTask *task, I64 reg_num) { switch (reg_num) { case REG_RAX: return &task->rax; case REG_RCX: return &task->rcx; case REG_RDX: return &task->rdx; case REG_RBX: return &task->rbx; case REG_RSP: return &task->rsp; case REG_RBP: return &task->rbp; case REG_RSI: return &task->rsi; case REG_RDI: return &task->rdi; case 8 : return &task->r8; case 9 : return &task->r9; case 10: return &task->r10; case 11: return &task->r11; case 12: return &task->r12; case 13: return &task->r13; case 14: return &task->r14; case 15: return &task->r15; } return NULL; } #define RAWDR_COL (text.cols - 40) U0 RawDumpRegs(CTask *task=NULL) { I64 i, j, old_col = text.raw_col; Bool old_raw = Raw(ON); U8 buf[200]; if (!task) task = Fs; text.raw_col = RAWDR_COL; '.'; for (j = 0; j < text.cols - RAWDR_COL - 1; j++) '.'; for (i = 0; i < 16; i++) { text.raw_col = (i + 1) * text.cols + RAWDR_COL; ".%3Z: %016X\n", i, "ST_U64_REGS", *TaskRegAddr(task, i); } i++; text.raw_col = i++ * text.cols + RAWDR_COL; ".RIP: %016X\n", task->rip; text.raw_col = i++ * text.cols + RAWDR_COL; ".%-*tp\n", text.cols - (RAWDR_COL + 1) - 1, Fs->rip; text.raw_col = i++ * text.cols + RAWDR_COL; '.'; if (Bt(&sys_run_level, RLf_COMPILER)) { j = Fs->rip; Ui(buf, &j,,, TRUE); "%s", buf; } else '\n'; text.raw_col = i * text.cols + RAWDR_COL; '.'; for (j = 0; j < text.cols - RAWDR_COL - 1; j++) '.'; text.raw_col = text.cols - 1; '.'; for (j = 2; j < i + 1; j++) { text.raw_col = j * text.cols - 1; '.'; } text.raw_col = j * text.cols - 1; '.'; text.raw_col = old_col; Raw(old_raw); } U0 DumpRegs(CTask *task=NULL) {//Dump registers I64 i; if (!task) task = Fs; for (i = 0; i < 16; i++) "%3Z: %016X\n", i, "ST_U64_REGS", *TaskRegAddr(task, i); "RIP: %016X\n", task->rip; } U8 *SysGetStr2(I64) { U8 buf[512]; StrNGet(buf, 512, FALSE); return StrNew(buf); } CBpt *BptFind(U8 *needle_addr, CTask *haystack_task=NULL, Bool rem=FALSE) { CBpt *res=NULL, *tmpb, *tmpb1, *tmpb2; if (!haystack_task) haystack_task = Fs; PUSHFD CLI tmpb1 = &haystack_task->bpt_list; tmpb = haystack_task->bpt_list; while (tmpb) { tmpb2 = tmpb->next; if (tmpb->addr == needle_addr) { res = tmpb; if (rem) tmpb1->next = tmpb2; else tmpb1 = &tmpb->next; } else tmpb1 = &tmpb->next; tmpb = tmpb2; } POPFD return res; } Bool BptS(U8 *addr, CTask *task=NULL, Bool live=TRUE) {//Set breakpoint. CBpt *tmpb; Bool res = TRUE; if (!task) task = Fs; PUSHFD CLI if (!(tmpb = BptFind(addr, task, FALSE))) { tmpb = CAlloc(sizeof(CBpt), task); tmpb->addr = addr; tmpb->val = *addr; res = FALSE; tmpb->next = task->bpt_list; task->bpt_list = tmpb; if (task == Fs && live) *addr = OC_BPT; } POPFD return res; } Bool BptR(U8 *addr, CTask *task=NULL, Bool live=TRUE, Bool rem=TRUE) {//Remove breakpoint. CBpt *tmpb; Bool res = FALSE; if (!task) task = Fs; PUSHFD CLI if (tmpb = BptFind(addr, task, rem)) { if (task == Fs && live) *tmpb->addr = tmpb->val; res = TRUE; if (rem) Free(tmpb); } POPFD return res; } Bool B(U8 *addr, CTask *task=NULL, Bool live=TRUE) {//Toggle breakpoint. //Return: TRUE if removed. Bool res = FALSE; PUSHFD CLI if (BptFind(addr, task, FALSE)) { BptR(addr, task, live, TRUE); res = TRUE; } else BptS(addr, task, live); POPFD return res; } I64 B2(CTask *task=NULL, Bool live=TRUE) {//Remove all breakpoints. //Return: count of removed. I64 res = 0; CBpt *tmpb, *tmpb1; if (!task) task = Fs; PUSHFD CLI tmpb = task->bpt_list; task->bpt_list = NULL; while (tmpb) { tmpb1 = tmpb->next; if (task == Fs && live) *tmpb->addr = tmpb->val; Free(tmpb); tmpb = tmpb1; res++; } POPFD return res; } U0 G(U8 *ip=INVALID_PTR, CTask *task=NULL) {//Go if (!task) task = Fs; if (ip != INVALID_PTR) task->rip = ip; if (BptFind(task->rip, task)) "\nBreakpoints found.\n" "Do one of the following, first:\n" ">S;\t\t\t//Single step\n" ">B2;\t\t//Clear all break points\n" ">G2;\t\t//Clear all break points and Go\n\n" "After resuming, <CTRL-ALT-n> next focus task\n" "After resuming, <CTRL-ALT-v> flushes screen VGA cache\n"; else { LBtr(&task->task_flags, TASKf_DISABLE_BPTS); LBtr(&task->rflags, RFLAGf_TRAP);//No single step Suspend(task, FALSE); if (task == Fs) { if (IsDebugMode && task->next_cc != &task->next_cc) { "Exit Debug\n"; Btr(&task->last_cc->flags, CCf_PROMPT); } } else Exit; } } U0 G2(U8 *ip=INVALID_PTR, CTask *task=NULL) {//Remove all breakpoints and Go. if (!task) task = Fs; B2(task); if (ext[EXT_WIN_FOCUS]) CallExtNum(EXT_WIN_FOCUS, debug.focus_task); LFBFlush; G(ip, task); } public U0 S(U8 *ip=INVALID_PTR, CTask *task=NULL) //Single-step. {//Single step. if (!task) task = Fs; PUSHFD CLI if (ip != INVALID_PTR) task->rip = ip; LBts(&task->task_flags, TASKf_DISABLE_BPTS); LBts(&task->rflags, RFLAGf_TRAP); Suspend(task, FALSE); if (task == Fs) { if (IsDebugMode) { if (task->next_cc != &task->next_cc) Btr(&task->last_cc->flags, CCf_PROMPT); } } else Exit; POPFD } U0 DebugHelp() { "\n" "The cmd line is basically the same as normal. Here are some common\n" "debugging commands.\n\n" ">EdLite(\"FileName\");\t\t\t\t// Edit file.\n" ">Dump(0x100000);\t\t\t\t\t// Dump page tables.\n" ">DumpMem(0x100000);\t\t\t\t\t// Dump page tables.\n" ">DumpMem(Fs, sizeof(CTask));\t\t// Dump current task record.\n" ">ClassRep(Fs, \"CTask\", 1);\t\t\t// Dump current task record.\n" ">ClassRep(Fs,, 1);\t\t\t\t\t// (It knows lastclass.)\n" ">CallerRep;\t\t\t\t\t\t\t// Stack trace report.\n" ">DumpAddress(_RSP);\t\t\t\t\t// Dump stack.\n" ">DumpRegs;\t\t\t\t\t\t\t// Dump Registers.\n" ">1 + 2 * 3 + &Print;\t\t\t\t// Show calculation res.\n" ">*(0x70000)(I64 *) = 0x123456789;\t// Assign value to 0x70000-0x70007.\n" ">_RAX = 0x1234;\t\t\t\t\t\t// Set RAX to 0x1234.\n" ">_RIP = &Break;\t\t\t\t\t\t// Set RIP.\n" ">I64 i;\t\t\t\t\t\t\t\t// Declare variable.\n" ">i = _RCX + _RDX;\t\t\t\t\t// Assign to variable.\n" ">U(&Print+0x8);\t\t\t\t\t\t// Unassemble Print.\n" ">Uf(\"Print\");\t\t\t\t\t\t// Unassembler function \"Print\".\n" ">Man(\"Print\");\t\t\t\t\t\t// Edit Src for \"Print\".\n" ">E(_RIP);\t\t\t\t\t\t\t// Edit Src Code.\n" ">Fix;\t\t\t\t\t\t\t\t// Edit Last Err Src Code.\n" ">B(&Main+0x20);\t\t\t\t\t\t// Toggle break point.\n\n" ">B2;\t\t\t\t\t\t\t\t// Clear all break points.\n" ">S;\t\t\t\t\t\t\t\t\t// Single step.\n" ">G;\t\t\t\t\t\t\t\t\t// Resume execution.\n" ">G2;\t\t\t\t\t\t\t\t// B2;LFBFlush;WinFocus;G;\n" ">Exit;\t\t\t\t\t\t\t\t// Exit (kill) task.\n\n" "After resuming, <CTRL-ALT-n> next focus task.\n" "After resuming, <CTRL-ALT-v> flushes screen VGA cache.\n\n"; } U0 Debug2() { Bool old_win_inhibit, old_waiting_message, old_single; I64 i, old_getstr2; U8 buf[200]; if (debug.panic) { if (IsRaw) { i = Fs->rip; Ui(buf, &i); "%s", buf; } else U(Fs->rip, 1); } else debug.panic = TRUE; "\n"; old_waiting_message = LBtr(&Fs->task_flags, TASKf_AWAITING_MESSAGE); old_win_inhibit = Fs->win_inhibit; Fs->win_inhibit = WIG_USER_TASK_DEFAULT; sys_focus_task = Fs; kbd.scan_code = 0; old_getstr2 = fp_getstr2; fp_getstr2 = &SysGetStr2; old_single = SingleUser(OFF); while (!mouse_hard.install_attempts) Yield; SingleUser(old_single); UserTaskCont; fp_getstr2 = old_getstr2; Fs->win_inhibit = old_win_inhibit; LBEqual(&Fs->task_flags, TASKf_AWAITING_MESSAGE, old_waiting_message); } U0 Fault3(I64 fault_num, I64 fault_err_code) { no_warn fault_err_code; PUSHFD CLI if (Gs->num && debug.mp_crash) { mp_count = 1; debug.mp_crash->cpu_num = Gs->num; debug.mp_crash->task = Fs; debug.mp_crash->rip = Fs->rip; debug.mp_crash->message = debug.message; debug.mp_crash->message_num = debug.message_num; MPInt(I_MP_CRASH, 0); SysHlt; } "\n\tZealOS Debugger\n\n" " Type Help; for help.\n\n"; Beep(62, TRUE); if (fault_num == I_DEBUG) { if (debug.message) { "\n!!! %s", debug.message; if (debug.message_num) "%016X", debug.message_num; " !!!\n\n"; } } if (debug.panic) CallerRep; Debug2; POPFD } U0 Fault2(I64 fault_num, I64 fault_err_code) {//Called from Fault2. //Be careful not to swap-out and ruin the saved context Bool was_raw, was_single_user, was_silent, was_in_debug, was_mouse_enabled; I64 i, old_raw_flags = text.raw_flags; was_single_user = SingleUser(ON); if (!IsDebugMode) debug.focus_task = sys_focus_task; sys_focus_task = NULL; if (fault_num == I_BPT) Fs->rip--; if (Fs->debug_task) CallExtNum(EXT_DEBUG_RESUME, fault_num, fault_err_code); else { was_mouse_enabled = CallExtStr("MouseHardEnable", FALSE); was_raw = Raw(ON); was_silent = Silent(OFF); text.raw_flags |= RAWF_SHOW_DOLLAR | RAWF_SCROLL; "Task \""; "%s", Fs->task_title; "\"\n\n"; "Fault: 0x%02X %Z\n", fault_num, fault_num, "ST_INT_NAMES"; "Err Code: %08X\n", fault_err_code; was_in_debug = DebugMode(ON); "RIP: %08X", Fs->rip; //Sometimes crashes on %p, so do this first ":%p\nRSP: %08X\n", Fs->rip, Fs->rsp; if (fault_num == I_PAGE_FAULT) { MOV_RAX_CR2 i = RAXGet; "Fault Addr: %08X:%p\n", i, i; } Fault3(fault_num, fault_err_code); DebugMode(was_in_debug); Silent(was_silent); Raw(was_raw); CallExtStr("MouseHardEnable", was_mouse_enabled); text.raw_flags = old_raw_flags; } SingleUser(was_single_user); if (LBtr(&Fs->task_flags, TASKf_KILL_AFTER_DEBUG)) Exit; } U0 Panic(U8 *message=NULL, I64 message_num=0, Bool panic=TRUE) {//Enter the debugger with panic? PUSHFD CLI debug.message = message; debug.message_num = message_num; debug.panic = panic; INT I_DEBUG POPFD } U0 Debug(U8 *message=NULL, I64 message_num=0) {//Enter debugger, no panic. Panic(message, message_num, FALSE); }