diff --git a/pykd/context.cpp b/pykd/context.cpp new file mode 100644 index 0000000..120ca46 --- /dev/null +++ b/pykd/context.cpp @@ -0,0 +1,214 @@ + +#include "stdafx.h" + +#include + +#include "context.h" + +///////////////////////////////////////////////////////////////////////////////// + +namespace Ctx { + +///////////////////////////////////////////////////////////////////////////////// + +namespace I386 { +#include "defctxi386.h" +} + +///////////////////////////////////////////////////////////////////////////////// + +void Registers::getI386Context( + IDebugAdvanced2 *advanced +) +{ + I386::CONTEXT Context = {0}; + + HRESULT hres = advanced->GetThreadContext(&Context, sizeof(Context)); + if (S_OK != hres) + throw Exception( pykd::buildExceptDesc("IDebugAdvanced2::GetThreadContext", hres) ); + + m_regValues[CV_REG_DR0] = Context.Dr0; + m_regValues[CV_REG_DR1] = Context.Dr1; + m_regValues[CV_REG_DR2] = Context.Dr2; + m_regValues[CV_REG_DR3] = Context.Dr3; + m_regValues[CV_REG_DR6] = Context.Dr6; + m_regValues[CV_REG_DR7] = Context.Dr7; + + m_regValues[CV_REG_GS] = Context.SegGs; + m_regValues[CV_REG_FS] = Context.SegFs; + m_regValues[CV_REG_ES] = Context.SegEs; + m_regValues[CV_REG_DS] = Context.SegDs; + + m_regValues[CV_REG_EDI] = Context.Edi; + m_regValues[CV_REG_ESI] = Context.Esi; + m_regValues[CV_REG_EBX] = Context.Ebx; + m_regValues[CV_REG_EDX] = Context.Edx; + m_regValues[CV_REG_ECX] = Context.Ecx; + m_regValues[CV_REG_EAX] = Context.Eax; + + m_regValues[CV_REG_EBP] = Context.Ebp; + + m_regValues[CV_REG_ESP] = Context.Esp; + m_regValues[CV_REG_SS] = Context.SegSs; + + m_regValues[CV_REG_EIP] = Context.Eip; + m_regValues[CV_REG_CS] = Context.SegCs; + + m_regValues[CV_REG_EFLAGS] = Context.EFlags; +} + +///////////////////////////////////////////////////////////////////////////////// + +namespace AMD64 { +#include "defctxamd64.h" +} + +///////////////////////////////////////////////////////////////////////////////// + +void Registers::getAmd64Context( + IDebugAdvanced2 *advanced +) +{ + AMD64::CONTEXT Context = {0}; + + HRESULT hres = advanced->GetThreadContext(&Context, sizeof(Context)); + if (S_OK != hres) + throw Exception( pykd::buildExceptDesc("IDebugAdvanced2::GetThreadContext", hres) ); + + m_regValues[CV_AMD64_MXCSR] = Context.MxCsr; + + m_regValues[CV_AMD64_CS] = Context.SegCs; + m_regValues[CV_AMD64_DS] = Context.SegDs; + m_regValues[CV_AMD64_ES] = Context.SegEs; + m_regValues[CV_AMD64_FS] = Context.SegFs; + m_regValues[CV_AMD64_GS] = Context.SegGs; + m_regValues[CV_AMD64_SS] = Context.SegSs; + + m_regValues[CV_AMD64_EFLAGS] = Context.EFlags; + + m_regValues[CV_AMD64_DR0] = Context.Dr0; + m_regValues[CV_AMD64_DR1] = Context.Dr1; + m_regValues[CV_AMD64_DR2] = Context.Dr2; + m_regValues[CV_AMD64_DR3] = Context.Dr3; + m_regValues[CV_AMD64_DR6] = Context.Dr6; + m_regValues[CV_AMD64_DR7] = Context.Dr7; + + m_regValues[CV_AMD64_RAX] = Context.Rax; + m_regValues[CV_AMD64_RCX] = Context.Rcx; + m_regValues[CV_AMD64_RDX] = Context.Rdx; + m_regValues[CV_AMD64_RBX] = Context.Rbx; + m_regValues[CV_AMD64_RSP] = Context.Rsp; + m_regValues[CV_AMD64_RBP] = Context.Rbp; + m_regValues[CV_AMD64_RSI] = Context.Rdi; + m_regValues[CV_AMD64_RDI] = Context.Rdi; + m_regValues[CV_AMD64_R8] = Context.R8; + m_regValues[CV_AMD64_R9] = Context.R9; + m_regValues[CV_AMD64_R10] = Context.R10; + m_regValues[CV_AMD64_R11] = Context.R11; + m_regValues[CV_AMD64_R12] = Context.R12; + m_regValues[CV_AMD64_R13] = Context.R13; + m_regValues[CV_AMD64_R14] = Context.R14; + m_regValues[CV_AMD64_R15] = Context.R15; + + m_regValues[CV_AMD64_RIP] = Context.Rip; +} + +///////////////////////////////////////////////////////////////////////////////// + +Registers::Registers( + IDebugControl4 *control, + IDebugAdvanced2 *advanced +) +{ + HRESULT hres = control->GetExecutingProcessorType(&m_processorType); + if (S_OK != hres) + throw Exception( pykd::buildExceptDesc("IDebugControl::GetExecutingProcessorType", hres) ); + + switch (m_processorType) + { + case IMAGE_FILE_MACHINE_I386: + getI386Context(advanced); + return; + + case IMAGE_FILE_MACHINE_AMD64: + getAmd64Context(advanced); + return; + } + + std::stringstream sstream; + sstream << __FUNCTION__ << ":\n"; + sstream << "Unsupported processor type: 0x" << std::hex << m_processorType; + throw Exception( sstream.str() ); +} + +///////////////////////////////////////////////////////////////////////////////// + +ULONG64 Registers::getValue(ULONG cvRegId) const +{ + RegValues::const_iterator it = m_regValues.find(cvRegId); + if (it == m_regValues.end()) + throw Exception(__FUNCTION__ ": Register missing"); + return it->second; +} + +///////////////////////////////////////////////////////////////////////////////// + +bool Registers::getValueNoThrow(ULONG cvRegId, ULONG64 &val) const +{ + RegValues::const_iterator it = m_regValues.find(cvRegId); + if (it == m_regValues.end()) + return false; + + val = it->second; + return true; +} + +///////////////////////////////////////////////////////////////////////////////// + +ULONG64 Registers::getIp() const +{ + return getValue( + IMAGE_FILE_MACHINE_I386 == m_processorType ? CV_REG_EIP : CV_AMD64_RIP + ); +} + +///////////////////////////////////////////////////////////////////////////////// + +ULONG64 Registers::getRetReg() const +{ + return getValue( + IMAGE_FILE_MACHINE_I386 == m_processorType ? CV_REG_EAX : CV_AMD64_RAX + ); +} + +///////////////////////////////////////////////////////////////////////////////// + +ULONG64 Registers::getSp() const +{ + return getValue( + IMAGE_FILE_MACHINE_I386 == m_processorType ? CV_REG_ESP : CV_AMD64_RSP + ); +} + +///////////////////////////////////////////////////////////////////////////////// + +python::object Registers::getByIndex(ULONG ind) const +{ + RegValues::const_iterator it = m_regValues.begin(); + for (ULONG i = 0; it != m_regValues.end(); ++i, ++it ) + { + if (i == ind) + return python::make_tuple(it->first, it->second); + } + + PyErr_SetString(PyExc_IndexError, "Index out of range"); + python::throw_error_already_set(); + + return python::object(); +} + +///////////////////////////////////////////////////////////////////////////////// + +} + +///////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/context.h b/pykd/context.h new file mode 100644 index 0000000..f9f9269 --- /dev/null +++ b/pykd/context.h @@ -0,0 +1,71 @@ + +#include +#include +#include + +#include "dbgexcept.h" + +#pragma once + +namespace Ctx { + +//////////////////////////////////////////////////////////////////////////////// + +typedef pykd::DbgException Exception; + +//////////////////////////////////////////////////////////////////////////////// + +class Registers +{ +public: + Registers( + IDebugControl4 *control, + IDebugAdvanced2 *advanced + ); + + // get register value by ID + ULONG64 getValue(ULONG cvRegId) const; + bool getValueNoThrow(ULONG cvRegId, ULONG64 &val) const; + + // get @$ip pseudo register + ULONG64 getIp() const; + + // get @$retreg pseudo register + ULONG64 getRetReg() const; + + // get @$csp pseudo register + ULONG64 getSp() const; + + // enumerate register values: tuple + ULONG getCount() const { + return static_cast( m_regValues.size() ); + } + python::object getByIndex(ULONG ind) const; + +private: + + // query i386 registers + void getI386Context( + IDebugAdvanced2 *advanced + ); + + // query AMD64 registers + void getAmd64Context( + IDebugAdvanced2 *advanced + ); + +private: + typedef std::map RegValues; + RegValues m_regValues; + + ULONG m_processorType; +}; + +//////////////////////////////////////////////////////////////////////////////// + +typedef boost::shared_ptr< Registers > ContextPtr; + +//////////////////////////////////////////////////////////////////////////////// + +} + diff --git a/pykd/dbgclient.h b/pykd/dbgclient.h index bc1ecc7..ab034ed 100644 --- a/pykd/dbgclient.h +++ b/pykd/dbgclient.h @@ -16,6 +16,7 @@ #include "cpureg.h" #include "inteventhandler.h" #include "synsymbol.h" +#include "context.h" ///////////////////////////////////////////////////////////////////////////////// @@ -233,6 +234,12 @@ public: return getDbgControl(GetPageSize); } + Ctx::ContextPtr getThreadContext() { + return Ctx::ContextPtr( + new Ctx::Registers(m_control, m_advanced) + ); + } + public: CComPtr& @@ -387,6 +394,10 @@ inline ULONG getPageSize() { return g_dbgClient->getPageSize(); } +inline Ctx::ContextPtr getThreadContext() { + return g_dbgClient->getThreadContext(); +} + ///////////////////////////////////////////////////////////////////////////////// template diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index efb3ad6..4348b9c 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -283,6 +283,8 @@ BOOST_PYTHON_MODULE( pykd ) "Get the number of actual processors in the machine" ) .def( "getPageSize", &DebugClient::getPageSize, "Get the page size for the currently executing processor context" ) + .def( "getContext", &DebugClient::getThreadContext, + "Get context of current thread (register values)" ) .def( "addSynSymbol", &DebugClient::addSyntheticSymbol, "Add new synthetic symbol for virtual address" ) .def( "delAllSynSymbols", &DebugClient::delAllSyntheticSymbols, @@ -454,6 +456,8 @@ BOOST_PYTHON_MODULE( pykd ) "Get the number of actual processors in the machine" ); python::def( "getPageSize", &getPageSize, "Get the page size for the currently executing processor context" ); + python::def( "getContext", &getThreadContext, + "Get context of current thread (register values)" ); python::class_, boost::noncopyable >("typeInfo", "Class representing typeInfo", python::no_init ) .def( "name", &TypeInfo::getName ) @@ -585,7 +589,22 @@ BOOST_PYTHON_MODULE( pykd ) "Return a frame's stack offset" ) .def_readonly( "frameNumber", &DEBUG_STACK_FRAME::FrameNumber, "Return a frame's number" ); - + + python::class_( + "Context", "Context of thread (register values)", python::no_init ) + .def( "ip", &Ctx::Registers::getIp, + "Get instruction pointer register" ) + .def( "retreg", &Ctx::Registers::getIp, + "Get primary return value register" ) + .def( "csp", &Ctx::Registers::getSp, + "Get current stack pointer" ) + .def( "get", &Ctx::Registers::getValue, + "Get register value by ID (CV_REG_XXX)" ) + .def("__len__", &Ctx::Registers::getCount, + "Return count of registers") + .def("__getitem__", &Ctx::Registers::getByIndex, + "Return tuple by index"); + python::def( "diaLoadPdb", &pyDia::GlobalScope::loadPdb, "Open pdb file for quering debug symbols. Return DiaSymbol of global scope"); diff --git a/pykd/defctxamd64.h b/pykd/defctxamd64.h new file mode 100644 index 0000000..fa4404c --- /dev/null +++ b/pykd/defctxamd64.h @@ -0,0 +1,95 @@ + +struct DECLSPEC_ALIGN(16) M128A { + ULONGLONG Low; + LONGLONG High; +}; + +struct XMM_SAVE_AREA32 { + USHORT ControlWord; + USHORT StatusWord; + UCHAR TagWord; + UCHAR Reserved1; + USHORT ErrorOpcode; + ULONG ErrorOffset; + USHORT ErrorSelector; + USHORT Reserved2; + ULONG DataOffset; + USHORT DataSelector; + USHORT Reserved3; + ULONG MxCsr; + ULONG MxCsr_Mask; + M128A FloatRegisters[8]; + M128A XmmRegisters[16]; + UCHAR Reserved4[96]; +}; + +struct DECLSPEC_ALIGN(16) CONTEXT { + ULONG64 P1Home; + ULONG64 P2Home; + ULONG64 P3Home; + ULONG64 P4Home; + ULONG64 P5Home; + ULONG64 P6Home; + ULONG ContextFlags; + ULONG MxCsr; + USHORT SegCs; + USHORT SegDs; + USHORT SegEs; + USHORT SegFs; + USHORT SegGs; + USHORT SegSs; + ULONG EFlags; + ULONG64 Dr0; + ULONG64 Dr1; + ULONG64 Dr2; + ULONG64 Dr3; + ULONG64 Dr6; + ULONG64 Dr7; + ULONG64 Rax; + ULONG64 Rcx; + ULONG64 Rdx; + ULONG64 Rbx; + ULONG64 Rsp; + ULONG64 Rbp; + ULONG64 Rsi; + ULONG64 Rdi; + ULONG64 R8; + ULONG64 R9; + ULONG64 R10; + ULONG64 R11; + ULONG64 R12; + ULONG64 R13; + ULONG64 R14; + ULONG64 R15; + ULONG64 Rip; + union { + XMM_SAVE_AREA32 FltSave; + struct { + M128A Header[2]; + M128A Legacy[8]; + M128A Xmm0; + M128A Xmm1; + M128A Xmm2; + M128A Xmm3; + M128A Xmm4; + M128A Xmm5; + M128A Xmm6; + M128A Xmm7; + M128A Xmm8; + M128A Xmm9; + M128A Xmm10; + M128A Xmm11; + M128A Xmm12; + M128A Xmm13; + M128A Xmm14; + M128A Xmm15; + }; + }; + M128A VectorRegister[26]; + ULONG64 VectorControl; + ULONG64 DebugControl; + ULONG64 LastBranchToRip; + ULONG64 LastBranchFromRip; + ULONG64 LastExceptionToRip; + ULONG64 LastExceptionFromRip; +}; diff --git a/pykd/defctxi386.h b/pykd/defctxi386.h new file mode 100644 index 0000000..a25e403 --- /dev/null +++ b/pykd/defctxi386.h @@ -0,0 +1,54 @@ + + +#define MAXIMUM_SUPPORTED_EXTENSION 512 + +#define SIZE_OF_80387_REGISTERS 80 + +struct FLOATING_SAVE_AREA { + ULONG ControlWord; + ULONG StatusWord; + ULONG TagWord; + ULONG ErrorOffset; + ULONG ErrorSelector; + ULONG DataOffset; + ULONG DataSelector; + UCHAR RegisterArea[SIZE_OF_80387_REGISTERS]; + ULONG Cr0NpxState; +}; + +struct CONTEXT { + + ULONG ContextFlags; + + ULONG Dr0; + ULONG Dr1; + ULONG Dr2; + ULONG Dr3; + ULONG Dr6; + ULONG Dr7; + + + FLOATING_SAVE_AREA FloatSave; + + + ULONG SegGs; + ULONG SegFs; + ULONG SegEs; + ULONG SegDs; + + ULONG Edi; + ULONG Esi; + ULONG Ebx; + ULONG Edx; + ULONG Ecx; + ULONG Eax; + + ULONG Ebp; + ULONG Eip; + ULONG SegCs; + ULONG EFlags; + ULONG Esp; + ULONG SegSs; + + UCHAR ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; +}; diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj index 2b15ed8..4f8e900 100644 --- a/pykd/pykd_2008.vcproj +++ b/pykd/pykd_2008.vcproj @@ -1,7 +1,7 @@ + + @@ -475,6 +479,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + @@ -511,6 +519,14 @@ RelativePath=".\dbgpath.h" > + + + + diff --git a/test/scripts/clienttest.py b/test/scripts/clienttest.py index e2341a7..a004fc9 100644 --- a/test/scripts/clienttest.py +++ b/test/scripts/clienttest.py @@ -21,6 +21,20 @@ class DbgClientTest( unittest.TestCase ): """Size of memory page must be >= 4kb""" self.assertTrue( pykd.getPageSize() >= 4*1024 ) + def testCurrentThreadContext( self ): + """Some check of current thread context content""" + ctx = pykd.getContext() +# for reg in ctx: +# regName = "" +# try: +# regName = pykd.diaI386Regs[ reg[0] ] +# except KeyError: +# regName = pykd.diaAmd64Regs[ reg[0] ] +# pykd.dprint( "\n" + regName + ": 0x%x " % reg[1]) + self.assertNotEqual( 0, len(ctx) ) + self.assertNotEqual( 0, ctx.ip() ) + self.assertNotEqual( 0, ctx.csp() ) + def testIsDumpAnalyzing( self ): self.assertFalse( pykd.isDumpAnalyzing() ) @@ -29,7 +43,7 @@ class DbgClientTest( unittest.TestCase ): pykd.setExecutionStatus( pykd.DEBUG_STATUS_GO ) pykd.waitForEvent() self.assertEqual( pykd.DEBUG_STATUS_BREAK, pykd.getExecutionStatus() ) - + def testPdbFile( self ): self.assertNotEqual( '', pykd.getPdbFile( target.module.begin() ) ) @@ -39,6 +53,6 @@ class DbgClientTest( unittest.TestCase ): def testThreadList( self ): self.assertNotEqual( 0, len(pykd.getThreadList()) ) - + def testSymbolsPath( self ): self.assertNotEqual( '', pykd.symbolsPath() ) diff --git a/test/targetapp/targetapp.cpp b/test/targetapp/targetapp.cpp index 772b002..86ee12c 100644 --- a/test/targetapp/targetapp.cpp +++ b/test/targetapp/targetapp.cpp @@ -7,6 +7,8 @@ #include "utils.h" +//////////////////////////////////////////////////////////////////////////////// + #pragma pack( push, 4 ) const ULONG g_constNumValue = 0x5555; @@ -160,6 +162,10 @@ listStruct1 g_listItem11 = { 100 }; listStruct1 g_listItem12 = { 200 }; listStruct1 g_listItem13 = { 300 }; +#pragma pack( pop ) + +//////////////////////////////////////////////////////////////////////////////// + #define InitializeListHead(ListHead) (\ (ListHead)->Flink = (ListHead)->Blink = (ListHead)) @@ -174,6 +180,8 @@ listStruct1 g_listItem13 = { 300 }; _EX_ListHead->Blink = (Entry);\ } +//////////////////////////////////////////////////////////////////////////////// + void FuncWithName0() { classChild _classChild; @@ -212,6 +220,8 @@ void FuncWithName0() std::cout << g_classChild.m_enumField; } +//////////////////////////////////////////////////////////////////////////////// + void FuncWithName1(int a) { unionTest _unionTest[2] = {0}; @@ -227,49 +237,23 @@ void FuncWithName1(int a) std::cout << g_string; } -void FuncWithVolatileArg(volatile long *arg1) -{ - InterlockedIncrement(arg1); -} +//////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK EnumWindowsProc( HWND hWindow, LPARAM lParam ) { + DWORD dwProccessId = 0; if (hWindow) std::cout << lParam; - - switch(lParam) { - case 1: - std::cout << "case 1"; - break; - - case 2: - std::cout << "case 2"; - break; - - case 3: - std::cout << "case 2"; - break; - - default: - { - DWORD dwProccessId = 0; - DWORD dwThreadId = ::GetWindowThreadProcessId(hWindow, &dwProccessId); - std::cout << dwProccessId << dwThreadId; - classWithDestructor classInstance(dwProccessId); - std::cout << GetWindowLong(hWindow, GWL_STYLE); - } - + DWORD dwThreadId = ::GetWindowThreadProcessId(hWindow, &dwProccessId); + std::cout << dwProccessId << dwThreadId; } - return FALSE; } -#pragma pack( pop ) - //////////////////////////////////////////////////////////////////////////////// int doLoadUnload() @@ -328,3 +312,4 @@ int _tmain(int argc, _TCHAR* argv[]) return 0; } +////////////////////////////////////////////////////////////////////////////////