Index: snapshot/win/exception_snapshot_win_test.cc |
diff --git a/snapshot/win/exception_snapshot_win_test.cc b/snapshot/win/exception_snapshot_win_test.cc |
index c074eb5499ab6f0319484dd7174e7972faccadc5..4108a575978f92dc10f2b325b64fb0a078aef71b 100644 |
--- a/snapshot/win/exception_snapshot_win_test.cc |
+++ b/snapshot/win/exception_snapshot_win_test.cc |
@@ -36,27 +36,51 @@ namespace crashpad { |
namespace test { |
namespace { |
-HANDLE DuplicateEvent(HANDLE process, HANDLE event) { |
- HANDLE handle; |
- if (DuplicateHandle(GetCurrentProcess(), |
- event, |
- process, |
- &handle, |
- SYNCHRONIZE | EVENT_MODIFY_STATE, |
- false, |
- 0)) { |
- return handle; |
+// Runs the ExceptionHandlerServer on a background thread. |
+class RunServerThread : public Thread { |
+ public: |
+ // Instantiates a thread which will invoke server->Run(delegate, pipe_name); |
+ RunServerThread(ExceptionHandlerServer* server, |
+ ExceptionHandlerServer::Delegate* delegate, |
+ const std::string& pipe_name) |
+ : server_(server), delegate_(delegate), pipe_name_(pipe_name) {} |
+ ~RunServerThread() override {} |
+ |
+ private: |
+ // Thread: |
+ void ThreadMain() override { server_->Run(delegate_, pipe_name_); } |
+ |
+ ExceptionHandlerServer* server_; |
+ ExceptionHandlerServer::Delegate* delegate_; |
+ std::string pipe_name_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RunServerThread); |
+}; |
+ |
+// During destruction, ensures that the server is stopped and the background |
+// thread joined. |
+class ScopedStopServerAndJoinThread { |
+ public: |
+ ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread) |
+ : server_(server), thread_(thread) {} |
+ ~ScopedStopServerAndJoinThread() { |
+ server_->Stop(); |
+ thread_->Join(); |
} |
- return nullptr; |
-} |
-class Delegate : public ExceptionHandlerServer::Delegate { |
+ private: |
+ ExceptionHandlerServer* server_; |
+ Thread* thread_; |
+ DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); |
+}; |
+ |
+class CrashingDelegate : public ExceptionHandlerServer::Delegate { |
public: |
- Delegate(HANDLE server_ready, HANDLE completed_test_event) |
+ CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event) |
: server_ready_(server_ready), |
completed_test_event_(completed_test_event), |
break_near_(0) {} |
- ~Delegate() override {} |
+ ~CrashingDelegate() override {} |
void set_break_near(WinVMAddress break_near) { break_near_ = break_near; } |
@@ -76,7 +100,7 @@ class Delegate : public ExceptionHandlerServer::Delegate { |
// Verify the exception happened at the expected location with a bit of |
// slop space to allow for reading the current PC before the exception |
- // happens. See CrashingChildProcess::Run(). |
+ // happens. See TestCrashingChild(). |
const uint64_t kAllowedOffset = 64; |
EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); |
EXPECT_LT(snapshot.Exception()->ExceptionAddress(), |
@@ -92,45 +116,7 @@ class Delegate : public ExceptionHandlerServer::Delegate { |
HANDLE completed_test_event_; // weak |
WinVMAddress break_near_; |
- DISALLOW_COPY_AND_ASSIGN(Delegate); |
-}; |
- |
-// Runs the ExceptionHandlerServer on a background thread. |
-class RunServerThread : public Thread { |
- public: |
- // Instantiates a thread which will invoke server->Run(delegate, pipe_name); |
- RunServerThread(ExceptionHandlerServer* server, |
- ExceptionHandlerServer::Delegate* delegate, |
- const std::string& pipe_name) |
- : server_(server), delegate_(delegate), pipe_name_(pipe_name) {} |
- ~RunServerThread() override {} |
- |
- private: |
- // Thread: |
- void ThreadMain() override { server_->Run(delegate_, pipe_name_); } |
- |
- ExceptionHandlerServer* server_; |
- ExceptionHandlerServer::Delegate* delegate_; |
- std::string pipe_name_; |
- |
- DISALLOW_COPY_AND_ASSIGN(RunServerThread); |
-}; |
- |
-// During destruction, ensures that the server is stopped and the background |
-// thread joined. |
-class ScopedStopServerAndJoinThread { |
- public: |
- ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread) |
- : server_(server), thread_(thread) {} |
- ~ScopedStopServerAndJoinThread() { |
- server_->Stop(); |
- thread_->Join(); |
- } |
- |
- private: |
- ExceptionHandlerServer* server_; |
- Thread* thread_; |
- DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); |
+ DISALLOW_COPY_AND_ASSIGN(CrashingDelegate); |
}; |
void TestCrashingChild(const base::string16& directory_modification) { |
@@ -139,7 +125,7 @@ void TestCrashingChild(const base::string16& directory_modification) { |
base::StringPrintf("%08x", GetCurrentProcessId()); |
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); |
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); |
- Delegate delegate(server_ready.get(), completed.get()); |
+ CrashingDelegate delegate(server_ready.get(), completed.get()); |
ExceptionHandlerServer exception_handler_server; |
RunServerThread server_thread( |
@@ -186,6 +172,104 @@ TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { |
} |
#endif // ARCH_CPU_64_BITS |
+class SimulateDelegate : public ExceptionHandlerServer::Delegate { |
+ public: |
+ SimulateDelegate(HANDLE server_ready, HANDLE completed_test_event) |
+ : server_ready_(server_ready), |
+ completed_test_event_(completed_test_event), |
+ dump_near_(0) {} |
+ ~SimulateDelegate() override {} |
+ |
+ void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; } |
+ |
+ void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } |
+ |
+ unsigned int ExceptionHandlerServerException( |
+ HANDLE process, |
+ WinVMAddress exception_information_address) override { |
+ ScopedProcessSuspend suspend(process); |
+ ProcessSnapshotWin snapshot; |
+ snapshot.Initialize(process, ProcessSuspensionState::kSuspended); |
+ snapshot.InitializeException(exception_information_address); |
+ EXPECT_TRUE(snapshot.Exception()); |
+ EXPECT_EQ(0, snapshot.Exception()->Exception()); |
+ EXPECT_EQ(0, snapshot.Exception()->ExceptionAddress()); |
+ |
+ // Verify the dump was captured at the expected location with some slop |
+ // space. |
+ const uint64_t kAllowedOffset = 64; |
+ EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), |
+ dump_near_); |
+ EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), |
+ dump_near_ + kAllowedOffset); |
+ |
+ SetEvent(completed_test_event_); |
+ |
+ return 0; |
+ } |
+ |
+ private: |
+ HANDLE server_ready_; // weak |
+ HANDLE completed_test_event_; // weak |
+ WinVMAddress dump_near_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SimulateDelegate); |
+}; |
+ |
+void TestDumpWithoutCrashingChild( |
+ const base::string16& directory_modification) { |
+ // Set up the registration server on a background thread. |
+ std::string pipe_name = "\\\\.\\pipe\\handler_test_pipe_" + |
+ base::StringPrintf("%08x", GetCurrentProcessId()); |
+ ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); |
+ ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); |
+ SimulateDelegate delegate(server_ready.get(), completed.get()); |
+ |
+ ExceptionHandlerServer exception_handler_server; |
+ RunServerThread server_thread( |
+ &exception_handler_server, &delegate, pipe_name); |
+ server_thread.Start(); |
+ ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( |
+ &exception_handler_server, &server_thread); |
+ |
+ WaitForSingleObject(server_ready.get(), INFINITE); |
+ |
+ // Spawn a child process, passing it the pipe name to connect to. |
+ base::FilePath test_executable = Paths::Executable(); |
+ std::wstring child_test_executable = |
+ test_executable.DirName() |
+ .Append(directory_modification) |
+ .Append(test_executable.BaseName().RemoveFinalExtension().value() + |
+ L"_dump_without_crashing.exe") |
+ .value(); |
+ ChildLauncher child(child_test_executable, base::UTF8ToUTF16(pipe_name)); |
+ child.Start(); |
+ |
+ // The child tells us (approximately) where it will capture a dump. |
+ WinVMAddress dump_near_address; |
+ LoggingReadFile(child.stdout_read_handle(), |
+ &dump_near_address, |
+ sizeof(dump_near_address)); |
+ delegate.set_dump_near(dump_near_address); |
+ |
+ // Wait for the child to crash and the exception information to be validated. |
+ WaitForSingleObject(completed.get(), INFINITE); |
+} |
+ |
+TEST(SimulateCrash, ChildDumpWithoutCrashing) { |
+ TestDumpWithoutCrashingChild(FILE_PATH_LITERAL(".")); |
+} |
+ |
+#if defined(ARCH_CPU_64_BITS) |
+TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) { |
+#ifndef NDEBUG |
+ TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); |
+#else |
+ TestDumpWithoutCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); |
+#endif |
+} |
+#endif // ARCH_CPU_64_BITS |
+ |
} // namespace |
} // namespace test |
} // namespace crashpad |