diff --git a/pykd/eventhandler.cpp b/pykd/eventhandler.cpp index 28d9859..4e6da05 100644 --- a/pykd/eventhandler.cpp +++ b/pykd/eventhandler.cpp @@ -118,5 +118,46 @@ BreakpointPtr Breakpoint::setSoftwareBreakpoint( kdlib::MEMOFFSET_64 offset, pyt /////////////////////////////////////////////////////////////////////////////// +EventHandler::EventHandler() +{ + m_pystate = PyThreadState_Get(); +} + +/////////////////////////////////////////////////////////////////////////////// + +kdlib::DebugCallbackResult EventHandler::onBreakpoint( kdlib::BREAKPOINT_ID bpId ) +{ + kdlib::DebugCallbackResult result = kdlib::DebugCallbackNoChange; + + PyEval_RestoreThread( m_pystate ); + + try { + + do { + + python::override pythonHandler = get_override( "onBreakpoint" ); + if ( !pythonHandler ) + { + result = kdlib::EventHandler::onBreakpoint( bpId ); + break; + } + + return pythonHandler(bpId ); + + } while( FALSE ); + + } + catch (const python::error_already_set &) + { + printException(); + result = kdlib::DebugCallbackBreak; + } + + m_pystate = PyEval_SaveThread(); + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// } // end namespace pykd diff --git a/pykd/eventhandler.h b/pykd/eventhandler.h index 8c57897..a2dc8f9 100644 --- a/pykd/eventhandler.h +++ b/pykd/eventhandler.h @@ -1,10 +1,13 @@ #pragma once #include "kdlib/dbgengine.h" +#include "kdlib/eventhandler.h" #include "boost/shared_ptr.hpp" #include "boost/noncopyable.hpp" #include "boost/python/object.hpp" +#include "boost/python/wrapper.hpp" + namespace python = boost::python; namespace pykd { @@ -30,4 +33,22 @@ protected: /////////////////////////////////////////////////////////////////////////////// +class EventHandler; +typedef boost::shared_ptr EventHandlerPtr; + +class EventHandler : public python::wrapper, public kdlib::EventHandler +{ +public: + + EventHandler(); + + virtual kdlib::DebugCallbackResult onBreakpoint( kdlib::BREAKPOINT_ID bpId ); + +private: + + PyThreadState* m_pystate; +}; + +/////////////////////////////////////////////////////////////////////////////// + } // end namespace pykd diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp index 3ad3cc3..8439a26 100644 --- a/pykd/pymod.cpp +++ b/pykd/pymod.cpp @@ -634,11 +634,11 @@ BOOST_PYTHON_MODULE( pykd ) .value("NoDebuggee", kdlib::DebugStatusNoDebuggee ) .export_values(); - //python::class_( - // "eventHandler", "Base class for overriding and handling debug notifications" ) - // .def( "onBreakpoint", &EventHandler::OnBreakpoint, - // "Triggered breakpoint event. Parameter is int: ID of breakpoint\n" - // "For ignore event method must return eventResult.noChange" ) + python::class_( + "eventHandler", "Base class for overriding and handling debug notifications" ) + .def( "onBreakpoint", &EventHandler::onBreakpoint, + "Triggered breakpoint event. Parameter is int: ID of breakpoint\n" + "For ignore event method must return eventResult.noChange" ) // .def( "onModuleLoad", &EventHandlerWrap::OnModuleLoad, // "Triggered module load event. Parameter are long: module base, string: module name\n" // "For ignore event method must return eventResult.noChange" ) @@ -657,7 +657,7 @@ BOOST_PYTHON_MODULE( pykd ) // .def( "onSymbolsUnloaded", &EventHandlerWrap::onSymbolsUnloaded, // "Triggered debug symbols unloaded. Parameter - module base or 0 (all modules)\n" // "There is no return value"); - + ; python::register_exception_translator( &ExceptionTranslator::indexTranslate ); diff --git a/test/scripts/breakpoint.py b/test/scripts/breakpoint.py new file mode 100644 index 0000000..0346306 --- /dev/null +++ b/test/scripts/breakpoint.py @@ -0,0 +1,112 @@ +""" breakpoints """ + +import unittest +import pykd +import target +import testutils + +class callCounter: + def __init__(self,func): + self.count = 0 + self.func = func + def __call__(self,val): + self.count = self.count+1 + return self.func(val) + +def stopOnBreak(id): + return pykd.Break + +def continueOnBreak(id): + return pykd.NoChange + + +class BreakpointTest( unittest.TestCase ): + + def testNoBreakpoint(self): + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + pykd.go() + self.assertEqual( pykd.NoDebuggee, pykd.go() ) + + def testSetBp(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 ) + self.assertEqual( pykd.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 ) + self.assertEqual( pykd.NoDebuggee, pykd.go() ) + + def testBreakCallback(self): + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + targetModule = pykd.module( target.moduleName ) + targetModule.reload() + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + pykd.go() + + breakCount = callCounter(stopOnBreak) + + bp = pykd.breakpoint( targetModule.CdeclFunc, breakCount ) + + self.assertEqual( pykd.Break, pykd.go() ) + + self.assertEqual( 1, breakCount.count ) + + def testNoBreakCallback(self): + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + targetModule = pykd.module( target.moduleName ) + targetModule.reload() + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + pykd.go() + + breakCount = callCounter(continueOnBreak) + + bp = pykd.breakpoint( targetModule.CdeclFunc, breakCount ) + + self.assertEqual( pykd.NoDebuggee, pykd.go() ) + + self.assertEqual( 1, breakCount.count ) + + def testBreakpointHandler(self): + + class BreakpointHandler( pykd.eventHandler ): + + def __init__(self): + super(BreakpointHandler, self).__init__() + self.count = 0 + + def onBreakpoint( self, bpid_): + self.count = self.count + 1 + + processId = pykd.startProcess( target.appPath + " breakhandlertest" ) + targetModule = pykd.module( target.moduleName ) + targetModule.reload() + with testutils.ContextCallIt( testutils.KillProcess(processId) ) as killStartedProcess : + + pykd.go() + + handler = BreakpointHandler() + + pykd.setBp( targetModule.CdeclFunc ) + + self.assertEqual( pykd.Break, pykd.go() ) + + self.assertEqual( 1, handler.count ) + + + + + + +