From b0a49c7ed42fa2812850cd621a73843f69b8b9fb Mon Sep 17 00:00:00 2001
From: "SND\\EreTIk_cp" <SND\EreTIk_cp@9b283d60-5439-405e-af05-b73fd8c4d996>
Date: Tue, 18 Dec 2012 16:28:04 +0000
Subject: [PATCH] [0.2.x] + exception handling

git-svn-id: https://pykd.svn.codeplex.com/svn@81883 9b283d60-5439-405e-af05-b73fd8c4d996
---
 pykd/dbgengine.h             |  40 +++++++++-
 pykd/dbgexcept.cpp           |   1 +
 pykd/dbgexcept.h             |  28 ++++++-
 pykd/eventhandler.h          |   6 +-
 pykd/exceptinfo.cpp          |  60 ++++++++++++++
 pykd/pykd_2008.vcproj        |  10 ++-
 pykd/pymod.cpp               |  54 +++++++++++--
 pykd/win/dbgeng.cpp          |   6 +-
 pykd/win/lastevent.cpp       |  97 ++++++++++++++++++++++
 test/scripts/ehexcepttest.py | 150 +++++++----------------------------
 test/scripts/pykdtest.py     |   6 +-
 test/targetapp/targetapp.cpp |  12 +++
 12 files changed, 331 insertions(+), 139 deletions(-)
 create mode 100644 pykd/exceptinfo.cpp
 create mode 100644 pykd/win/lastevent.cpp

diff --git a/pykd/dbgengine.h b/pykd/dbgengine.h
index eaf3ef2..4ae318c 100644
--- a/pykd/dbgengine.h
+++ b/pykd/dbgengine.h
@@ -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 );
 
diff --git a/pykd/dbgexcept.cpp b/pykd/dbgexcept.cpp
index a5b4a11..7d6e919 100644
--- a/pykd/dbgexcept.cpp
+++ b/pykd/dbgexcept.cpp
@@ -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;
 
 ///////////////////////////////////////////////////////////////////////////////////
diff --git a/pykd/dbgexcept.h b/pykd/dbgexcept.h
index 397c3e8..741e03b 100644
--- a/pykd/dbgexcept.h
+++ b/pykd/dbgexcept.h
@@ -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;
diff --git a/pykd/eventhandler.h b/pykd/eventhandler.h
index 4ee4a17..074732b 100644
--- a/pykd/eventhandler.h
+++ b/pykd/eventhandler.h
@@ -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:
diff --git a/pykd/exceptinfo.cpp b/pykd/exceptinfo.cpp
new file mode 100644
index 0000000..4134fbb
--- /dev/null
+++ b/pykd/exceptinfo.cpp
@@ -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();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj
index acb922b..d59616c 100644
--- a/pykd/pykd_2008.vcproj
+++ b/pykd/pykd_2008.vcproj
@@ -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"
 				>
diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp
index 23260fb..d452d2e 100644
--- a/pykd/pymod.cpp
+++ b/pykd/pymod.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" );
diff --git a/pykd/win/dbgeng.cpp b/pykd/win/dbgeng.cpp
index eaacc27..feab142 100644
--- a/pykd/win/dbgeng.cpp
+++ b/pykd/win/dbgeng.cpp
@@ -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;
     }
diff --git a/pykd/win/lastevent.cpp b/pykd/win/lastevent.cpp
new file mode 100644
index 0000000..e925a5e
--- /dev/null
+++ b/pykd/win/lastevent.cpp
@@ -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)
+    );
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
diff --git a/test/scripts/ehexcepttest.py b/test/scripts/ehexcepttest.py
index 5bd8bcd..cdab7bf 100644
--- a/test/scripts/ehexcepttest.py
+++ b/test/scripts/ehexcepttest.py
@@ -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] )
diff --git a/test/scripts/pykdtest.py b/test/scripts/pykdtest.py
index deb1db0..d466116 100644
--- a/test/scripts/pykdtest.py
+++ b/test/scripts/pykdtest.py
@@ -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(
diff --git a/test/targetapp/targetapp.cpp b/test/targetapp/targetapp.cpp
index 1e21075..a70d385 100644
--- a/test/targetapp/targetapp.cpp
+++ b/test/targetapp/targetapp.cpp
@@ -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);