| 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
|
|
|