crashpad/snapshot/win/exception_snapshot_win_test.cc
Peter Boström 1aa478d161 Remove DISALLOW_* macros in crashpad
This change was partially scripted and partially done manually with vim
regex + manually placing the deleted constructors.

The script change looked for destructors in the public: section of a
class, if that existed the deleted constructors would go before the
destructor.

For manual placement I looked for any constructor in the public: section
of the corresponding class. If there wasn't one, then it would ideally
have gone as the first entry except below enums, classes and typedefs.
This may not have been perfect, but is hopefully good enough. Fingers
crossed.

#include "base/macros.h" is removed from files that don't use
ignore_result, which is the only other thing defined in base/macros.h.

Bug: chromium:1010217
Change-Id: I099526255a40b1ac1264904b4ece2f3f503c9418
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3171034
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Peter Boström <pbos@chromium.org>
2021-09-21 15:09:44 +00:00

322 lines
11 KiB
C++

// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "snapshot/win/exception_snapshot_win.h"
#include <string>
#include "base/files/file_path.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/errors.h"
#include "test/test_paths.h"
#include "test/win/child_launcher.h"
#include "util/file/file_io.h"
#include "util/thread/thread.h"
#include "util/win/exception_handler_server.h"
#include "util/win/registration_protocol_win.h"
#include "util/win/scoped_handle.h"
#include "util/win/scoped_process_suspend.h"
namespace crashpad {
namespace test {
namespace {
// Runs the ExceptionHandlerServer on a background thread.
class RunServerThread : public Thread {
public:
// Instantiates a thread which will invoke server->Run(delegate);
RunServerThread(ExceptionHandlerServer* server,
ExceptionHandlerServer::Delegate* delegate)
: server_(server), delegate_(delegate) {}
RunServerThread(const RunServerThread&) = delete;
RunServerThread& operator=(const RunServerThread&) = delete;
~RunServerThread() override {}
private:
// Thread:
void ThreadMain() override { server_->Run(delegate_); }
ExceptionHandlerServer* server_;
ExceptionHandlerServer::Delegate* delegate_;
};
// 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(const ScopedStopServerAndJoinThread&) = delete;
ScopedStopServerAndJoinThread& operator=(
const ScopedStopServerAndJoinThread&) = delete;
~ScopedStopServerAndJoinThread() {
server_->Stop();
thread_->Join();
}
private:
ExceptionHandlerServer* server_;
Thread* thread_;
};
class CrashingDelegate : public ExceptionHandlerServer::Delegate {
public:
CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event)
: server_ready_(server_ready),
completed_test_event_(completed_test_event),
break_near_(0) {}
CrashingDelegate(const CrashingDelegate&) = delete;
CrashingDelegate& operator=(const CrashingDelegate&) = delete;
~CrashingDelegate() {}
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,
WinVMAddress debug_critical_section_address) override {
ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot;
snapshot.Initialize(process,
ProcessSuspensionState::kSuspended,
exception_information_address,
debug_critical_section_address);
// Confirm the exception record was read correctly.
EXPECT_NE(snapshot.Exception()->ThreadID(), 0u);
EXPECT_EQ(EXCEPTION_BREAKPOINT, snapshot.Exception()->Exception());
// 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 TestCrashingChild().
#if !defined(NDEBUG)
// Debug build is likely not optimized and contains more instructions.
constexpr uint64_t kAllowedOffset = 200;
#else
constexpr uint64_t kAllowedOffset = 100;
#endif
EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_);
EXPECT_LT(snapshot.Exception()->ExceptionAddress(),
break_near_ + kAllowedOffset);
SetEvent(completed_test_event_);
return snapshot.Exception()->Exception();
}
private:
HANDLE server_ready_; // weak
HANDLE completed_test_event_; // weak
WinVMAddress break_near_;
};
void TestCrashingChild(TestPaths::Architecture architecture) {
// Set up the registration server on a background thread.
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent");
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
ASSERT_TRUE(completed.is_valid()) << ErrorMessage("CreateEvent");
CrashingDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server(true);
std::wstring pipe_name(L"\\\\.\\pipe\\test_name");
exception_handler_server.SetPipeName(pipe_name);
RunServerThread server_thread(&exception_handler_server, &delegate);
server_thread.Start();
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
&exception_handler_server, &server_thread);
EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0)
<< ErrorMessage("WaitForSingleObject");
// Spawn a child process, passing it the pipe name to connect to.
base::FilePath child_test_executable =
TestPaths::BuildArtifact(L"snapshot",
L"crashing_child",
TestPaths::FileType::kExecutable,
architecture);
ChildLauncher child(child_test_executable, pipe_name);
ASSERT_NO_FATAL_FAILURE(child.Start());
// The child tells us (approximately) where it will crash.
WinVMAddress break_near_address;
LoggingReadFileExactly(child.stdout_read_handle(),
&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.
EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0)
<< ErrorMessage("WaitForSingleObject");
EXPECT_EQ(child.WaitForExit(), EXCEPTION_BREAKPOINT);
}
#if defined(ADDRESS_SANITIZER)
// https://crbug.com/845011
#define MAYBE_ChildCrash DISABLED_ChildCrash
#else
#define MAYBE_ChildCrash ChildCrash
#endif
TEST(ExceptionSnapshotWinTest, MAYBE_ChildCrash) {
TestCrashingChild(TestPaths::Architecture::kDefault);
}
#if defined(ARCH_CPU_64_BITS)
TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
GTEST_SKIP();
}
TestCrashingChild(TestPaths::Architecture::k32Bit);
}
#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(const SimulateDelegate&) = delete;
SimulateDelegate& operator=(const SimulateDelegate&) = delete;
~SimulateDelegate() {}
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,
WinVMAddress debug_critical_section_address) override {
ScopedProcessSuspend suspend(process);
ProcessSnapshotWin snapshot;
snapshot.Initialize(process,
ProcessSuspensionState::kSuspended,
exception_information_address,
debug_critical_section_address);
EXPECT_TRUE(snapshot.Exception());
EXPECT_EQ(snapshot.Exception()->Exception(), 0x517a7edu);
// Verify the dump was captured at the expected location with some slop
// space.
#if defined(ADDRESS_SANITIZER)
// ASan instrumentation inserts more instructions between the expected
// location and what's reported. https://crbug.com/845011.
constexpr uint64_t kAllowedOffset = 500;
#elif !defined(NDEBUG)
// Debug build is likely not optimized and contains more instructions.
constexpr uint64_t kAllowedOffset = 200;
#else
constexpr uint64_t kAllowedOffset = 100;
#endif
EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(),
dump_near_);
EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(),
dump_near_ + kAllowedOffset);
EXPECT_EQ(snapshot.Exception()->ExceptionAddress(),
snapshot.Exception()->Context()->InstructionPointer());
SetEvent(completed_test_event_);
return 0;
}
private:
HANDLE server_ready_; // weak
HANDLE completed_test_event_; // weak
WinVMAddress dump_near_;
};
void TestDumpWithoutCrashingChild(TestPaths::Architecture architecture) {
// Set up the registration server on a background thread.
ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr));
ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent");
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
ASSERT_TRUE(completed.is_valid()) << ErrorMessage("CreateEvent");
SimulateDelegate delegate(server_ready.get(), completed.get());
ExceptionHandlerServer exception_handler_server(true);
std::wstring pipe_name(L"\\\\.\\pipe\\test_name");
exception_handler_server.SetPipeName(pipe_name);
RunServerThread server_thread(&exception_handler_server, &delegate);
server_thread.Start();
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
&exception_handler_server, &server_thread);
EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0)
<< ErrorMessage("WaitForSingleObject");
// Spawn a child process, passing it the pipe name to connect to.
base::FilePath child_test_executable =
TestPaths::BuildArtifact(L"snapshot",
L"dump_without_crashing",
TestPaths::FileType::kExecutable,
architecture);
ChildLauncher child(child_test_executable, pipe_name);
ASSERT_NO_FATAL_FAILURE(child.Start());
// The child tells us (approximately) where it will capture a dump.
WinVMAddress dump_near_address;
LoggingReadFileExactly(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.
EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0)
<< ErrorMessage("WaitForSingleObject");
EXPECT_EQ(child.WaitForExit(), 0u);
}
#if defined(ADDRESS_SANITIZER)
// https://crbug.com/845011
#define MAYBE_ChildDumpWithoutCrashing DISABLED_ChildDumpWithoutCrashing
#else
#define MAYBE_ChildDumpWithoutCrashing ChildDumpWithoutCrashing
#endif
TEST(SimulateCrash, MAYBE_ChildDumpWithoutCrashing) {
TestDumpWithoutCrashingChild(TestPaths::Architecture::kDefault);
}
#if defined(ARCH_CPU_64_BITS)
TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) {
if (!TestPaths::Has32BitBuildArtifacts()) {
GTEST_SKIP();
}
TestDumpWithoutCrashingChild(TestPaths::Architecture::k32Bit);
}
#endif // ARCH_CPU_64_BITS
} // namespace
} // namespace test
} // namespace crashpad