| 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..7d7f18ebfda7b5123b5f429d3a8832c316d809fb 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(WinVMAddress 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(), 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
|
| + WinVMAddress 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;
|
| + WinVMAddress break_near_address;
|
| 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
|
|
|