Chromium Code Reviews| 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 db558cde3bb9b262dd1753fd2456b811da91ed13..cbba0d3aec45ff3411ababf9a9933e26cbbf4313 100644 |
| --- a/snapshot/win/exception_snapshot_win_test.cc |
| +++ b/snapshot/win/exception_snapshot_win_test.cc |
| @@ -16,11 +16,14 @@ |
| #include <string> |
| +#include "base/files/file_path.h" |
| #include "base/strings/stringprintf.h" |
| +#include "base/strings/string16.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "client/crashpad_client.h" |
| #include "gtest/gtest.h" |
| #include "snapshot/win/process_snapshot_win.h" |
| +#include "test/paths.h" |
| #include "test/win/win_child_process.h" |
| #include "util/thread/thread.h" |
| #include "util/win/exception_handler_server.h" |
| @@ -46,56 +49,49 @@ HANDLE DuplicateEvent(HANDLE process, HANDLE event) { |
| return nullptr; |
| } |
| -class ExceptionSnapshotWinTest : public testing::Test { |
| +class Delegate : public ExceptionHandlerServer::Delegate { |
| public: |
| - class Delegate : public ExceptionHandlerServer::Delegate { |
| - public: |
| - Delegate(HANDLE server_ready, HANDLE completed_test_event) |
| - : server_ready_(server_ready), |
| - completed_test_event_(completed_test_event), |
| - break_near_(nullptr) {} |
| - ~Delegate() override {} |
| - |
| - void set_break_near(void* break_near) { break_near_ = break_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); |
| - |
| - // Confirm the exception record was read correctly. |
| - EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); |
| - EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); |
| - |
| - // 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(). |
| - const uint64_t kAllowedOffset = 64; |
| - EXPECT_GT(snapshot.Exception()->ExceptionAddress(), |
| - reinterpret_cast<uint64_t>(break_near_)); |
| - EXPECT_LT(snapshot.Exception()->ExceptionAddress(), |
| - reinterpret_cast<uint64_t>(break_near_) + kAllowedOffset); |
| - |
| - SetEvent(completed_test_event_); |
| - |
| - return snapshot.Exception()->Exception(); |
| - } |
| - |
| - private: |
| - HANDLE server_ready_; // weak |
| - HANDLE completed_test_event_; // weak |
| - void* break_near_; |
| - |
| - DISALLOW_COPY_AND_ASSIGN(Delegate); |
| - }; |
| + Delegate(HANDLE server_ready, HANDLE completed_test_event) |
| + : server_ready_(server_ready), |
| + completed_test_event_(completed_test_event), |
| + break_near_(0) {} |
| + ~Delegate() override {} |
| + |
| + void set_break_near(uint64_t break_near) { break_near_ = break_near; } |
|
Mark Mentovai
2015/09/18 15:44:06
WinVMAddress for the argument and the member.
scottmg
2015/09/18 19:45:26
Done.
|
| + |
| + 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); |
| + |
| + // Confirm the exception record was read correctly. |
| + EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); |
| + EXPECT_EQ(snapshot.Exception()->Exception(), EXCEPTION_BREAKPOINT); |
| + |
| + // 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(). |
| + const uint64_t kAllowedOffset = 64; |
| + EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); |
| + EXPECT_LT(snapshot.Exception()->ExceptionAddress(), |
| + break_near_ + kAllowedOffset); |
| + |
| + SetEvent(completed_test_event_); |
| + |
| + return snapshot.Exception()->Exception(); |
| + } |
| private: |
| - ScopedKernelHANDLE exception_happened_; |
| + HANDLE server_ready_; // weak |
| + HANDLE completed_test_event_; // weak |
| + uint64_t break_near_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(Delegate); |
| }; |
| // Runs the ExceptionHandlerServer on a background thread. |
| @@ -136,51 +132,7 @@ class ScopedStopServerAndJoinThread { |
| DISALLOW_COPY_AND_ASSIGN(ScopedStopServerAndJoinThread); |
| }; |
| -std::string ReadString(FileHandle handle) { |
| - size_t length = 0; |
| - EXPECT_TRUE(LoggingReadFile(handle, &length, sizeof(length))); |
| - scoped_ptr<char[]> buffer(new char[length]); |
| - EXPECT_TRUE(LoggingReadFile(handle, &buffer[0], length)); |
| - return std::string(&buffer[0], length); |
| -} |
| - |
| -void WriteString(FileHandle handle, const std::string& str) { |
| - size_t length = str.size(); |
| - EXPECT_TRUE(LoggingWriteFile(handle, &length, sizeof(length))); |
| - EXPECT_TRUE(LoggingWriteFile(handle, &str[0], length)); |
| -} |
| - |
| -__declspec(noinline) void* CurrentAddress() { |
| - return _ReturnAddress(); |
| -} |
| - |
| -class CrashingChildProcess final : public WinChildProcess { |
| - public: |
| - CrashingChildProcess() : WinChildProcess() {} |
| - ~CrashingChildProcess() {} |
| - |
| - private: |
| - int Run() override { |
| - std::string pipe_name = ReadString(ReadPipeHandle()); |
| - CrashpadClient client; |
| - EXPECT_TRUE(client.SetHandler(pipe_name)); |
| - EXPECT_TRUE(client.UseHandler()); |
| - // Save the address where we're about to crash so the exception handler can |
| - // verify it's in approximately the right location (with a bit of fudge for |
| - // the code between here and the __debugbreak()). |
| - void* break_address = CurrentAddress(); |
| - LoggingWriteFile(WritePipeHandle(), &break_address, sizeof(break_address)); |
| - __debugbreak(); |
| - return 0; |
| - }; |
| -}; |
| - |
| -TEST_F(ExceptionSnapshotWinTest, ChildCrash) { |
| - // Spawn a child process that will immediately crash (once we let it |
| - // run below by telling it what to connect to). |
| - WinChildProcess::EntryPoint<CrashingChildProcess>(); |
| - scoped_ptr<WinChildProcess::Handles> handle = WinChildProcess::Launch(); |
| - |
| +void TestCrashingChild(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()); |
| @@ -196,19 +148,74 @@ TEST_F(ExceptionSnapshotWinTest, ChildCrash) { |
| &exception_handler_server, &server_thread); |
| WaitForSingleObject(server_ready.get(), INFINITE); |
| - // Allow the child to continue and tell it where to connect to. |
| - WriteString(handle->write.get(), pipe_name); |
| + |
| + // 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"_crashing_child.exe") |
| + .value(); |
| + |
| + // Create a pipe for the stdout of the child. |
| + SECURITY_ATTRIBUTES security_attributes = {0}; |
| + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
| + security_attributes.bInheritHandle = true; |
| + HANDLE stdout_read; |
| + HANDLE stdout_write; |
| + ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)); |
| + ScopedFileHANDLE read_handle(stdout_read); |
| + ScopedFileHANDLE write_handle(stdout_write); |
| + ASSERT_TRUE(SetHandleInformation(read_handle.get(), HANDLE_FLAG_INHERIT, 0)); |
| + |
| + std::wstring command_line = |
| + child_test_executable + L" " + base::UTF8ToUTF16(pipe_name); |
| + STARTUPINFO startup_info = {0}; |
| + startup_info.cb = sizeof(startup_info); |
| + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); |
| + startup_info.hStdOutput = write_handle.get(); |
| + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); |
| + startup_info.dwFlags = STARTF_USESTDHANDLES; |
| + PROCESS_INFORMATION process_information; |
| + ASSERT_TRUE(CreateProcess(child_test_executable.c_str(), |
| + &command_line[0], |
| + nullptr, |
| + nullptr, |
| + true, |
| + 0, |
| + nullptr, |
| + nullptr, |
| + &startup_info, |
| + &process_information)); |
| + // Take ownership of the two process handles returned. |
| + ScopedKernelHANDLE process_main_thread_handle(process_information.hThread); |
| + ScopedKernelHANDLE process_handle(process_information.hProcess); |
| // The child tells us (approximately) where it will crash. |
| - void* break_near_address; |
| + uint64_t break_near_address; |
|
Mark Mentovai
2015/09/18 15:44:06
And this.
scottmg
2015/09/18 19:45:26
Done.
|
| LoggingReadFile( |
| - handle->read.get(), &break_near_address, sizeof(break_near_address)); |
| + read_handle.get(), &break_near_address, sizeof(break_near_address)); |
| delegate.set_break_near(break_near_address); |
| // Wait for the child to crash and the exception information to be validated. |
| WaitForSingleObject(completed.get(), INFINITE); |
| } |
| +TEST(ExceptionSnapshotWinTest, ChildCrash) { |
| + TestCrashingChild(FILE_PATH_LITERAL(".")); |
| +} |
| + |
| +#if defined(ARCH_CPU_64_BITS) |
| +TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { |
| +#ifndef NDEBUG |
| + TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Debug")); |
| +#else |
| + TestCrashingChild(FILE_PATH_LITERAL("..\\..\\out\\Release")); |
| +#endif |
| +} |
| +#endif // ARCH_CPU_64_BITS |
| + |
| } // namespace |
| } // namespace test |
| } // namespace crashpad |