Chromium Code Reviews| Index: chrome_frame/crash_reporting/vectored_handler_unittest.cc |
| =================================================================== |
| --- chrome_frame/crash_reporting/vectored_handler_unittest.cc (revision 37418) |
| +++ chrome_frame/crash_reporting/vectored_handler_unittest.cc (working copy) |
| @@ -6,209 +6,180 @@ |
| #include "base/logging.h" |
| #include "chrome_frame/crash_reporting/vectored_handler-impl.h" |
| -#include "chrome_frame/crash_reporting/crash_report.h" |
| -#include "gmock/gmock.h" |
| +#include "chrome_frame/crash_reporting/veh_test.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| -// Class that mocks external call from VectoredHandlerT for testing purposes. |
| -class EMock : public Win32VEHTraits { |
| +using testing::_; |
| + |
| +ACTION_P(StackTraceDump, s) { |
| + memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0])); |
| + return s->count_; |
| +} |
| +namespace { |
| +class MockApi : public Win32VEHTraits, public ModuleOfInterest { |
| public: |
| - static inline bool WriteDump(EXCEPTION_POINTERS* p) { |
| - g_dump_made = true; |
| - return true; |
| + MockApi() { |
| + Win32VEHTraits::InitializeIgnoredBlocks(); |
| } |
| - static inline void* Register(PVECTORED_EXCEPTION_HANDLER func, |
| - const void* module_start, |
| - const void* module_end) { |
| - InitializeIgnoredBlocks(); |
| - VEHTraitsBase::SetModule(module_start, module_end); |
| - // Return some arbitrary number, expecting to get the same on Unregister() |
| - return reinterpret_cast<void*>(4); |
| - } |
| + MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*)); |
| + MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*()); |
| + MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip, |
| + DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash)); |
| - static inline ULONG Unregister(void* handle) { |
| - EXPECT_EQ(handle, reinterpret_cast<void*>(4)); |
| - return 1; |
| + // Helpers |
| + void SetSEH(const SEHChain& sehchain) { |
| + EXPECT_CALL(*this, RtlpGetExceptionList()) |
| + .Times(testing::AnyNumber()) |
| + .WillRepeatedly(testing::Return(sehchain.chain_)); |
| } |
| - static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, |
| - DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) { |
| - EXPECT_EQ(2, FramesToSkip); |
| - EXPECT_LE(FramesToSkip + FramesToCapture, |
| - VectoredHandlerBase::max_back_trace); |
| - memcpy(BackTrace, g_stack, g_stack_entries * sizeof(BackTrace[0])); |
| - return g_stack_entries; |
| + void SetStack(const StackHelper& s) { |
| + EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _)) |
| + .Times(testing::AnyNumber()) |
| + .WillRepeatedly(StackTraceDump(&s)); |
| } |
| - static inline EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() { |
| - return g_seh_chain; |
| - } |
| + enum {max_back_trace = 15}; |
| +}; |
| +}; // namespace |
| - // Test helpers |
| - // Create fake SEH chain of random filters - with and without our module. |
| - static void SetHaveSEHFilter() { |
| - SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000, |
| - reinterpret_cast<const char*>(g_module_start) + 0x1000, |
| - reinterpret_cast<const char*>(g_module_end) + 0x7127, |
| - NULL); |
| - } |
| +typedef VectoredHandlerT<MockApi> VectoredHandlerMock; |
| +TEST(ChromeFrame, ExceptionReport) { |
| + MockApi api; |
| + VectoredHandlerMock veh(&api); |
| - static void SetNoSEHFilter() { |
| - SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000, |
| - reinterpret_cast<const char*>(g_module_end) + 0x7127, |
| - NULL); |
| - } |
| + // Start address of our module. |
| + char* s = reinterpret_cast<char*>(0x30000000); |
| + char *e = s + 0x10000; |
| + api.SetModule(s, e); |
| - // Create fake stack - with and without our module. |
| - static void SetOnStack() { |
| - SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283, |
| - reinterpret_cast<const char*>(g_module_start) - 0x278361, |
| - reinterpret_cast<const char*>(g_module_start) + 0x9171, |
| - reinterpret_cast<const char*>(g_module_end) + 1231, |
| - NULL); |
| - } |
| + SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL); |
| + SEHChain no_seh(s - 0x1000, e + 0x7127, NULL); |
| + StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL); |
| + StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL); |
| - static void SetNotOnStack() { |
| - SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283, |
| - reinterpret_cast<const char*>(g_module_start) - 0x278361, |
| - reinterpret_cast<const char*>(g_module_end) + 1231, |
| - NULL); |
| - } |
| + char* our_code = s + 0x1111; |
| + char* not_our_code = s - 0x5555; |
| + char* not_our_code2 = e + 0x5555; |
| - // Populate stack array |
| - static void SetStack(const void* p, ...) { |
| - va_list vl; |
| - va_start(vl, p); |
| - g_stack_entries = 0; |
| - for (; p; ++g_stack_entries) { |
| - CHECK(g_stack_entries < arraysize(g_stack)); |
| - g_stack[g_stack_entries] = p; |
| - p = va_arg(vl, const void*); |
| - } |
| - } |
| + // Exception in our code, but we have SEH filter => no dump. |
| + api.SetSEH(have_seh); |
| + api.SetStack(on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(0); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| - static void SetSEHChain(const void* p, ...) { |
| - va_list vl; |
| - va_start(vl, p); |
| - int i = 0; |
| - for (; p; ++i) { |
| - CHECK(i + 1 < arraysize(g_seh_chain)); |
| - g_seh_chain[i].Handler = const_cast<void*>(p); |
| - g_seh_chain[i].Next = &g_seh_chain[i + 1]; |
| - p = va_arg(vl, const void*); |
| - } |
| + // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected |
| + // from its clients", shall not be caught since it's a warning only. |
| + EXPECT_CALL(api, WriteDump(_)).Times(0); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| - g_seh_chain[i].Next = EXCEPTION_CHAIN_END; |
| - } |
| + // Exception, not in our code, we do not have SEH and we are not in stack. |
| + api.SetSEH(no_seh); |
| + api.SetStack(not_on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(0); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| - static EXCEPTION_REGISTRATION_RECORD g_seh_chain[25]; |
| - static const void* g_stack[VectoredHandlerBase::max_back_trace]; |
| - static WORD g_stack_entries; |
| - static bool g_dump_made; |
| -}; |
| + // Exception, not in our code, no SEH, but we are on the stack. |
| + api.SetSEH(no_seh); |
| + api.SetStack(on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(1); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, |
| + not_our_code2))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| -EXCEPTION_REGISTRATION_RECORD EMock::g_seh_chain[25]; |
| -const void* EMock::g_stack[VectoredHandlerBase::max_back_trace]; |
| -WORD EMock::g_stack_entries; |
| -bool EMock::g_dump_made; |
| + // Exception, in our code, no SEH, not on stack (assume FPO screwed us) |
| + api.SetSEH(no_seh); |
| + api.SetStack(not_on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(1); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| -typedef VectoredHandlerT<EMock> VectoredHandlerMock; |
| + // Exception, in IsBadStringPtrA, we are on the stack. |
| + api.SetSEH(no_seh); |
| + api.SetStack(on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(0); |
| + char* ignore_address = reinterpret_cast<char*>(GetProcAddress( |
| + GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10; |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| -class ExPtrsHelper : public _EXCEPTION_POINTERS { |
| - public: |
| - ExPtrsHelper() { |
| - ExceptionRecord = &er_; |
| - ContextRecord = &ctx_; |
| - ZeroMemory(&er_, sizeof(er_)); |
| - ZeroMemory(&ctx_, sizeof(ctx_)); |
| - } |
| + // Exception, in IsBadStringPtrA, we are not on the stack. |
| + api.SetSEH(no_seh); |
| + api.SetStack(not_on_stack); |
| + EXPECT_CALL(api, WriteDump(_)).Times(0); |
| + EXPECT_EQ(ExceptionContinueSearch, |
| + veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address))); |
| + testing::Mock::VerifyAndClearExpectations(&api); |
| +} |
| - void Set(DWORD code, void* address, DWORD flags) { |
| - er_.ExceptionCode = code; |
| - er_.ExceptionAddress = address; |
| - er_.ExceptionFlags = flags; |
| - } |
| +MATCHER_P(ExceptionCodeIs, code, "") { |
| + return (arg->ExceptionRecord->ExceptionCode == code); |
| +} |
| - EXCEPTION_RECORD er_; |
| - CONTEXT ctx_; |
| -}; |
| +void OverflowStack() { |
| + char tmp[1024 * 2048]; |
| + ZeroMemory(tmp, sizeof(tmp)); |
|
amit
2010/01/30 00:52:58
call OverflowStack here again, just to be sure? :)
|
| +} |
| +DWORD WINAPI CrashingThread(PVOID tmp) { |
| + __try { |
| + OverflowStack(); |
| + } __except(EXCEPTION_EXECUTE_HANDLER) { |
| -TEST(ChromeFrame, ExceptionReport) { |
| - char* s = reinterpret_cast<char*>(0x30000000); |
| - char* e = s + 0x10000; |
| - void* handler = VectoredHandlerMock::Register(s, e); |
| - char* our_code = s + 0x1111; |
| - char* not_our_code = s - 0x5555; |
| - char* not_our_code2 = e + 0x5555; |
| + } |
| - ExPtrsHelper ex; |
| - // Exception in our code, but we have SEH filter |
| - ex.Set(STATUS_ACCESS_VIOLATION, our_code, 0); |
| - EMock::SetHaveSEHFilter(); |
| - EMock::SetOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_FALSE(EMock::g_dump_made); |
| + // This will cause STATUS_ACCESS_VIOLATION |
| + __try { |
| + OverflowStack(); |
| + } __except(EXCEPTION_EXECUTE_HANDLER) { |
| - // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected |
| - // from its clients", shall not be caught since it's a warning only. |
| - ex.Set(RPC_E_DISCONNECTED, our_code, 0); |
| - EMock::SetHaveSEHFilter(); |
| - EMock::SetOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_FALSE(EMock::g_dump_made); |
| + } |
| + return 0; |
| +} |
| - // Exception, not in our code, we do not have SEH and we are not in stack. |
| - ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code, 0); |
| - EMock::SetNoSEHFilter(); |
| - EMock::SetNotOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(2, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_FALSE(EMock::g_dump_made); |
| +static VectoredHandlerMock* g_mock_veh = NULL; |
| +static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) { |
| + return g_mock_veh->Handler(exptrs); |
| +} |
| - // Exception, not in our code, no SEH, but we are on the stack. |
| - ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code2, 0); |
| - EMock::SetNoSEHFilter(); |
| - EMock::SetOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(3, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_TRUE(EMock::g_dump_made); |
| - EMock::g_dump_made = false; |
| +TEST(ChromeFrame, TrickyStackOverflow) { |
| + MockApi api; |
| + VectoredHandlerMock veh(&api); |
| + // Start address of our module. |
| + char* s = reinterpret_cast<char*>(0x30000000); |
| + char *e = s + 0x10000; |
| + api.SetModule(s, e); |
| - // Exception, in our code, no SEH, not on stack (assume FPO screwed us) |
| - ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, our_code, 0); |
| - EMock::SetNoSEHFilter(); |
| - EMock::SetNotOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(4, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_TRUE(EMock::g_dump_made); |
| - EMock::g_dump_made = false; |
| + SEHChain no_seh(s - 0x1000, e + 0x7127, NULL); |
| + StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL); |
| + api.SetSEH(no_seh); |
| + api.SetStack(on_stack); |
| - // Exception, in IsBadStringPtrA, we are on the stack. |
| - char* ignore_address = reinterpret_cast<char*>(GetProcAddress( |
| - GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10; |
| - ex.Set(STATUS_ACCESS_VIOLATION, ignore_address + 10, 0); |
| - EMock::SetNoSEHFilter(); |
| - EMock::SetOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(5, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_FALSE(EMock::g_dump_made); |
| - EMock::g_dump_made = false; |
| + EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1); |
| - // Exception, in IsBadStringPtrA, we are not in stack. |
| - ex.Set(STATUS_ACCESS_VIOLATION, ignore_address + 10, 0); |
| - EMock::SetNoSEHFilter(); |
| - EMock::SetNotOnStack(); |
| - EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); |
| - EXPECT_EQ(6, VectoredHandlerMock::g_exceptions_seen); |
| - EXPECT_FALSE(EMock::g_dump_made); |
| - EMock::g_dump_made = false; |
| + g_mock_veh = &veh; |
| + void* id = ::AddVectoredExceptionHandler(FALSE, VEH); |
| - VectoredHandlerMock::Unregister(); |
| + DWORD tid; |
| + HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid); |
| + ::WaitForSingleObject(h, INFINITE); |
| + ::CloseHandle(h); |
| + |
| + EXPECT_EQ(2, veh.get_exceptions_seen()); |
| + ::RemoveVectoredExceptionHandler(id); |
| + g_mock_veh = NULL; |
| } |