diff --git a/pykd/bpoint.cpp b/pykd/bpoint.cpp index 06b1f93..45edcc5 100644 --- a/pykd/bpoint.cpp +++ b/pykd/bpoint.cpp @@ -48,9 +48,9 @@ static IDebugBreakpoint *setBreakPoint( //////////////////////////////////////////////////////////////////////////////// -static ULONG getBpId(IDebugBreakpoint *bp, IDebugControl4 *control = NULL) +static BPOINT_ID getBpId(IDebugBreakpoint *bp, IDebugControl4 *control = NULL) { - ULONG Id; + BPOINT_ID Id; HRESULT hres = bp->GetId(&Id); if (S_OK != hres) { @@ -63,17 +63,25 @@ static ULONG getBpId(IDebugBreakpoint *bp, IDebugControl4 *control = NULL) //////////////////////////////////////////////////////////////////////////////// -ULONG DebugClient::setSoftwareBp(ULONG64 addr) +BPOINT_ID DebugClient::setSoftwareBp(ULONG64 addr, BpCallback &callback /*= BpCallback()*/) { addr = addr64(addr); IDebugBreakpoint *bp = setBreakPoint(m_control, DEBUG_BREAKPOINT_CODE, addr); - return getBpId(bp, m_control); + const BPOINT_ID Id = getBpId(bp, m_control); + + if (!callback.is_none()) + { + boost::recursive_mutex::scoped_lock mapBpLock(*m_bpCallbacks.m_lock); + m_bpCallbacks.m_map[Id] = callback; + } + + return Id; } //////////////////////////////////////////////////////////////////////////////// -ULONG DebugClient::setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType) +BPOINT_ID DebugClient::setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType, BpCallback &callback /*= BpCallback()*/) { addr = addr64(addr); IDebugBreakpoint *bp = setBreakPoint(m_control, DEBUG_BREAKPOINT_DATA, addr); @@ -85,7 +93,15 @@ ULONG DebugClient::setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType) throw DbgException("IDebugBreakpoint::SetDataParameters", hres); } - return getBpId(bp, m_control); + const BPOINT_ID Id = getBpId(bp, m_control); + + if (!callback.is_none()) + { + boost::recursive_mutex::scoped_lock mapBpLock(*m_bpCallbacks.m_lock); + m_bpCallbacks.m_map[Id] = callback; + } + + return Id; } //////////////////////////////////////////////////////////////////////////////// @@ -113,7 +129,7 @@ python::list DebugClient::getAllBp() //////////////////////////////////////////////////////////////////////////////// -void DebugClient::removeBp(ULONG Id) +void DebugClient::removeBp(BPOINT_ID Id) { IDebugBreakpoint *bp; HRESULT hres = m_control->GetBreakpointById(Id, &bp); diff --git a/pykd/bpoint.h b/pykd/bpoint.h index c987a31..cfd9128 100644 --- a/pykd/bpoint.h +++ b/pykd/bpoint.h @@ -10,14 +10,14 @@ namespace pykd { //////////////////////////////////////////////////////////////////////////////// -inline ULONG setSoftwareBp(ULONG64 addr) { - return g_dbgClient->setSoftwareBp(addr); +inline BPOINT_ID setSoftwareBp(ULONG64 addr, BpCallback &callback = BpCallback()) { + return g_dbgClient->setSoftwareBp(addr, callback); } //////////////////////////////////////////////////////////////////////////////// -inline ULONG setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType) { - return g_dbgClient->setHardwareBp(addr, size, accessType); +inline BPOINT_ID setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType, BpCallback &callback = BpCallback()) { + return g_dbgClient->setHardwareBp(addr, size, accessType, callback); } //////////////////////////////////////////////////////////////////////////////// @@ -28,7 +28,7 @@ inline python::list getAllBp() { //////////////////////////////////////////////////////////////////////////////// -inline void removeBp(ULONG Id) { +inline void removeBp(BPOINT_ID Id) { return g_dbgClient->removeBp(Id); } diff --git a/pykd/dbgclient.cpp b/pykd/dbgclient.cpp index 20bd201..bab0f5f 100644 --- a/pykd/dbgclient.cpp +++ b/pykd/dbgclient.cpp @@ -15,7 +15,7 @@ DebugClientPtr g_dbgClient( DebugClient::createDbgClient() ); DebugClient::DebugClient( IDebugClient4 *client ) : DbgObject( client ) , m_symSymbols( new SyntheticSymbols(*m_symbols, *this) ) - , m_internalDbgEventHandler(client, m_symSymbols) + , m_internalDbgEventHandler(client, this, m_symSymbols, m_bpCallbacks) { } diff --git a/pykd/dbgclient.h b/pykd/dbgclient.h index 3aacc80..f4f9d14 100644 --- a/pykd/dbgclient.h +++ b/pykd/dbgclient.h @@ -29,6 +29,18 @@ typedef boost::shared_ptr DebugClientPtr; ///////////////////////////////////////////////////////////////////////////////// +typedef ULONG BPOINT_ID; +typedef python::object BpCallback; +typedef std::map BpCallbackMapIml; +struct BpCallbackMap { + boost::shared_ptr m_lock; + BpCallbackMapIml m_map; + + BpCallbackMap() : m_lock(new boost::recursive_mutex) {} +}; + +///////////////////////////////////////////////////////////////////////////////// + class DebugClient : private DbgObject { private: @@ -298,12 +310,12 @@ public: } // breakpoints management - ULONG setSoftwareBp(ULONG64 addr); - ULONG setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType); + BPOINT_ID setSoftwareBp(ULONG64 addr, BpCallback &callback = BpCallback()); + BPOINT_ID setHardwareBp(ULONG64 addr, ULONG size, ULONG accessType, BpCallback &callback = BpCallback()); python::list getAllBp(); - void removeBp(ULONG Id); + void removeBp(BPOINT_ID Id); void removeAllBp(); private: @@ -314,6 +326,8 @@ private: //python::list //loadArray( ULONG64 offset, ULONG count, bool phyAddr ); + BpCallbackMap m_bpCallbacks; + SynSymbolsPtr m_symSymbols; // DebugClient is creator InternalDbgEventHandler m_internalDbgEventHandler; diff --git a/pykd/dbgext.cpp b/pykd/dbgext.cpp index 5ba7123..040c582 100644 --- a/pykd/dbgext.cpp +++ b/pykd/dbgext.cpp @@ -85,6 +85,8 @@ BOOST_PYTHON_FUNCTION_OVERLOADS( loadSignDWords_, loadSignDWords, 2, 3 ); BOOST_PYTHON_FUNCTION_OVERLOADS( loadSignQWords_, loadSignQWords, 2, 3 ); BOOST_PYTHON_FUNCTION_OVERLOADS( compareMemory_, compareMemory, 3, 4 ); BOOST_PYTHON_FUNCTION_OVERLOADS( getLocals_, getLocals, 0, 1 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( setSoftwareBp_, setSoftwareBp, 1, 2 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( setHardwareBp_, setHardwareBp, 3, 4 ); BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_loadChars, DebugClient::loadChars, 2, 3 ); BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_loadWChars, DebugClient::loadWChars, 2, 3 ); @@ -99,6 +101,8 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_loadSignQWords, DebugClient: BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_compareMemory, DebugClient::compareMemory, 3, 4 ); BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_getLocals, DebugClient::getLocals, 0, 1 ); BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( pyDia_Symbol_findChildrenEx, pyDia::Symbol::findChildrenEx, 1, 3 ); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_setSoftwareBp, DebugClient::setSoftwareBp, 1, 2 ); +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( DebugClient_setHardwareBp, DebugClient::setHardwareBp, 3, 4 ); #define DEF_PY_CONST_ULONG(x) \ python::scope().attr(#x) = ULONG(##x) @@ -300,10 +304,10 @@ BOOST_PYTHON_MODULE( pykd ) "Get context of current thread (register values)" ) .def( "getLocals", &DebugClient::getLocals, DebugClient_getLocals( python::args( "ctx" ), "Get list of local variables" ) ) - .def( "setBp", &DebugClient::setSoftwareBp, - "Set software breakpoint on executiont" ) - .def( "setBp", &DebugClient::setHardwareBp, - "Set hardware breakpoint" ) + .def( "setBp", &DebugClient::setSoftwareBp, DebugClient_setSoftwareBp( python::args( "offset", "callback" ), + "Set software breakpoint on executiont" ) ) + .def( "setBp", &DebugClient::setHardwareBp, DebugClient_setHardwareBp( python::args( "offset", "size", "accsessType", "callback" ), + "Set hardware breakpoint" ) ) .def( "getAllBp", &DebugClient::getAllBp, "Get all breapoint IDs" ) .def( "removeBp", &DebugClient::removeBp, @@ -485,10 +489,10 @@ BOOST_PYTHON_MODULE( pykd ) "Get context of current thread (register values)" ); python::def( "getLocals", &getLocals, getLocals_( python::args( "ctx" ), "Get list of local variables" ) ); - python::def( "setBp", &setSoftwareBp, - "Set software breakpoint on executiont" ); - python::def( "setBp", &setHardwareBp, - "Set hardware breakpoint" ); + python::def( "setBp", &setSoftwareBp, setSoftwareBp_( python::args( "offset", "callback" ), + "Set software breakpoint on executiont" ) ); + python::def( "setBp", &setHardwareBp, setHardwareBp_( python::args( "offset", "size", "accsessType", "callback" ) , + "Set hardware breakpoint" ) ); python::def( "getAllBp", &getAllBp, "Get all breapoint IDs" ); python::def( "removeBp", &removeBp, diff --git a/pykd/inteventhandler.cpp b/pykd/inteventhandler.cpp index 90fd97d..abad293 100644 --- a/pykd/inteventhandler.cpp +++ b/pykd/inteventhandler.cpp @@ -1,8 +1,7 @@ #include "stdafx.h" -#include "inteventhandler.h" -#include "dbgexcept.h" +#include "dbgclient.h" namespace pykd { @@ -10,13 +9,21 @@ namespace pykd { InternalDbgEventHandler::InternalDbgEventHandler( IDebugClient4 *client, - SynSymbolsPtr synSymbols -) : m_synSymbols(synSymbols) + DebugClient *parentClient, + SynSymbolsPtr synSymbols, + BpCallbackMap &bpCallbacks +) : m_parentClient(parentClient) + , m_synSymbols(synSymbols) + , m_bpCallbacks(bpCallbacks) { HRESULT hres = client->CreateClient(&m_client); if (FAILED(hres)) throw DbgException("Call IDebugClient::CreateClient failed"); + hres = m_client->QueryInterface(__uuidof(IDebugControl), (void**)&m_control); + if ( FAILED( hres ) ) + throw DbgException("QueryInterface IDebugControl failed"); + m_client->SetEventCallbacks(this); } @@ -34,6 +41,8 @@ HRESULT InternalDbgEventHandler::GetInterestMask( ) { *Mask = + DEBUG_EVENT_BREAKPOINT | + DEBUG_EVENT_CHANGE_ENGINE_STATE | DEBUG_EVENT_CHANGE_SYMBOL_STATE; return S_OK; @@ -56,6 +65,46 @@ HRESULT InternalDbgEventHandler::ChangeSymbolState( /////////////////////////////////////////////////////////////////////////////////// +HRESULT InternalDbgEventHandler::ChangeEngineState( + __in ULONG Flags, + __in ULONG64 Argument +) +{ + HRESULT hres = S_OK; + + if (DEBUG_CES_BREAKPOINTS & Flags) + hres = bpChanged(static_cast(Argument)); + + return hres; +} + +/////////////////////////////////////////////////////////////////////////////////// + +HRESULT InternalDbgEventHandler::Breakpoint(IDebugBreakpoint *bp) +{ + BPOINT_ID Id; + HRESULT hres = bp->GetId(&Id); + if (S_OK == hres) + { + boost::recursive_mutex::scoped_lock mapBpLock(*m_bpCallbacks.m_lock); + BpCallbackMapIml::iterator it = m_bpCallbacks.m_map.find(Id); + if (it != m_bpCallbacks.m_map.end()) + { + try { + PyThread_StateSave pyThreadSave( m_parentClient->getThreadState() ); + hres = python::extract( it->second(Id) ); + return hres; + } + catch (const python::error_already_set &) { + // TODO: some logging, alerting... + } + } + } + return DEBUG_STATUS_NO_CHANGE; +} + +/////////////////////////////////////////////////////////////////////////////////// + HRESULT InternalDbgEventHandler::symLoaded( __in ULONG64 ModuleAddress ) @@ -73,4 +122,23 @@ HRESULT InternalDbgEventHandler::symLoaded( //////////////////////////////////////////////////////////////////////////////// +HRESULT InternalDbgEventHandler::bpChanged(BPOINT_ID Id) +{ + if (DEBUG_ANY_ID == Id) + return S_OK; + + IDebugBreakpoint *bp; + HRESULT hres = m_control->GetBreakpointById(Id, &bp); + if (E_NOINTERFACE == hres) + { + // breakpoint was removed + boost::recursive_mutex::scoped_lock mapBpLock(*m_bpCallbacks.m_lock); + m_bpCallbacks.m_map.erase(Id); + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + }; // namespace pykd diff --git a/pykd/inteventhandler.h b/pykd/inteventhandler.h index 366b5c0..d52d13e 100644 --- a/pykd/inteventhandler.h +++ b/pykd/inteventhandler.h @@ -7,14 +7,26 @@ #include #include "synsymbol.h" +//////////////////////////////////////////////////////////////////////////////// + + namespace pykd { +//////////////////////////////////////////////////////////////////////////////// + +struct BpCallbackMap; +class DebugClient; + +//////////////////////////////////////////////////////////////////////////////// + class InternalDbgEventHandler : public DebugBaseEventCallbacks { public: InternalDbgEventHandler( IDebugClient4 *client, - SynSymbolsPtr synSymbols + DebugClient *parentClient, + SynSymbolsPtr synSymbols, + BpCallbackMap &bpCallbacks ); ~InternalDbgEventHandler(); @@ -32,14 +44,34 @@ protected: STDMETHOD(ChangeSymbolState)( __in ULONG Flags, __in ULONG64 Argument - ); + ) override; + + STDMETHOD(ChangeEngineState)( + __in ULONG Flags, + __in ULONG64 Argument + ) override; + + STDMETHOD(Breakpoint)( + __in IDebugBreakpoint *bp + ) override; private: HRESULT symLoaded(__in ULONG64 ModuleAddress); + HRESULT bpChanged(ULONG Id); + IDebugClient *m_client; + IDebugControl *m_control; + + DebugClient *m_parentClient; + SynSymbolsPtr m_synSymbols; + BpCallbackMap &m_bpCallbacks; }; +//////////////////////////////////////////////////////////////////////////////// + }; // namespace pykd + +//////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/pykd/stdafx.h b/pykd/stdafx.h index 5ff2c5c..fe14b03 100644 --- a/pykd/stdafx.h +++ b/pykd/stdafx.h @@ -52,3 +52,5 @@ namespace python = boost::python; #include #include +#include + diff --git a/pykd/synsymbol.h b/pykd/synsymbol.h index e297cc5..7742977 100644 --- a/pykd/synsymbol.h +++ b/pykd/synsymbol.h @@ -7,7 +7,6 @@ #include "dbgexcept.h" #include #include -#include namespace pykd { diff --git a/test/scripts/ehexcepttest.py b/test/scripts/ehexcepttest.py index e5af138..4514223 100644 --- a/test/scripts/ehexcepttest.py +++ b/test/scripts/ehexcepttest.py @@ -43,6 +43,24 @@ class BreakExceptionHandler(pykd.eventHandler): return pykd.DEBUG_STATUS_BREAK +class BpHandlerTestResult: + """ Breakpoint handler test results""" + def __init__(self): + self.wasCodeBp = None + self.wasDataBp = None + +bpHandlerTestResult = BpHandlerTestResult() + +def codeBpHandler(bpId): + """ Handler of software breakpoint """ + bpHandlerTestResult.wasCodeBp = bpId + return pykd.DEBUG_STATUS_NO_CHANGE + +def dataBpHandler(bpId): + """ Handler of hardware breakpoint """ + bpHandlerTestResult.wasDataBp = bpId + return pykd.DEBUG_STATUS_NO_CHANGE + class EhExceptionBreakpointTest(unittest.TestCase): """Unit tests of exceptions end breakpoint handling""" @@ -53,7 +71,8 @@ class EhExceptionBreakpointTest(unittest.TestCase): targetMod = testClient.loadModule( "targetapp" ) - bpIdSoftware = testClient.setBp( targetMod.offset("changeValueForAccessTesting") ) + bpIdSoftware = testClient.setBp( targetMod.offset("changeValueForAccessTesting"), + codeBpHandler ) allBp = testClient.getAllBp() self.assertEqual( 1, len( allBp ) ) @@ -75,13 +94,18 @@ class EhExceptionBreakpointTest(unittest.TestCase): 1, pykd.DEBUG_BREAK_EXECUTE ) bpIdHwWrite = testClient.setBp( targetMod.offset("g_valueForAccessTesting1"), - 1, pykd.DEBUG_BREAK_WRITE ) + 1, pykd.DEBUG_BREAK_WRITE, dataBpHandler ) bpIdHwRead = testClient.setBp( targetMod.offset("g_valueForAccessTesting2"), 1, pykd.DEBUG_BREAK_READ ) + self.assertTrue( bpIdSoftware != bpIdHwExecute and + bpIdHwExecute != bpIdHwWrite and + bpIdHwWrite != bpIdHwRead ) + allBp = testClient.getAllBp() self.assertEqual( 4, len( allBp ) ) + self.assertTrue( bpIdSoftware in allBp ) self.assertTrue( bpIdHwExecute in allBp ) self.assertTrue( bpIdHwWrite in allBp ) @@ -106,3 +130,6 @@ class EhExceptionBreakpointTest(unittest.TestCase): testClient.removeBp() self.assertEqual( 0, len( testClient.getAllBp() ) ) + + self.assertEqual( bpHandlerTestResult.wasCodeBp, bpIdSoftware ) + self.assertTrue( bpHandlerTestResult.wasDataBp, bpIdHwWrite )