///////////////////////////////////////////////////////////////////////////// // // thread.cpp - Extension to select a Singularity thread. // // Copyright Microsoft Corporation. All rights reserved. // #include "singx86.h" struct GDTE { USHORT limit; USHORT base0_15; BYTE base16_23; BYTE access; BYTE granularity; BYTE base24_31; }; static void Usage() { ExtOut("Usage:\n" " !thread {options} thread_addr\n" "Options:\n" " -p show stack prior to interrupt or exception (if any)\n" " -t show top stack (i.e. interrupt or exception (if any))\n" "Notes:\n" " There are potentially three contexts associated with a thread:\n" " the scheduler context, the interrupt context, and the exception\n" " context. The -p flag is only useful if an exception or interrupt\n" " has occurred. Using -p -p always gets the scheduler context."); } static PCSTR ParseArguments(PCSTR args, OUT PLONG plPriorStack, OUT PLONG plTopStack) { *plPriorStack = 0; *plTopStack = 0; while (*args != '\0') { while (*args == ' ' || *args == '\t') { args++; } if (*args != '-' && *args != '/') { break; } args++; switch (*args++) { case 'p': *plPriorStack = *plPriorStack + 1; break; case 't': *plTopStack = 1; break; default: Usage(); return NULL; } } return args; } EXT_DECL(thread) // Defines: PDEBUG_CLIENT Client, PCSTR args { EXT_ENTER(); // Defines: HRESULT status = S_OK; Thread thread; ThreadContext tc; LONG priorStack = 0; LONG topStack = 0; ULONG64 address = 0; if ((args = ParseArguments(args, &priorStack, &topStack)) == NULL) { status = S_FALSE; goto Exit; } status = ExtEvalU64(&args, &address); if (status != S_OK) { //// This code \/ needs to be replicated for ARM. ULONG indexGdtr = 0; ULONG indexGdtl = 0; ULONG indexFs = 0; EXT_CHECK(g_ExtRegisters->GetIndexByName("gdtr", &indexGdtr)); EXT_CHECK(g_ExtRegisters->GetIndexByName("gdtl", &indexGdtl)); if (TargetMachine == IMAGE_FILE_MACHINE_AMD64) { EXT_CHECK(g_ExtRegisters->GetIndexByName("gs", &indexFs)); } else if (TargetMachine == IMAGE_FILE_MACHINE_I386) { EXT_CHECK(g_ExtRegisters->GetIndexByName("fs", &indexFs)); } ExtVerb("gdtr: %04x\n", indexGdtr); ExtVerb("gdtl: %04x\n", indexGdtl); ExtVerb("fs: %04x\n", indexFs); ULONG64 gdtr; ULONG64 gdtl; ULONG64 fs; DEBUG_VALUE v; EXT_CHECK(g_ExtRegisters->GetValue(indexGdtr, &v)); ExtVerb("gdtr: %04x -> %p\n", indexGdtr, v.I64); gdtr = v.I64; EXT_CHECK(g_ExtRegisters->GetValue(indexGdtl, &v)); gdtl = v.I64; ExtVerb("gdtl: %04x -> %p\n", indexGdtl, v.I64); EXT_CHECK(g_ExtRegisters->GetValue(indexFs, &v)); fs = v.I64; ExtVerb("fs: %04x -> %p\n", indexFs, v.I64); GDTE fse; ZeroMemory(&fse, sizeof(fse)); EXT_CHECK(TraceRead(gdtr + fs, &fse, sizeof(fse))); ULONG64 fs0 = ((ULONG64)fse.base0_15 + ((ULONG64)fse.base16_23 << 16) | ((ULONG64)fse.base24_31 << 24)); ExtVerb("fs[0] = %p\n", fs0); // BUG: does not get fs offset from HAL!!! ProcessorContext *ppc; EXT_CHECK(TraceRead(fs0, &ppc, sizeof(ppc))); ProcessorContext pc; EXT_CHECK(ProcessorContextStruct.Read((ULONG64)ppc, &pc)); ExtVerb(" _processor: %p\n", pc._processor); ExtVerb(" exception: %p\n", pc.exception); ExtVerb(" cpuId: %p\n", pc.cpuRecord.id); ExtOut("%d: Int Stack: %p..%p\n", (int)pc.cpuRecord.id, pc.cpuRecord.interruptStackLimit, pc.cpuRecord.interruptStackBegin - 1); ThreadContext *ptc; EXT_CHECK(TraceRead(fs0+sizeof(void*), &ptc, sizeof(ptc))); ThreadContext tc; EXT_CHECK(ProcessorContextStruct.Read((ULONG64)ptc, &tc)); ExtVerb(" threadContext: %p\n", ptc); address = tc._thread; //// This code /\ needs to be replicated for ARM. } if (address == 0) { ExtErr("Null is invalid thread address.\n"); return S_FALSE; } EXT_CHECK(ThreadStruct->Read(address, &thread)); ULONG64 caddress[3]; int currentStack = 0; caddress[0] = address + ThreadFields[0].offset; // Thread.context if (thread.context.next) { caddress[1] = thread.context.next; currentStack++; EXT_CHECK(ThreadContextStruct->Read(caddress[1], &tc)); if (tc.next) { caddress[2] = tc.next; EXT_CHECK(ThreadContextStruct->Read(caddress[2], &tc)); currentStack++; } } if (topStack == 0) { currentStack = 0; } else { if (priorStack != 0) { currentStack -= priorStack; if (currentStack < 0) { currentStack = 0; } } } EXT_CHECK(ThreadContextStruct->Read(caddress[currentStack], &tc)); if (TargetMachine == IMAGE_FILE_MACHINE_I386) { ExtOut(" %p { tid=%03x pid=%03x eip=%p, ebp=%p, esp=%p %02x }\n", address, (ULONG)(tc.threadIndex == 0xffff ? 0xfff : tc.threadIndex), (ULONG)(tc.processId == 0xffff ? 0xfff : tc.processId), tc.threadRecord.spill.ip, tc.threadRecord.spill.bp, tc.threadRecord.spill.sp, (ULONG)tc.gcStates); X86_CONTEXT context; PVOID mmxRegs = NULL; PVOID stRegs = NULL; EXT_CHECK(g_ExtSymbols->GetScope(NULL, NULL, &context, sizeof(context))); EXT_CHECK(ThreadContextStruct->RawAccess(ThreadContextFields[0].offset, &mmxRegs)); EXT_CHECK(ThreadContextStruct->RawAccess(ThreadContextFields[1].offset, &stRegs)); context.Eip = (ULONG)tc.threadRecord.spill.ip; context.Esp = (ULONG)tc.threadRecord.spill.sp; context.Ebp = (ULONG)tc.threadRecord.spill.bp; context.Eax = (ULONG)tc.threadRecord.spill.ax; context.Ebx = (ULONG)tc.threadRecord.spill.bx; context.Ecx = (ULONG)tc.threadRecord.spill.cx; context.Edx = (ULONG)tc.threadRecord.spill.dx; context.Esi = (ULONG)tc.threadRecord.spill.si; context.Edi = (ULONG)tc.threadRecord.spill.di; context.EFlags = (ULONG)tc.threadRecord.spill.fl; // CONTEXT_FLOATING_POINT context.FloatSave.ControlWord = (ULONG)tc.threadRecord.spill.mmx.fcw; context.FloatSave.StatusWord = (ULONG)tc.threadRecord.spill.mmx.fsw; context.FloatSave.TagWord = (ULONG)tc.threadRecord.spill.mmx.ftw; context.FloatSave.ErrorOffset = (ULONG)tc.threadRecord.spill.mmx.ip; context.FloatSave.ErrorSelector = (ULONG)tc.threadRecord.spill.mmx.cs; context.FloatSave.DataOffset = (ULONG)tc.threadRecord.spill.mmx.dp; context.FloatSave.DataSelector = (ULONG)tc.threadRecord.spill.mmx.ds; memcpy((PBYTE)context.FloatSave.RegisterArea+0, (PBYTE)stRegs + 0x00, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+10, (PBYTE)stRegs + 0x10, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+20, (PBYTE)stRegs + 0x20, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+30, (PBYTE)stRegs + 0x30, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+40, (PBYTE)stRegs + 0x40, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+50, (PBYTE)stRegs + 0x50, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+60, (PBYTE)stRegs + 0x60, 10); memcpy((PBYTE)context.FloatSave.RegisterArea+70, (PBYTE)stRegs + 0x70, 10); memcpy(context.ExtendedRegisters, mmxRegs, 512); EXT_CHECK(g_ExtSymbols->SetScope(0, NULL, &context, sizeof(context))); } else if (TargetMachine == IMAGE_FILE_MACHINE_AMD64) { ExtOut(" %p { tid=%03x pid=%03x rip=%p, rbp=%p, rsp=%p %02x }\n", address, (ULONG)(tc.threadIndex == 0xffff ? 0xfff : tc.threadIndex), (ULONG)(tc.processId == 0xffff ? 0xfff : tc.processId), tc.threadRecord.spill.ip, tc.threadRecord.spill.bp, tc.threadRecord.spill.sp, (ULONG)tc.gcStates); X64_CONTEXT context; PVOID mmxRegs = NULL; EXT_CHECK(g_ExtSymbols->GetScope(NULL, NULL, &context, sizeof(context))); EXT_CHECK(ThreadContextStruct->RawAccess(ThreadContextFields[0].offset, &mmxRegs)); // TODO: fix the extension to proper handle different architectures. Copy only // these registers for now context.Rip = tc.threadRecord.spill.ip; context.Rsp = tc.threadRecord.spill.sp; context.Rbp = tc.threadRecord.spill.bp; context.Rax = tc.threadRecord.spill.ax; context.Rbx = tc.threadRecord.spill.bx; context.Rcx = tc.threadRecord.spill.cx; context.Rdx = tc.threadRecord.spill.dx; context.Rsi = tc.threadRecord.spill.si; context.Rdi = tc.threadRecord.spill.di; context.R8 = tc.threadRecord.spill.r8; context.R9 = tc.threadRecord.spill.r9; context.R10 = tc.threadRecord.spill.r10; context.R11 = tc.threadRecord.spill.r11; context.R12 = tc.threadRecord.spill.r12; context.R13 = tc.threadRecord.spill.r13; context.R14 = tc.threadRecord.spill.r14; context.R15 = tc.threadRecord.spill.r15; context.EFlags = (ULONG)tc.threadRecord.spill.fl; // CONTEXT_FLOATING_POINT memcpy(&context.FltSave, mmxRegs, 512); EXT_CHECK(g_ExtSymbols->SetScope(0, NULL, &context, sizeof(context))); } else if (TargetMachine == IMAGE_FILE_MACHINE_ARM) { ExtOut(" %p { tid=%03x pid=%03x pc=%p, sp=%p %02x }\n", address, (ULONG)(tc.threadIndex == 0xffff ? 0xfff : tc.threadIndex), (ULONG)(tc.processId == 0xffff ? 0xfff : tc.processId), tc.threadRecord.spill.pc, tc.threadRecord.spill.sp, (ULONG)tc.gcStates); ARM_CONTEXT context; EXT_CHECK(g_ExtSymbols->GetScope(NULL, NULL, &context, sizeof(context))); // TODO: fix the extension to proper handle different architectures. Copy only // these registers for now context.R0 = (ULONG)tc.threadRecord.spill.r0; context.R1 = (ULONG)tc.threadRecord.spill.r1; context.R2 = (ULONG)tc.threadRecord.spill.r2; context.R3 = (ULONG)tc.threadRecord.spill.r3; context.R4 = (ULONG)tc.threadRecord.spill.r4; context.R5 = (ULONG)tc.threadRecord.spill.r5; context.R6 = (ULONG)tc.threadRecord.spill.r6; context.R7 = (ULONG)tc.threadRecord.spill.r7; context.R8 = (ULONG)tc.threadRecord.spill.r8; context.R9 = (ULONG)tc.threadRecord.spill.r9; context.R10 = (ULONG)tc.threadRecord.spill.r10; context.R11 = (ULONG)tc.threadRecord.spill.r11; context.R12 = (ULONG)tc.threadRecord.spill.r12; context.Pc = (ULONG)tc.threadRecord.spill.pc; context.Lr = (ULONG)tc.threadRecord.spill.lr; context.Sp = (ULONG)tc.threadRecord.spill.sp; context.Cpsr = (ULONG)tc.threadRecord.spill.cpsr; EXT_CHECK(g_ExtSymbols->SetScope(0, NULL, &context, sizeof(context))); } EXT_LEAVE(); // Macro includes: return status; }