From b89ffed9ebb177377372629b5034a69d689abc66 Mon Sep 17 00:00:00 2001 From: "SND\\kernelnet_cp" Date: Sat, 6 Dec 2014 13:42:51 +0000 Subject: [PATCH] [0.3.x] added : getNumberBreakpoints routine ( return number of breakpoints in the current process ) [0.3.x] added : getBp routine ( return breakpoint object by index ) git-svn-id: https://pykd.svn.codeplex.com/svn@89401 9b283d60-5439-405e-af05-b73fd8c4d996 --- pykd/pyeventhandler.cpp | 229 +++++++++++++++---------------------- pykd/pyeventhandler.h | 124 ++++++++++++++++++-- pykd/pykdver.h | 2 +- pykd/pymod.cpp | 36 +++--- test/scripts/breakpoint.py | 46 ++++++-- 5 files changed, 266 insertions(+), 171 deletions(-) diff --git a/pykd/pyeventhandler.cpp b/pykd/pyeventhandler.cpp index 99cb0a9..b5175b0 100644 --- a/pykd/pyeventhandler.cpp +++ b/pykd/pyeventhandler.cpp @@ -240,165 +240,69 @@ kdlib::DebugCallbackResult EventHandler::onModuleUnload( kdlib::MEMOFFSET_64 of return result; } -/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// -class CallbackBreakpoint : public kdlib::BaseBreakpoint +void EventHandler::onCurrentThreadChange(kdlib::THREAD_DEBUG_ID threadid) { - -public: - - static kdlib::BREAKPOINT_ID setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback); - static kdlib::BREAKPOINT_ID setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback ); - static void removeBreakpoint( kdlib::BREAKPOINT_ID id ); - -private: - - virtual kdlib::DebugCallbackResult onHit(); - - python::object m_callback; - PyThreadState* m_pystate; - - typedef std::map BreakpointMap; - static boost::recursive_mutex m_breakpointLock; - static BreakpointMap m_breakpointMap; -}; - -CallbackBreakpoint::BreakpointMap CallbackBreakpoint::m_breakpointMap; -boost::recursive_mutex CallbackBreakpoint::m_breakpointLock; - -/////////////////////////////////////////////////////////////////////////////// - -kdlib::BREAKPOINT_ID CallbackBreakpoint::setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback) -{ - boost::recursive_mutex::scoped_lock l(m_breakpointLock); - - CallbackBreakpoint *bp = new CallbackBreakpoint(); - bp->m_pystate = PyThreadState_Get(); - bp->m_callback = callback; - bp->set(offset); - - m_breakpointMap[bp->m_id] = bp; - - return bp->m_id; -} - -/////////////////////////////////////////////////////////////////////////////// - -kdlib::BREAKPOINT_ID CallbackBreakpoint::setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback ) -{ - boost::recursive_mutex::scoped_lock l(m_breakpointLock); - - CallbackBreakpoint *bp = new CallbackBreakpoint(); - bp->m_pystate = PyThreadState_Get(); - bp->m_callback = callback; - bp->set(offset,size,accessType); - - m_breakpointMap[bp->m_id] = bp; - - return bp->m_id; -} - -/////////////////////////////////////////////////////////////////////////////// - -void CallbackBreakpoint::removeBreakpoint( kdlib::BREAKPOINT_ID id ) -{ - boost::recursive_mutex::scoped_lock l(m_breakpointLock); - - BreakpointMap::iterator it = m_breakpointMap.find(id); - if ( it != m_breakpointMap.end() ) - m_breakpointMap.erase(it); - - kdlib::breakPointRemove(id); -} - -/////////////////////////////////////////////////////////////////////////////// - -kdlib::DebugCallbackResult CallbackBreakpoint::onHit() -{ - kdlib::DebugCallbackResult result = kdlib::DebugCallbackNoChange; - PyEval_RestoreThread( m_pystate ); try { - do { - - if ( !m_callback ) - { - result = kdlib::DebugCallbackBreak; - break; - } - - python::object resObj = m_callback(); - - if ( resObj.is_none() ) - { - result = kdlib::DebugCallbackNoChange; - break; - } - - int retVal = python::extract( resObj ); - - if ( retVal >= kdlib::DebugCallbackMax ) - { - result = kdlib::DebugCallbackBreak; - break; - } - - result = kdlib::DebugCallbackResult(retVal); - - } while( FALSE ); - + python::override pythonHandler = get_override("onCurrentThreadChange"); + if ( pythonHandler ) + { + pythonHandler(threadid); + } } catch (const python::error_already_set &) { printException(); - result = kdlib::DebugCallbackBreak; } m_pystate = PyEval_SaveThread(); - - return result; } -/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// -kdlib::BREAKPOINT_ID setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback) +Breakpoint::Breakpoint(kdlib::BreakpointPtr bp) { - return CallbackBreakpoint::setSoftwareBreakpoint(offset,callback); + m_pystate = PyThreadState_Get(); + m_breakpoint = bp; } -/////////////////////////////////////////////////////////////////////////////// - -kdlib::BREAKPOINT_ID setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType,python::object &callback) -{ - return CallbackBreakpoint::setHardwareBreakpoint(offset, size, accessType,callback); -} - -/////////////////////////////////////////////////////////////////////////////// - -void breakPointRemove( kdlib::BREAKPOINT_ID id ) -{ - CallbackBreakpoint::removeBreakpoint(id); -} - -/////////////////////////////////////////////////////////////////////////////// - -Breakpoint::Breakpoint( kdlib::MEMOFFSET_64 offset ) +Breakpoint::Breakpoint(kdlib::MEMOFFSET_64 offset) { AutoRestorePyState pystate(&m_pystate); - set(offset); + + m_breakpoint = kdlib::softwareBreakPointSet(offset, this); } -/////////////////////////////////////////////////////////////////////////////// +Breakpoint::Breakpoint(kdlib::MEMOFFSET_64 offset, python::object &callback) +{ + m_callback = callback; -Breakpoint::Breakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType ) + AutoRestorePyState pystate(&m_pystate); + + m_breakpoint = kdlib::softwareBreakPointSet(offset, this); +} + +Breakpoint::Breakpoint(kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType) { AutoRestorePyState pystate(&m_pystate); - set(offset, size, accessType); + + m_breakpoint = kdlib::hardwareBreakPointSet(offset, size, accessType, this); } -/////////////////////////////////////////////////////////////////////////////// +Breakpoint::Breakpoint(kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback) +{ + m_callback = callback; + + AutoRestorePyState pystate(&m_pystate); + + m_breakpoint = kdlib::hardwareBreakPointSet(offset, size, accessType, this); +} + +///////////////////////////////////////////////////////////////////////////////// kdlib::DebugCallbackResult Breakpoint::onHit() { @@ -410,14 +314,23 @@ kdlib::DebugCallbackResult Breakpoint::onHit() do { - python::override pythonHandler = get_override( "onHit" ); - if ( !pythonHandler ) - { - result = kdlib::BaseBreakpoint::onHit(); - break; - } + python::object resObj; - python::object resObj = pythonHandler(); + if ( m_callback ) + { + resObj = m_callback(); + } + else + { + python::override pythonHandler = get_override( "onHit" ); + if ( !pythonHandler ) + { + result = kdlib::DebugCallbackBreak; + break; + } + + resObj = pythonHandler(); + } if ( resObj.is_none() ) { @@ -449,6 +362,44 @@ kdlib::DebugCallbackResult Breakpoint::onHit() return result; } -/////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +Breakpoint* Breakpoint::setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback ) +{ + Breakpoint *internalBp = new Breakpoint(offset, callback); + return new Breakpoint(internalBp->m_breakpoint); +} + +///////////////////////////////////////////////////////////////////////////////// + +Breakpoint* Breakpoint::setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback ) +{ + Breakpoint *internalBp = new Breakpoint(offset, size, accessType, callback); + return new Breakpoint(internalBp->m_breakpoint); +} + +///////////////////////////////////////////////////////////////////////////////// + +Breakpoint* Breakpoint::getBreakpointByIndex(unsigned long index) +{ + kdlib::BreakpointPtr bp; + + { + AutoRestorePyState pystate; + bp = kdlib::getBreakpointByIndex(index); + } + + return new Breakpoint(bp); +} + +///////////////////////////////////////////////////////////////////////////////// + +void Breakpoint::remove() +{ + m_breakpoint->remove(); + m_breakpoint = 0; +} + +///////////////////////////////////////////////////////////////////////////////// } // end namespace pykd diff --git a/pykd/pyeventhandler.h b/pykd/pyeventhandler.h index a451ed5..0487822 100644 --- a/pykd/pyeventhandler.h +++ b/pykd/pyeventhandler.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include @@ -33,6 +33,7 @@ public: virtual kdlib::DebugCallbackResult onException( const kdlib::ExceptionInfo &exceptionInfo ); virtual kdlib::DebugCallbackResult onModuleLoad( kdlib::MEMOFFSET_64 offset, const std::wstring &name ); virtual kdlib::DebugCallbackResult onModuleUnload( kdlib::MEMOFFSET_64 offset, const std::wstring &name ); + virtual void onCurrentThreadChange(kdlib::THREAD_DEBUG_ID threadid); private: @@ -41,26 +42,133 @@ private: ///////////////////////////////////////////////////////////////////////////////// -kdlib::BREAKPOINT_ID setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback = python::object() ); -kdlib::BREAKPOINT_ID setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType,python::object &callback = python::object()) ; -void breakPointRemove( kdlib::BREAKPOINT_ID id ); - -class Breakpoint : public python::wrapper, public kdlib::BaseBreakpoint { +class Breakpoint : public python::wrapper, public kdlib::BreakpointCallback +{ public: - Breakpoint( kdlib::MEMOFFSET_64 offset ); - Breakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType ); + static Breakpoint* setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, python::object &callback = python::object() ); + + static Breakpoint* setHardwareBreakpoint( kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback= python::object() ); + + static unsigned long getNumberBreakpoints() { + AutoRestorePyState pystate; + return kdlib::getNumberBreakpoints(); + } + + static Breakpoint* getBreakpointByIndex(unsigned long index); + +public: + + explicit Breakpoint(kdlib::BreakpointPtr bp); + + explicit Breakpoint(kdlib::MEMOFFSET_64 offset); + + Breakpoint(kdlib::MEMOFFSET_64 offset, python::object &callback); + + Breakpoint(kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType); + + Breakpoint(kdlib::MEMOFFSET_64 offset, size_t size, kdlib::ACCESS_TYPE accessType, python::object &callback); + + ~Breakpoint() + {} virtual kdlib::DebugCallbackResult onHit(); + virtual void onRemove() { + delete this; + } + + kdlib::BREAKPOINT_ID getId() const + { + AutoRestorePyState pystate; + return m_breakpoint->getId(); + } + + + kdlib::MEMOFFSET_64 getOffset() const + { + AutoRestorePyState pystate; + return m_breakpoint->getOffset(); + } + + void remove(); + private: PyThreadState* m_pystate; + kdlib::BreakpointPtr m_breakpoint; + + python::object m_callback; }; + + + + + + + + +// +//class BreakpointBase +//{ +//public: +// kdlib::DebugCallbackResult onHit() { +// return kdlib::DebugCallbackBreak; +// } +//}; +// +//typedef kdlib::AutoBreakpoint Breakpoint; + + +//class BaseBreakpoint { +//public: +// virtual kdlib::DebugCallbackResult onHit() = 0; +//}; + + +//class Breakpoint +//{ +//public: +// +// Breakpoint( kdlib::MEMOFFSET_64 offset ) { +// } +// +// //static kdlib::BreakpointPtr createSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset ) { +// // return kdlib::BreakpointPtr( new kdlib::AutoBreakpoint(offset) ); +// //} +// +// virtual kdlib::DebugCallbackResult onHit(); +// +// static kdlib::BREAKPOINT_ID getId(kdlib::BreakpointPtr& bp) { +// AutoRestorePyState pystate; +// return bp->getId(); +// } +// +// static kdlib::MEMOFFSET_64 getOffset(kdlib::BreakpointPtr& bp) { +// AutoRestorePyState pystate; +// return bp->getOffset(); +// } +// +// static void remove(kdlib::BreakpointPtr& bp) { +// AutoRestorePyState pystate; +// return bp->remove(); +// } +// +// Breakpoint() { +// m_pystate = PyThreadState_Get(); +// } +// +//private: +// +// PyThreadState* m_pystate; +//}; + + + /////////////////////////////////////////////////////////////////////////////// } // end namespace pykd diff --git a/pykd/pykdver.h b/pykd/pykdver.h index 9abd567..85809e4 100644 --- a/pykd/pykdver.h +++ b/pykd/pykdver.h @@ -2,7 +2,7 @@ #define PYKD_VERSION_MAJOR 0 #define PYKD_VERSION_MINOR 3 #define PYKD_VERSION_SUBVERSION 0 -#define PYKD_VERSION_BUILDNO 12 +#define PYKD_VERSION_BUILDNO 13 #define __VER_STR2__(x) #x #define __VER_STR1__(x) __VER_STR2__(x) diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp index 131550c..d740800 100644 --- a/pykd/pymod.cpp +++ b/pykd/pymod.cpp @@ -74,8 +74,8 @@ BOOST_PYTHON_FUNCTION_OVERLOADS( getThreadIdBySystemId_, pykd::getThreadIdBySyst BOOST_PYTHON_FUNCTION_OVERLOADS( createStruct_, pykd::defineStruct, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( createUnion_, pykd::defineUnion, 1, 2 ); -BOOST_PYTHON_FUNCTION_OVERLOADS( setSoftwareBreakpoint_, pykd::setSoftwareBreakpoint, 1, 2 ); -BOOST_PYTHON_FUNCTION_OVERLOADS( setHardwareBreakpoint_, pykd::setHardwareBreakpoint, 3, 4 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( setSoftwareBreakpoint_, Breakpoint::setSoftwareBreakpoint, 1, 2 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( setHardwareBreakpoint_, Breakpoint::setHardwareBreakpoint, 3, 4 ); BOOST_PYTHON_FUNCTION_OVERLOADS( Module_enumSymbols, ModuleAdapter::enumSymbols, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( Module_findSymbol, ModuleAdapter::findSymbol, 2, 3 ); @@ -363,12 +363,14 @@ BOOST_PYTHON_MODULE( pykd ) "Get the function argument by name" ); // breakpoints - python::def( "setBp", pykd::setSoftwareBreakpoint, setSoftwareBreakpoint_( python::args( "offset", "callback" ), - "Set software breakpoint on executiont" ) ); - python::def( "setBp", pykd::setHardwareBreakpoint, setHardwareBreakpoint_( python::args( "offset", "size", "accsessType", "callback" ) , - "Set hardware breakpoint" ) ); - python::def( "removeBp", pykd::breakPointRemove, - "Remove breapoint by IDs" ); + python::def( "setBp", &Breakpoint::setSoftwareBreakpoint, + setSoftwareBreakpoint_( python::args( "offset", "callback" ),"Set software breakpoint on executiont" )[python::return_value_policy()]); + python::def( "setBp", &Breakpoint::setHardwareBreakpoint, + setHardwareBreakpoint_( python::args( "offset", "size", "accsessType", "callback" ),"Set hardware breakpoint")[python::return_value_policy()]); + python::def( "getNumberBreakpoints", &Breakpoint::getNumberBreakpoints, + "Return number of breakpoints in the current process" ); + python::def( "getBp", &Breakpoint::getBreakpointByIndex, python::return_value_policy(), + "Return breakpoint object by index"); // processes and threads python::def ( "getNumberProcesses", pykd::getNumberProcesses, @@ -401,7 +403,7 @@ BOOST_PYTHON_MODULE( pykd ) "Get all target processes " ); python::def ( "getNumberThreads", pykd::getNumberThreads, - "Return number of threads on the target system" ); + "Return number of threads on the current system" ); python::def( "getThreadId", pykd::getThreadIdByIndex, "Return thread id by index"); python::def( "getThreadOffset", pykd::getThreadOffset, getThreadOffset_( python::args("Id"), @@ -654,7 +656,6 @@ BOOST_PYTHON_MODULE( pykd ) .add_static_property( "Double", &BaseTypesEnum::getDouble ) ; - python::class_( "stackFrame", "class for stack's frame representation", python::no_init ) .add_property( "ip", StackFrameAdapter::getIP, @@ -886,18 +887,25 @@ BOOST_PYTHON_MODULE( pykd ) .def( "onExecutionStatusChange", &EventHandler::onExecutionStatusChange, "Triggered execution status changed. Parameter - execution status.\n" "There is no return value" ) + .def( "onCurrentThreadChange", &EventHandler::onCurrentThreadChange, + "The current thread has changed, which implies that the current target and current process might also have changed.\n" + "There is no return value" ) // .def( "onSymbolsLoaded", &EventHandlerWrap::onSymbolsLoaded, // "Triggered debug symbols loaded. Parameter - module base or 0\n" // "There is no return value") // .def( "onSymbolsUnloaded", &EventHandlerWrap::onSymbolsUnloaded, // "Triggered debug symbols unloaded. Parameter - module base or 0 (all modules)\n" // "There is no return value"); - ; + ; - python::class_( "breakpoint", - "class for breakpoint representation", python::no_init ) - .def( python::init() ) + + python::class_( "breakpoint", + "class for CPU context representation", python::init()) .def( python::init() ) + .def("getId", &Breakpoint::getId, + "Return breakpoint ID" ) + .def("remove", &Breakpoint::remove, + "Remove breakpoint" ) .def("onHit", &Breakpoint::onHit, "Breakpoint hit callback") ; diff --git a/test/scripts/breakpoint.py b/test/scripts/breakpoint.py index eb1e049..9e3f9bb 100644 --- a/test/scripts/breakpoint.py +++ b/test/scripts/breakpoint.py @@ -37,15 +37,14 @@ class BreakpointTest( unittest.TestCase ): pykd.setBp( targetModule.CdeclFunc ) self.assertEqual( pykd.executionStatus.Break, pykd.go() ) - def testRemoveBp(self): processId = pykd.startProcess( target.appPath + " breakhandlertest" ) targetModule = pykd.module( target.moduleName ) targetModule.reload() with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : pykd.go() - bpid = pykd.setBp( targetModule.CdeclFunc ) - pykd.removeBp( bpid ) + bp = pykd.setBp( targetModule.CdeclFunc ) + bp.remove() self.assertEqual( pykd.executionStatus.NoDebuggee, pykd.go() ) def testBreakCallback(self): @@ -54,15 +53,26 @@ class BreakpointTest( unittest.TestCase ): targetModule.reload() with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : pykd.go() - breakCount = callCounter(stopOnBreak) - - bp = pykd.setBp( targetModule.CdeclFunc, breakCount ) - + pykd.setBp( targetModule.CdeclFunc, breakCount ) self.assertEqual( pykd.executionStatus.Break, pykd.go() ) - self.assertEqual( 1, breakCount.count ) + def testBpScope(self): + + def setBpFunc(): + #breakpoint must be set until remove method will be called explicitly + pykd.setBp(targetModule.CdeclFunc) + + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + targetModule = pykd.module( target.moduleName ) + targetModule.reload() + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + pykd.go() + setBpFunc() + self.assertEqual( pykd.executionStatus.Break, pykd.go() ) + + def testNoBreakCallback(self): processId = pykd.startProcess( target.appPath + " breakhandlertest" ) targetModule = pykd.module( target.moduleName ) @@ -72,7 +82,7 @@ class BreakpointTest( unittest.TestCase ): breakCount = callCounter(continueOnBreak) - bp = pykd.setBp( targetModule.CdeclFunc, breakCount ) + pykd.setBp( targetModule.CdeclFunc, breakCount ) self.assertEqual( pykd.executionStatus.NoDebuggee, pykd.go() ) @@ -127,6 +137,7 @@ class BreakpointTest( unittest.TestCase ): self.assertEqual( 1, bp.count ) + def testBreakpointCondition(self): def makebpcallback(n): @@ -155,3 +166,20 @@ class BreakpointTest( unittest.TestCase ): self.assertEqual( pykd.executionStatus.NoDebuggee, pykd.go() ) + def testBreakpointEnum(self): + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + targetModule = pykd.module( target.moduleName ) + targetModule.reload() + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + pykd.go() + + pykd.setBp( targetModule.CdeclFunc) + pykd.setBp( targetModule.CdeclFunc + 1) + pykd.setBp( targetModule.CdeclFunc + 2) + + self.assertEqual(3, pykd.getNumberBreakpoints()); + bpLst = [pykd.getBp(i) for i in xrange(3)] + self.assertEqual(3, len(bpLst)) + map( lambda bp: bp.remove(), bpLst) + self.assertEqual(0, pykd.getNumberBreakpoints()); +