From bbe357cf312d718699d2a0f8a4168639cc35b2a6 Mon Sep 17 00:00:00 2001 From: "SND\\EreTIk_cp" Date: Wed, 25 Jan 2012 11:57:47 +0000 Subject: [PATCH] [0.1.x] + fork new thread context by stack frame + test of local variables of previous stack frame git-svn-id: https://pykd.svn.codeplex.com/svn@73627 9b283d60-5439-405e-af05-b73fd8c4d996 --- pykd/context.cpp | 54 +++++++++++++++++++++++++++++++++--- pykd/context.h | 15 ++++++++-- pykd/dbgext.cpp | 6 +++- test/scripts/localstest.py | 27 ++++++++++++------ test/targetapp/targetapp.cpp | 11 ++++++++ 5 files changed, 98 insertions(+), 15 deletions(-) diff --git a/pykd/context.cpp b/pykd/context.cpp index c39550a..1d3dca9 100644 --- a/pykd/context.cpp +++ b/pykd/context.cpp @@ -129,10 +129,7 @@ ThreadContext::ThreadContext( IDebugClient4 *client ) : return; } - std::stringstream sstream; - sstream << __FUNCTION__ << ":\n"; - sstream << "Unsupported processor type: 0x" << std::hex << m_processorType; - throw DbgException( sstream.str() ); + throwUnsupportedProcessor(__FUNCTION__); } ///////////////////////////////////////////////////////////////////////////////// @@ -191,6 +188,29 @@ ULONG64 ThreadContext::getSp() const ///////////////////////////////////////////////////////////////////////////////// +ContextPtr ThreadContext::forkByStackFrame(DEBUG_STACK_FRAME &frame) const +{ + ContextPtr newContext( new ThreadContext(*this) ); + switch (m_processorType) + { + case IMAGE_FILE_MACHINE_I386: + newContext->m_regValues[CV_REG_EIP] = frame.InstructionOffset; + newContext->m_regValues[CV_REG_EBP] = frame.FrameOffset; + newContext->m_regValues[CV_REG_ESP] = frame.StackOffset; + return newContext; + + case IMAGE_FILE_MACHINE_AMD64: + newContext->m_regValues[CV_AMD64_RIP] = frame.InstructionOffset; + newContext->m_regValues[CV_AMD64_RBP] = frame.FrameOffset; + newContext->m_regValues[CV_AMD64_RSP] = frame.StackOffset; + return newContext; + } + + throwUnsupportedProcessor(__FUNCTION__); +} + +///////////////////////////////////////////////////////////////////////////////// + python::object ThreadContext::getByIndex(ULONG ind) const { RegValues::const_iterator it = m_regValues.begin(); @@ -345,4 +365,30 @@ bool ThreadContext::getSubValue(ULONG cvRegId, ULONG64 &val) const ///////////////////////////////////////////////////////////////////////////////// +void ThreadContext::throwUnsupportedProcessor(PCSTR szFunction) const +{ + std::stringstream sstream; + sstream << szFunction << ":\n"; + sstream << "Unsupported processor type: 0x" << std::hex << m_processorType; + throw DbgException( sstream.str() ); +} + +///////////////////////////////////////////////////////////////////////////////// + +std::string printStackFrame(DEBUG_STACK_FRAME &frame) +{ + std::stringstream sstream; + + sstream << std::dec << "(" << frame.FrameNumber << ")"; + + sstream << " ip= 0x" << std::hex << frame.InstructionOffset; + sstream << ", ret= 0x" << std::hex << frame.ReturnOffset; + sstream << ", frame= 0x" << std::hex << frame.FrameOffset; + sstream << ", stack= 0x" << std::hex << frame.StackOffset; + + return sstream.str(); +} + +///////////////////////////////////////////////////////////////////////////////// + } diff --git a/pykd/context.h b/pykd/context.h index 32e7c32..327157f 100644 --- a/pykd/context.h +++ b/pykd/context.h @@ -10,6 +10,14 @@ namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +class ThreadContext; +typedef boost::shared_ptr< ThreadContext > ContextPtr; + +//////////////////////////////////////////////////////////////////////////////// + std::string processorToStr(ULONG processorMode); //////////////////////////////////////////////////////////////////////////////// @@ -17,7 +25,6 @@ std::string processorToStr(ULONG processorMode); class ThreadContext : private DbgObject { public: - ThreadContext( IDebugClient4 *client ); // get register value by ID @@ -45,6 +52,8 @@ public: return pykd::processorToStr(m_processorType); } + ContextPtr forkByStackFrame(DEBUG_STACK_FRAME &frame) const; + private: // query i386 registers @@ -56,6 +65,8 @@ private: // try query as "sub-register" bool getSubValue(ULONG cvRegId, ULONG64 &val) const; + void __declspec(noreturn) throwUnsupportedProcessor(PCSTR szFunction) const; + private: typedef std::map RegValues; RegValues m_regValues; @@ -65,7 +76,7 @@ private: //////////////////////////////////////////////////////////////////////////////// -typedef boost::shared_ptr< ThreadContext > ContextPtr; +std::string printStackFrame(DEBUG_STACK_FRAME &frame); //////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index 040c582..4cb3a14 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -632,7 +632,9 @@ BOOST_PYTHON_MODULE( pykd ) .def_readonly( "stackOffset", &DEBUG_STACK_FRAME::StackOffset, "Return a frame's stack offset" ) .def_readonly( "frameNumber", &DEBUG_STACK_FRAME::FrameNumber, - "Return a frame's number" ); + "Return a frame's number" ) + .def( "__str__", &printStackFrame, + "Return stacks frame as string"); python::class_( "Context", "Context of thread (register values)", python::no_init ) @@ -646,6 +648,8 @@ BOOST_PYTHON_MODULE( pykd ) "Get register value by ID (CV_REG_XXX)" ) .def( "processorType", &ThreadContext::getProcessorType, "Get processor ThreadContext as string") + .def( "fork", &ThreadContext::forkByStackFrame, + "Create new thread context by stackFrame") .def("__len__", &ThreadContext::getCount, "Return count of registers") .def("__getitem__", &ThreadContext::getByIndex, diff --git a/test/scripts/localstest.py b/test/scripts/localstest.py index 4d9e3df..5a2ad6a 100644 --- a/test/scripts/localstest.py +++ b/test/scripts/localstest.py @@ -8,14 +8,14 @@ class LocalVarsTest(unittest.TestCase): def testLocalVariable(self): """Start new process and test local variables""" - testClient = pykd.createDbgClient() - testClient.startProcess( target.appPath + " -testEnumWindows" ) + newClnt = pykd.createDbgClient() + newClnt.startProcess( target.appPath + " -testEnumWindows" ) - testClient.go() # initial breakpoint -> wmain - testClient.go() # wmain -> targetapp!EnumWindowsProc1 - # pykd.dprint( "\n" + testClient.dbgCommand("u") ) + newClnt.go() # initial breakpoint -> wmain + newClnt.go() # wmain -> targetapp!EnumWindowsProc1 + # pykd.dprint( "\n" + newClnt.dbgCommand("u") ) - locals = testClient.getLocals() + locals = newClnt.getLocals() self.assertNotEqual( 0, locals["hWindow"] ) self.assertEqual( pykd.DataIsParam, locals["hWindow"].dataKind() ) @@ -31,9 +31,20 @@ class LocalVarsTest(unittest.TestCase): self.assertEqual( locals["dwProccessId"] + 1, locals["staticVar"] ) - testClient.go() # targetapp!EnumWindowsProc1 -> targetapp!EnumWindowsProc2 - locals = testClient.getLocals() + newClnt.go() # targetapp!EnumWindowsProc1 -> targetapp!functionCalledFromEnumWindowsProc1 + + # get local variables from previous stack frame + prevStackThreadCtx = newClnt.getContext().fork( newClnt.getCurrentStack()[1] ) + prevLocals = newClnt.getLocals( prevStackThreadCtx ) + + self.assertEqual( len(prevLocals), len(locals) ) + for varName in locals.iterkeys(): + self.assertEqual( prevLocals[varName], locals[varName] ) + + newClnt.go() # targetapp!EnumWindowsProc1 -> targetapp!EnumWindowsProc2 + locals = newClnt.getLocals() self.assertEqual( len(locals), 2 ) locValues = locals.values() self.assertTrue( locValues[0] == 7 or locValues[1] == 7 ) + diff --git a/test/targetapp/targetapp.cpp b/test/targetapp/targetapp.cpp index dac3f17..0057b6a 100644 --- a/test/targetapp/targetapp.cpp +++ b/test/targetapp/targetapp.cpp @@ -303,6 +303,16 @@ void FuncWithName1(int a) //////////////////////////////////////////////////////////////////////////////// #pragma optimize("g", off) +VOID functionCalledFromEnumWindowsProc1(DWORD dwProcessId) +{ + DWORD dwCurrentProcessId = GetCurrentProcessId(); + if (dwCurrentProcessId != dwProcessId) + std::cout << dwCurrentProcessId << dwProcessId; + __debugbreak(); +} + +//////////////////////////////////////////////////////////////////////////////// + BOOL CALLBACK EnumWindowsProc1( HWND hWindow, const LPARAM lParam @@ -318,6 +328,7 @@ BOOL CALLBACK EnumWindowsProc1( DWORD dwThreadId = ::GetWindowThreadProcessId(hWindow, &dwProccessId); staticVar = dwProccessId + 1; __debugbreak(); + functionCalledFromEnumWindowsProc1(dwProccessId); std::cout << dwProccessId << dwThreadId << staticVar; } return hWindow ? FALSE : TRUE;