mirror of
https://github.com/ivellioscolin/pykd.git
synced 2025-04-19 19:13:22 +08:00
[0.2.x] + exception handling
git-svn-id: https://pykd.svn.codeplex.com/svn@81883 9b283d60-5439-405e-af05-b73fd8c4d996
This commit is contained in:
parent
74d6b77576
commit
b0a49c7ed4
@ -80,15 +80,53 @@ enum DEBUG_CALLBACK_RESULT {
|
||||
DebugCallbackMax = 3
|
||||
};
|
||||
|
||||
struct ExceptionInfo {
|
||||
|
||||
bool FirstChance;
|
||||
|
||||
ULONG ExceptionCode; /* NTSTATUS */
|
||||
ULONG ExceptionFlags;
|
||||
ULONG64 ExceptionRecord;
|
||||
ULONG64 ExceptionAddress;
|
||||
|
||||
std::vector<ULONG64> Parameters;
|
||||
|
||||
ExceptionInfo(ULONG FirstChance, const EXCEPTION_RECORD64 &Exception);
|
||||
|
||||
python::list getParameters() const;
|
||||
std::string print() const;
|
||||
};
|
||||
typedef boost::shared_ptr< ExceptionInfo > ExceptionInfoPtr;
|
||||
|
||||
struct DEBUG_EVENT_CALLBACK {
|
||||
|
||||
virtual DEBUG_CALLBACK_RESULT OnBreakpoint( ULONG bpId ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnModuleLoad( ULONG64 offset, const std::string &name ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnModuleUnload( ULONG64 offset, const std::string &name ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) = 0;
|
||||
};
|
||||
|
||||
enum EVENT_TYPE {
|
||||
EventTypeBreakpoint = 0x0001,
|
||||
EventTypeException = 0x0002,
|
||||
EventTypeCreateThread = 0x0004,
|
||||
EventTypeExitThread = 0x0008,
|
||||
EventTypeCreateProcess = 0x0010,
|
||||
EventTypeExitProcess = 0x0020,
|
||||
EventTypeLoadModule = 0x0040,
|
||||
EventTypeUnloadModule = 0x0080,
|
||||
EventTypeSystemError = 0x0100,
|
||||
EventTypeSessionStatus = 0x0200,
|
||||
EventTypeChangeDebuggeeState = 0x0400,
|
||||
EventTypeChangeEngineState = 0x0800,
|
||||
EventTypeChangeSymbolState = 0x1000,
|
||||
|
||||
EventTypeMax,
|
||||
};
|
||||
|
||||
EVENT_TYPE getLastEventType();
|
||||
ExceptionInfoPtr getLastExceptionInfo();
|
||||
|
||||
void eventRegisterCallbacks( const DEBUG_EVENT_CALLBACK *callbacks );
|
||||
void eventRemoveCallbacks( const DEBUG_EVENT_CALLBACK *callbacks );
|
||||
|
||||
|
@ -14,6 +14,7 @@ python::handle<> exceptPyType<SymbolException>::pyExceptType;
|
||||
//python::handle<> exceptPyType<pyDia::Exception>::pyExceptType;
|
||||
python::handle<> exceptPyType<TypeException>::pyExceptType;
|
||||
python::handle<> exceptPyType<AddSyntheticSymbolException>::pyExceptType;
|
||||
python::handle<> exceptPyType<WrongEventTypeException>::pyExceptType;
|
||||
python::handle<> exceptPyType<ImplementException>::pyExceptType;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -143,7 +143,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
std::string buildDesc( const std::string &typeName, const std::string &errorStr )
|
||||
static std::string buildDesc( const std::string &typeName, const std::string &errorStr )
|
||||
{
|
||||
std::stringstream sstr;
|
||||
sstr << typeName << " : " << errorStr;
|
||||
@ -171,7 +171,7 @@ private:
|
||||
|
||||
ULONG64 m_targetAddress;
|
||||
|
||||
std::string buildDesc( ULONG64 addr, bool phyAddr )
|
||||
static std::string buildDesc( ULONG64 addr, bool phyAddr )
|
||||
{
|
||||
std::stringstream sstr;
|
||||
if ( phyAddr )
|
||||
@ -195,7 +195,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::string buildDesc(HRESULT hres) {
|
||||
static std::string buildDesc(HRESULT hres) {
|
||||
std::stringstream sstream;
|
||||
sstream << "Add synthetic symbol faield\n";
|
||||
sstream << "HRESULT 0x" << std::hex << hres;
|
||||
@ -203,6 +203,26 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class WrongEventTypeException : public DbgException
|
||||
{
|
||||
public:
|
||||
|
||||
WrongEventTypeException(ULONG eventType)
|
||||
: DbgException( buildDesc(eventType) )
|
||||
{}
|
||||
|
||||
private:
|
||||
static std::string buildDesc(ULONG eventType) {
|
||||
std::stringstream sstream;
|
||||
sstream << "Unknown/not compatible debug event type: 0x";
|
||||
sstream << std::hex << eventType;
|
||||
return sstream.str();
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ImplementException : public DbgException
|
||||
@ -215,7 +235,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
std::string buildDesc( const std::string &file, int line, const std::string &msg )
|
||||
static std::string buildDesc( const std::string &file, int line, const std::string &msg )
|
||||
{
|
||||
std::stringstream sstream;
|
||||
sstream << "File: " << file << " Line: " << line << " " << msg;
|
||||
|
@ -25,7 +25,7 @@ private:
|
||||
virtual DEBUG_CALLBACK_RESULT OnBreakpoint( ULONG bpId ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnModuleLoad( ULONG64 offset, const std::string &name ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnModuleUnload( ULONG64 offset, const std::string &name ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) = 0;
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) = 0;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
@ -48,8 +48,8 @@ public:
|
||||
return handler("onModuleUnload", offset, name );
|
||||
}
|
||||
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ULONG64 offset, ULONG code ) {
|
||||
return handler("onException", offset, code );
|
||||
virtual DEBUG_CALLBACK_RESULT OnException( ExceptionInfoPtr exceptInfo ) {
|
||||
return handler("onException", exceptInfo );
|
||||
}
|
||||
|
||||
private:
|
||||
|
60
pykd/exceptinfo.cpp
Normal file
60
pykd/exceptinfo.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "dbgengine.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace pykd {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExceptionInfo::ExceptionInfo(ULONG FirstChance, const EXCEPTION_RECORD64 &Exception)
|
||||
{
|
||||
FirstChance = FirstChance != FALSE;
|
||||
|
||||
ExceptionCode = Exception.ExceptionCode;
|
||||
ExceptionFlags = Exception.ExceptionFlags;
|
||||
ExceptionRecord = Exception.ExceptionRecord;
|
||||
ExceptionAddress = Exception.ExceptionAddress;
|
||||
|
||||
Parameters.resize(Exception.NumberParameters);
|
||||
for (ULONG i = 0; i < Exception.NumberParameters; ++i)
|
||||
Parameters[i] = Exception.ExceptionInformation[i];
|
||||
}
|
||||
|
||||
|
||||
python::list ExceptionInfo::getParameters() const
|
||||
{
|
||||
python::list ret;
|
||||
std::vector<ULONG64>::const_iterator itParam = Parameters.begin();
|
||||
for (; itParam != Parameters.end(); ++itParam)
|
||||
ret.append(*itParam);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ExceptionInfo::print() const
|
||||
{
|
||||
std::stringstream sstream;
|
||||
|
||||
sstream << "FirstChance= " << (FirstChance ? "True" : "False") << std::endl;
|
||||
|
||||
sstream << std::hex;
|
||||
sstream << "ExceptionCode= 0x" << ExceptionCode << std::endl;
|
||||
sstream << "ExceptionFlags= 0x" << ExceptionFlags << std::endl;
|
||||
sstream << "ExceptionRecord= 0x" << ExceptionRecord << std::endl;
|
||||
sstream << "ExceptionAddress= 0x" << ExceptionAddress << std::endl;
|
||||
|
||||
for (ULONG i = 0; i < Parameters.size(); ++i)
|
||||
{
|
||||
sstream << "Param[" << std::dec << i << "]= 0x";
|
||||
sstream << Parameters[i] << std::endl;
|
||||
}
|
||||
|
||||
return sstream.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="windows-1251"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9,00"
|
||||
Version="9.00"
|
||||
Name="pykd"
|
||||
ProjectGUID="{FE961905-666F-4908-A212-961465F46F13}"
|
||||
RootNamespace="pykd"
|
||||
@ -389,6 +389,10 @@
|
||||
RelativePath=".\eventhandler.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\exceptinfo.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\module.cpp"
|
||||
>
|
||||
@ -581,6 +585,10 @@
|
||||
RelativePath=".\win\dbgio.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\win\lastevent.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\win\memory.cpp"
|
||||
>
|
||||
|
@ -28,7 +28,6 @@ using namespace pykd;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
static const std::string pykdVersion = PYKD_VERSION_BUILD_STR
|
||||
#ifdef _DEBUG
|
||||
" <DBG>"
|
||||
@ -464,6 +463,42 @@ BOOST_PYTHON_MODULE( pykd )
|
||||
.def( "__str__", &StackFrame::print,
|
||||
"Return stacks frame as a string");
|
||||
|
||||
python::class_< ExceptionInfo, ExceptionInfoPtr, boost::noncopyable >(
|
||||
"exceptionInfo", "Exception information", python::no_init )
|
||||
.def_readonly( "FirstChance", &ExceptionInfo::FirstChance,
|
||||
"Specifies whether this exception has been previously encountered")
|
||||
.def_readonly( "ExceptionCode", &ExceptionInfo::ExceptionCode,
|
||||
"The reason the exception occurred")
|
||||
.def_readonly( "ExceptionFlags", &ExceptionInfo::ExceptionFlags,
|
||||
"The exception flags")
|
||||
.def_readonly( "ExceptionRecord", &ExceptionInfo::ExceptionRecord,
|
||||
"A pointer to an associated EXCEPTION_RECORD structure")
|
||||
.def_readonly( "ExceptionAddress", &ExceptionInfo::ExceptionAddress,
|
||||
"The address where the exception occurred")
|
||||
.add_property( "Parameters", &ExceptionInfo::getParameters,
|
||||
"An array of additional arguments that describe the exception")
|
||||
.def( "__str__", &ExceptionInfo::print,
|
||||
"Return object as a string");
|
||||
|
||||
python::enum_<EVENT_TYPE>("eventType", "Type of debug event")
|
||||
.value("Breakpoint", EventTypeBreakpoint)
|
||||
.value("Exception", EventTypeException)
|
||||
.value("CreateThread", EventTypeCreateThread)
|
||||
.value("ExitThread", EventTypeExitThread)
|
||||
.value("CreateProcess", EventTypeCreateProcess)
|
||||
.value("ExitProcess", EventTypeExitProcess)
|
||||
.value("LoadModule", EventTypeLoadModule)
|
||||
.value("UnloadModule", EventTypeUnloadModule)
|
||||
.value("SystemError", EventTypeSystemError)
|
||||
.value("SessionStatus", EventTypeSessionStatus)
|
||||
.value("ChangeDebuggeeState", EventTypeChangeDebuggeeState)
|
||||
.value("ChangeEngineState", EventTypeChangeEngineState)
|
||||
.value("ChangeSymbolState", EventTypeChangeSymbolState)
|
||||
.export_values();
|
||||
|
||||
python::def( "lastEvent", &getLastEventType, "Return type of last event: eventType" );
|
||||
python::def( "lastException", &getLastExceptionInfo, "Return data of last exception event: exceptionInfo" );
|
||||
|
||||
python::class_<Disasm>("disasm", "Class disassemble a processor instructions" )
|
||||
.def( python::init<>( "constructor" ) )
|
||||
.def( python::init<ULONG64>( boost::python::args("offset"), "constructor" ) )
|
||||
@ -477,20 +512,26 @@ BOOST_PYTHON_MODULE( pykd )
|
||||
.def( "ea", &Disasm::ea, "Return effective address for last disassembled instruction or 0" )
|
||||
.def( "reset", &Disasm::reset, "Reset current offset to begin" );
|
||||
|
||||
python::enum_<DEBUG_CALLBACK_RESULT>("eventResult", "Return value of event handler")
|
||||
.value("Proceed", DebugCallbackProceed)
|
||||
.value("NoChange", DebugCallbackNoChange)
|
||||
.value("Break", DebugCallbackBreak)
|
||||
.export_values();
|
||||
|
||||
python::class_<EventHandlerWrap, EventHandlerPtr, boost::noncopyable>(
|
||||
"eventHandler", "Base class for overriding and handling debug notifications" )
|
||||
.def( "onBreakpoint", &EventHandlerWrap::OnBreakpoint,
|
||||
"Triggered breakpoint event. Parameter is int: ID of breakpoint\n"
|
||||
"For ignore event method must return False value" )
|
||||
"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 False value" )
|
||||
"For ignore event method must return eventResult.noChange" )
|
||||
.def( "onModuleUnload", &EventHandlerWrap::OnModuleUnload,
|
||||
"Triggered module unload event. Parameter are long: module base, string: module name\n"
|
||||
"For ignore event method must return False value" )
|
||||
"For ignore event method must return eventResult.noChange" )
|
||||
.def( "onException", &EventHandlerWrap::OnException,
|
||||
"Triggered exception event. Parameters are long: exception address, long: exception code\n"
|
||||
"For ignore event method must return False value" );
|
||||
"Triggered exception event. Parameter - exceptionInfo\n"
|
||||
"For ignore event method must return eventResult.noChange" );
|
||||
|
||||
// wrapper for standart python exceptions
|
||||
python::register_exception_translator<PyException>( &PyException::exceptionTranslate );
|
||||
@ -498,6 +539,7 @@ BOOST_PYTHON_MODULE( pykd )
|
||||
pykd::exception<DbgException>( "BaseException", "Pykd base exception class" );
|
||||
pykd::exception<MemoryException,DbgException>( "MemoryException", "Target memory access exception class" );
|
||||
//pykd::exception<WaitEventException,DbgException>( "WaitEventException", "Debug interface access exception" );
|
||||
pykd::exception<WrongEventTypeException,DbgException>( "WaitEventException", "Debug interface access exception" );
|
||||
pykd::exception<SymbolException,DbgException>( "SymbolException", "Symbol exception" );
|
||||
//pykd::exception<pyDia::Exception,SymbolException>( "DiaException", "Debug interface access exception" );
|
||||
pykd::exception<TypeException,SymbolException>( "TypeException", "type exception" );
|
||||
|
@ -1137,7 +1137,7 @@ HRESULT STDMETHODCALLTYPE DebugEngine::Breakpoint(
|
||||
{
|
||||
PyThread_StateSave pyThreadSave( it->pystate );
|
||||
|
||||
DEBUG_CALLBACK_RESULT ret = it->callback->OnBreakpoint( id );
|
||||
DEBUG_CALLBACK_RESULT ret = it->callback->OnBreakpoint( id );
|
||||
|
||||
result = ret != DebugCallbackNoChange ? ret : result;
|
||||
}
|
||||
@ -1250,7 +1250,9 @@ HRESULT STDMETHODCALLTYPE DebugEngine::Exception(
|
||||
{
|
||||
PyThread_StateSave pyThreadSave( it->pystate );
|
||||
|
||||
DEBUG_CALLBACK_RESULT ret = it->callback->OnException( Exception->ExceptionAddress, Exception->ExceptionCode );
|
||||
DEBUG_CALLBACK_RESULT ret = it->callback->OnException(
|
||||
ExceptionInfoPtr( new ExceptionInfo(FirstChance, *Exception) )
|
||||
);
|
||||
|
||||
result = ret != DebugCallbackNoChange ? ret : result;
|
||||
}
|
||||
|
97
pykd/win/lastevent.cpp
Normal file
97
pykd/win/lastevent.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "dbgengine.h"
|
||||
|
||||
#include "win/dbgeng.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace pykd {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EVENT_TYPE getLastEventType()
|
||||
{
|
||||
ULONG eventType;
|
||||
ULONG ProcessId;
|
||||
ULONG ThreadId;
|
||||
HRESULT hres =
|
||||
g_dbgEng->control->GetLastEventInformation(
|
||||
&eventType,
|
||||
&ProcessId,
|
||||
&ThreadId,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
if (S_OK != hres)
|
||||
throw DbgException("IDebugControl::GetLastEventInformation", hres);
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case DEBUG_EVENT_BREAKPOINT:
|
||||
return EventTypeBreakpoint;
|
||||
case DEBUG_EVENT_EXCEPTION:
|
||||
return EventTypeException;
|
||||
case DEBUG_EVENT_CREATE_THREAD:
|
||||
return EventTypeCreateThread;
|
||||
case DEBUG_EVENT_EXIT_THREAD:
|
||||
return EventTypeExitThread;
|
||||
case DEBUG_EVENT_CREATE_PROCESS:
|
||||
return EventTypeCreateProcess;
|
||||
case DEBUG_EVENT_EXIT_PROCESS:
|
||||
return EventTypeExitProcess;
|
||||
case DEBUG_EVENT_LOAD_MODULE:
|
||||
return EventTypeLoadModule;
|
||||
case DEBUG_EVENT_UNLOAD_MODULE:
|
||||
return EventTypeUnloadModule;
|
||||
case DEBUG_EVENT_SYSTEM_ERROR:
|
||||
return EventTypeSystemError;
|
||||
case DEBUG_EVENT_SESSION_STATUS:
|
||||
return EventTypeSessionStatus;
|
||||
case DEBUG_EVENT_CHANGE_DEBUGGEE_STATE:
|
||||
return EventTypeChangeDebuggeeState;
|
||||
case DEBUG_EVENT_CHANGE_ENGINE_STATE:
|
||||
return EventTypeChangeEngineState;
|
||||
case DEBUG_EVENT_CHANGE_SYMBOL_STATE:
|
||||
return EventTypeChangeSymbolState;
|
||||
}
|
||||
|
||||
throw WrongEventTypeException(eventType);
|
||||
}
|
||||
|
||||
ExceptionInfoPtr getLastExceptionInfo()
|
||||
{
|
||||
ULONG eventType;
|
||||
ULONG ProcessId;
|
||||
ULONG ThreadId;
|
||||
DEBUG_LAST_EVENT_INFO_EXCEPTION LastException;
|
||||
ULONG retLength = sizeof(LastException);
|
||||
HRESULT hres =
|
||||
g_dbgEng->control->GetLastEventInformation(
|
||||
&eventType,
|
||||
&ProcessId,
|
||||
&ThreadId,
|
||||
&LastException,
|
||||
sizeof(LastException),
|
||||
&retLength,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
if (S_OK != hres)
|
||||
throw DbgException("IDebugControl::GetLastEventInformation", hres);
|
||||
|
||||
if (DEBUG_EVENT_EXCEPTION != eventType)
|
||||
throw WrongEventTypeException(eventType);
|
||||
|
||||
return ExceptionInfoPtr(
|
||||
new ExceptionInfo(LastException.FirstChance, LastException.ExceptionRecord)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,136 +1,46 @@
|
||||
"""Debug events handler: test exceptions and debug breaks"""
|
||||
"""Exception event test"""
|
||||
|
||||
import unittest
|
||||
import target
|
||||
import pykd
|
||||
import testutils
|
||||
|
||||
class BreakExceptionHandler(pykd.eventHandler):
|
||||
class ExceptionHandler(pykd.eventHandler):
|
||||
"""Track load/unload module implementation"""
|
||||
def __init__(self, client):
|
||||
pykd.eventHandler.__init__(self, client)
|
||||
|
||||
self.client = client
|
||||
|
||||
self.wasSecondChance = False
|
||||
|
||||
self.wasBreakpoint = False
|
||||
self.wasBreakpointIds = []
|
||||
|
||||
self.bpLastModuleName = ""
|
||||
self.bpCount = 0
|
||||
|
||||
self.firstChanceAccessAddresses = []
|
||||
self.secondChanceAccessAddresses = []
|
||||
|
||||
def onBreakpoint(self, bpId):
|
||||
"""Breakpoint handler"""
|
||||
self.wasBreakpoint = True
|
||||
self.wasBreakpointIds.append( bpId )
|
||||
return pykd.DEBUG_STATUS_BREAK
|
||||
|
||||
def onException(self, exceptParams):
|
||||
"""Exception handler"""
|
||||
|
||||
self.wasSecondChance = not exceptParams["FirstChance"]
|
||||
|
||||
if exceptParams["Code"] == pykd.EXCEPTION_ACCESS_VIOLATION:
|
||||
if self.wasSecondChance:
|
||||
self.secondChanceAccessAddresses.append( exceptParams["Parameters"][1] )
|
||||
else:
|
||||
self.firstChanceAccessAddresses.append( exceptParams["Parameters"][1] )
|
||||
elif exceptParams["Code"] == pykd.EXCEPTION_BREAKPOINT:
|
||||
self.bpCount += 1
|
||||
|
||||
return pykd.DEBUG_STATUS_BREAK
|
||||
|
||||
class BpHandlerTestResult:
|
||||
""" Breakpoint handler test results"""
|
||||
def __init__(self):
|
||||
self.wasCodeBp = None
|
||||
self.wasDataBp = None
|
||||
pykd.eventHandler.__init__(self)
|
||||
self.accessViolationOccured = False
|
||||
|
||||
bpHandlerTestResult = BpHandlerTestResult()
|
||||
def onException(self, exceptInfo):
|
||||
"""Exception handler"""
|
||||
self.accessViolationOccured = exceptInfo.ExceptionCode == 0xC0000005
|
||||
|
||||
def codeBpHandler(bpId):
|
||||
""" Handler of software breakpoint """
|
||||
bpHandlerTestResult.wasCodeBp = bpId
|
||||
if self.accessViolationOccured:
|
||||
self.param0 = exceptInfo.Parameters[0]
|
||||
self.param1 = exceptInfo.Parameters[1]
|
||||
return pykd.eventResult.Break
|
||||
|
||||
def dataBpHandler(bpId):
|
||||
""" Handler of hardware breakpoint """
|
||||
bpHandlerTestResult.wasDataBp = bpId
|
||||
return pykd.DEBUG_STATUS_NO_CHANGE
|
||||
return pykd.eventResult.NoChange
|
||||
|
||||
class EhExceptionBreakpointTest(unittest.TestCase):
|
||||
"""Unit tests of exceptions end breakpoint handling"""
|
||||
class EhExceptionTest(unittest.TestCase):
|
||||
"""Exception event test"""
|
||||
|
||||
def testBreakpointException(self):
|
||||
"""Start new process and track exceptions/breakpoints"""
|
||||
testClient = pykd.createDbgClient()
|
||||
testClient.startProcess( target.appPath + " -testExceptions" )
|
||||
def testException(self):
|
||||
"""Start new process and track exceptions"""
|
||||
_locProcessId = pykd.startProcess( target.appPath + " -testAccessViolation" )
|
||||
with testutils.ContextCallIt( testutils.KillProcess(_locProcessId) ) as killStartedProcess :
|
||||
exceptionHandler = ExceptionHandler()
|
||||
|
||||
targetMod = testClient.loadModule( "targetapp" )
|
||||
while not exceptionHandler.accessViolationOccured:
|
||||
pykd.go()
|
||||
|
||||
bpIdSoftware = testClient.setBp( targetMod.offset("changeValueForAccessTesting"),
|
||||
codeBpHandler )
|
||||
self.assertEqual( pykd.lastEvent(), pykd.eventType.Exception )
|
||||
|
||||
allBp = testClient.getAllBp()
|
||||
self.assertEqual( 1, len( allBp ) )
|
||||
self.assertTrue( bpIdSoftware in allBp )
|
||||
self.assertTrue( exceptionHandler.accessViolationOccured )
|
||||
self.assertEqual( exceptionHandler.param0, 1 ) # write
|
||||
self.assertEqual( exceptionHandler.param1, 6 ) # addr
|
||||
|
||||
hwBpIsNotSet = True
|
||||
# kd> ba e 1 <some_address>
|
||||
# ^ Unable to set breakpoint error
|
||||
# The system resets thread contexts after the process
|
||||
# breakpoint so hardware breakpoints cannot be set.
|
||||
# Go to the executable's entry point and set it then.
|
||||
|
||||
# raw_input("Press <ENTER>....")
|
||||
|
||||
breakExceptionHandler = BreakExceptionHandler( testClient )
|
||||
while not breakExceptionHandler.wasSecondChance:
|
||||
testClient.go()
|
||||
if hwBpIsNotSet:
|
||||
hwBpIsNotSet = False
|
||||
bpIdHwExecute = testClient.setBp( targetMod.offset("readValueForAccessTesting"),
|
||||
1, pykd.DEBUG_BREAK_EXECUTE )
|
||||
|
||||
bpIdHwWrite = testClient.setBp( targetMod.offset("g_valueForAccessTesting1"),
|
||||
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 )
|
||||
self.assertTrue( bpIdHwRead in allBp )
|
||||
|
||||
|
||||
self.assertEqual( [2, 3], breakExceptionHandler.firstChanceAccessAddresses )
|
||||
|
||||
self.assertEqual( 3, breakExceptionHandler.bpCount ) # main and 2 in doExeptions
|
||||
|
||||
self.assertEqual( [3, ], breakExceptionHandler.secondChanceAccessAddresses )
|
||||
|
||||
self.assertTrue( breakExceptionHandler.wasBreakpoint )
|
||||
|
||||
self.assertTrue( bpIdSoftware in breakExceptionHandler.wasBreakpointIds )
|
||||
self.assertTrue( bpIdHwExecute in breakExceptionHandler.wasBreakpointIds )
|
||||
self.assertTrue( bpIdHwWrite in breakExceptionHandler.wasBreakpointIds )
|
||||
self.assertTrue( bpIdHwRead in breakExceptionHandler.wasBreakpointIds )
|
||||
|
||||
testClient.removeBp(bpIdHwRead)
|
||||
self.assertEqual( 3, len( testClient.getAllBp() ) )
|
||||
|
||||
testClient.removeBp()
|
||||
self.assertEqual( 0, len( testClient.getAllBp() ) )
|
||||
|
||||
self.assertEqual( bpHandlerTestResult.wasCodeBp, bpIdSoftware )
|
||||
self.assertTrue( bpHandlerTestResult.wasDataBp, bpIdHwWrite )
|
||||
exceptInfo = pykd.lastException()
|
||||
self.assertEqual( exceptInfo.ExceptionCode, 0xC0000005 )
|
||||
self.assertEqual( exceptionHandler.param0, exceptInfo.Parameters[0] )
|
||||
self.assertEqual( exceptionHandler.param1, exceptInfo.Parameters[1] )
|
||||
|
@ -21,6 +21,7 @@ import typedvar
|
||||
import regtest
|
||||
import localstest
|
||||
import customtypestest
|
||||
import ehexcepttest
|
||||
|
||||
class StartProcessWithoutParamsTest(unittest.TestCase):
|
||||
def testStart(self):
|
||||
@ -44,13 +45,14 @@ def getTestSuite( singleName = "" ):
|
||||
unittest.TestLoader().loadTestsFromTestCase( moduletest.ModuleTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( memtest.MemoryTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( typeinfo.TypeInfoTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( typedvar.TypedVarTest ),
|
||||
#unittest.TestLoader().loadTestsFromTestCase( typedvar.TypedVarTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( regtest.CpuRegTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( customtypestest.CustomTypesTest ),
|
||||
# ^^^
|
||||
unittest.TestLoader().loadTestsFromTestCase( TerminateProcessTest ),
|
||||
|
||||
unittest.TestLoader().loadTestsFromTestCase( localstest.LocalVarsTest ),
|
||||
#unittest.TestLoader().loadTestsFromTestCase( localstest.LocalVarsTest ),
|
||||
unittest.TestLoader().loadTestsFromTestCase( ehexcepttest.EhExceptionTest ),
|
||||
] )
|
||||
else:
|
||||
return unittest.TestSuite(
|
||||
|
@ -537,6 +537,15 @@ int doLoadUnload()
|
||||
|
||||
return 0;
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int doAccessViolation()
|
||||
{
|
||||
char *p = (char *)6;
|
||||
*p = 12;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -649,6 +658,9 @@ int _tmain(int argc, _TCHAR* argv[])
|
||||
if ( !_tcsicmp(argv[1], _T("-testLoadUnload")) )
|
||||
return doLoadUnload();
|
||||
|
||||
if ( !_tcsicmp(argv[1], _T("-testAccessViolation")) )
|
||||
return doAccessViolation();
|
||||
|
||||
if ( !_tcsicmp(argv[1], _T("-testEnumWindows")) )
|
||||
{
|
||||
::EnumWindows(&EnumWindowsProc1, 6);
|
||||
|
Loading…
Reference in New Issue
Block a user