mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 10:43:46 +00:00
win: Fix incorrect thread suspend count due to ScopedProcessSuspend
After https://codereview.chromium.org/1303173011/, the thread suspend count would be one too large because the count is adjusted when the process is suspended. Counteract this by passing in whether the process is suspended or not so that the thread's suspension count can be adjusted. Add a test to sanity-check thread suspend count. R=mark@chromium.org Review URL: https://codereview.chromium.org/1326443007 .
This commit is contained in:
parent
5111a1823f
commit
d7f90b45b6
@ -48,7 +48,8 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
|||||||
ScopedProcessSuspend suspend(process);
|
ScopedProcessSuspend suspend(process);
|
||||||
|
|
||||||
ProcessSnapshotWin process_snapshot;
|
ProcessSnapshotWin process_snapshot;
|
||||||
if (!process_snapshot.Initialize(process)) {
|
if (!process_snapshot.Initialize(process,
|
||||||
|
ProcessSuspensionState::kSuspended)) {
|
||||||
LOG(WARNING) << "ProcessSnapshotWin::Initialize failed";
|
LOG(WARNING) << "ProcessSnapshotWin::Initialize failed";
|
||||||
return kFailedTerminationCode;
|
return kFailedTerminationCode;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,8 @@ TEST(CrashpadInfoClientOptions, OneModule) {
|
|||||||
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
ProcessSnapshotWin process_snapshot;
|
ProcessSnapshotWin process_snapshot;
|
||||||
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
#else
|
#else
|
||||||
#error Port.
|
#error Port.
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
@ -188,7 +189,8 @@ TEST(CrashpadInfoClientOptions, TwoModules) {
|
|||||||
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
ProcessSnapshotWin process_snapshot;
|
ProcessSnapshotWin process_snapshot;
|
||||||
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_snapshot.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
#else
|
#else
|
||||||
#error Port.
|
#error Port.
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "util/win/exception_handler_server.h"
|
#include "util/win/exception_handler_server.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
#include "util/win/scoped_handle.h"
|
#include "util/win/scoped_handle.h"
|
||||||
|
#include "util/win/scoped_process_suspend.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
@ -62,8 +63,9 @@ class ExceptionSnapshotWinTest : public testing::Test {
|
|||||||
unsigned int ExceptionHandlerServerException(
|
unsigned int ExceptionHandlerServerException(
|
||||||
HANDLE process,
|
HANDLE process,
|
||||||
WinVMAddress exception_information_address) override {
|
WinVMAddress exception_information_address) override {
|
||||||
|
ScopedProcessSuspend suspend(process);
|
||||||
ProcessSnapshotWin snapshot;
|
ProcessSnapshotWin snapshot;
|
||||||
snapshot.Initialize(process);
|
snapshot.Initialize(process, ProcessSuspensionState::kSuspended);
|
||||||
snapshot.InitializeException(exception_information_address);
|
snapshot.InitializeException(exception_information_address);
|
||||||
|
|
||||||
// Confirm the exception record was read correctly.
|
// Confirm the exception record was read correctly.
|
||||||
|
@ -55,7 +55,8 @@ class TestPEImageAnnotationsReader final : public WinMultiprocess {
|
|||||||
|
|
||||||
void WinMultiprocessParent() override {
|
void WinMultiprocessParent() override {
|
||||||
ProcessReaderWin process_reader;
|
ProcessReaderWin process_reader;
|
||||||
ASSERT_TRUE(process_reader.Initialize(ChildProcess()));
|
ASSERT_TRUE(process_reader.Initialize(ChildProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
// Wait for the child process to indicate that it's done setting up its
|
// Wait for the child process to indicate that it's done setting up its
|
||||||
// annotations via the CrashpadInfo interface.
|
// annotations via the CrashpadInfo interface.
|
||||||
|
@ -28,7 +28,8 @@ namespace {
|
|||||||
TEST(PEImageReader, DebugDirectory) {
|
TEST(PEImageReader, DebugDirectory) {
|
||||||
PEImageReader pe_image_reader;
|
PEImageReader pe_image_reader;
|
||||||
ProcessReaderWin process_reader;
|
ProcessReaderWin process_reader;
|
||||||
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
|
HMODULE self = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||||
MODULEINFO module_info;
|
MODULEINFO module_info;
|
||||||
ASSERT_TRUE(GetModuleInformation(
|
ASSERT_TRUE(GetModuleInformation(
|
||||||
|
@ -117,8 +117,8 @@ template <class Traits>
|
|||||||
void FillThreadContextAndSuspendCount(
|
void FillThreadContextAndSuspendCount(
|
||||||
const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>&
|
const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>&
|
||||||
thread_info,
|
thread_info,
|
||||||
ProcessReaderWin::Thread* thread) {
|
ProcessReaderWin::Thread* thread,
|
||||||
|
ProcessSuspensionState suspension_state) {
|
||||||
// Don't suspend the thread if it's this thread. This is really only for test
|
// Don't suspend the thread if it's this thread. This is really only for test
|
||||||
// binaries, as we won't be walking ourselves, in general.
|
// binaries, as we won't be walking ourselves, in general.
|
||||||
bool is_current_thread = thread_info.ClientId.UniqueThread ==
|
bool is_current_thread = thread_info.ClientId.UniqueThread ==
|
||||||
@ -130,6 +130,7 @@ void FillThreadContextAndSuspendCount(
|
|||||||
// TODO(scottmg): Handle cross-bitness in this function.
|
// TODO(scottmg): Handle cross-bitness in this function.
|
||||||
|
|
||||||
if (is_current_thread) {
|
if (is_current_thread) {
|
||||||
|
DCHECK(suspension_state == ProcessSuspensionState::kRunning);
|
||||||
thread->suspend_count = 0;
|
thread->suspend_count = 0;
|
||||||
RtlCaptureContext(&thread->context);
|
RtlCaptureContext(&thread->context);
|
||||||
} else {
|
} else {
|
||||||
@ -138,7 +139,11 @@ void FillThreadContextAndSuspendCount(
|
|||||||
PLOG(ERROR) << "SuspendThread failed";
|
PLOG(ERROR) << "SuspendThread failed";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
thread->suspend_count = previous_suspend_count;
|
DCHECK(previous_suspend_count > 0 ||
|
||||||
|
suspension_state == ProcessSuspensionState::kRunning);
|
||||||
|
thread->suspend_count =
|
||||||
|
previous_suspend_count -
|
||||||
|
(suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
|
||||||
|
|
||||||
memset(&thread->context, 0, sizeof(thread->context));
|
memset(&thread->context, 0, sizeof(thread->context));
|
||||||
thread->context.ContextFlags = CONTEXT_ALL;
|
thread->context.ContextFlags = CONTEXT_ALL;
|
||||||
@ -171,6 +176,7 @@ ProcessReaderWin::ProcessReaderWin()
|
|||||||
process_info_(),
|
process_info_(),
|
||||||
threads_(),
|
threads_(),
|
||||||
modules_(),
|
modules_(),
|
||||||
|
suspension_state_(),
|
||||||
initialized_threads_(false),
|
initialized_threads_(false),
|
||||||
initialized_() {
|
initialized_() {
|
||||||
}
|
}
|
||||||
@ -178,10 +184,12 @@ ProcessReaderWin::ProcessReaderWin()
|
|||||||
ProcessReaderWin::~ProcessReaderWin() {
|
ProcessReaderWin::~ProcessReaderWin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessReaderWin::Initialize(HANDLE process) {
|
bool ProcessReaderWin::Initialize(HANDLE process,
|
||||||
|
ProcessSuspensionState suspension_state) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
process_ = process;
|
process_ = process;
|
||||||
|
suspension_state_ = suspension_state;
|
||||||
process_info_.Initialize(process);
|
process_info_.Initialize(process);
|
||||||
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
@ -254,7 +262,7 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
|
|||||||
Thread thread;
|
Thread thread;
|
||||||
thread.id = thread_info.ClientId.UniqueThread;
|
thread.id = thread_info.ClientId.UniqueThread;
|
||||||
|
|
||||||
FillThreadContextAndSuspendCount(thread_info, &thread);
|
FillThreadContextAndSuspendCount(thread_info, &thread, suspension_state_);
|
||||||
|
|
||||||
// TODO(scottmg): I believe we could reverse engineer the PriorityClass from
|
// TODO(scottmg): I believe we could reverse engineer the PriorityClass from
|
||||||
// the Priority, BasePriority, and
|
// the Priority, BasePriority, and
|
||||||
|
@ -26,6 +26,15 @@
|
|||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief State of process being read by ProcessReaderWin.
|
||||||
|
enum class ProcessSuspensionState : bool {
|
||||||
|
//! \brief The process has not been suspended.
|
||||||
|
kRunning,
|
||||||
|
|
||||||
|
//! \brief The process is suspended.
|
||||||
|
kSuspended,
|
||||||
|
};
|
||||||
|
|
||||||
//! \brief Accesses information about another process, identified by a `HANDLE`.
|
//! \brief Accesses information about another process, identified by a `HANDLE`.
|
||||||
class ProcessReaderWin {
|
class ProcessReaderWin {
|
||||||
public:
|
public:
|
||||||
@ -52,11 +61,17 @@ class ProcessReaderWin {
|
|||||||
//!
|
//!
|
||||||
//! \param[in] process Process handle, must have `PROCESS_QUERY_INFORMATION`,
|
//! \param[in] process Process handle, must have `PROCESS_QUERY_INFORMATION`,
|
||||||
//! `PROCESS_VM_READ`, and `PROCESS_DUP_HANDLE` access.
|
//! `PROCESS_VM_READ`, and `PROCESS_DUP_HANDLE` access.
|
||||||
|
//! \param[in] suspension_state Whether \a process has already been suspended
|
||||||
|
//! by the caller. Typically, this will be
|
||||||
|
//! ProcessSuspensionState::kSuspended, except for testing uses and where
|
||||||
|
//! the reader is reading itself.
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, indicating that this object will respond
|
//! \return `true` on success, indicating that this object will respond
|
||||||
//! validly to further method calls. `false` on failure. On failure, no
|
//! validly to further method calls. `false` on failure. On failure, no
|
||||||
//! further method calls should be made.
|
//! further method calls should be made.
|
||||||
bool Initialize(HANDLE process);
|
//!
|
||||||
|
//! \sa ScopedProcessSuspend
|
||||||
|
bool Initialize(HANDLE process, ProcessSuspensionState suspension_state);
|
||||||
|
|
||||||
//! \return `true` if the target task is a 64-bit process.
|
//! \return `true` if the target task is a 64-bit process.
|
||||||
bool Is64Bit() const { return process_info_.Is64Bit(); }
|
bool Is64Bit() const { return process_info_.Is64Bit(); }
|
||||||
@ -96,6 +111,7 @@ class ProcessReaderWin {
|
|||||||
ProcessInfo process_info_;
|
ProcessInfo process_info_;
|
||||||
std::vector<Thread> threads_;
|
std::vector<Thread> threads_;
|
||||||
std::vector<ProcessInfo::Module> modules_;
|
std::vector<ProcessInfo::Module> modules_;
|
||||||
|
ProcessSuspensionState suspension_state_;
|
||||||
bool initialized_threads_;
|
bool initialized_threads_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/win/win_multiprocess.h"
|
#include "test/win/win_multiprocess.h"
|
||||||
|
#include "util/win/scoped_process_suspend.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
@ -26,7 +27,8 @@ namespace {
|
|||||||
|
|
||||||
TEST(ProcessReaderWin, SelfBasic) {
|
TEST(ProcessReaderWin, SelfBasic) {
|
||||||
ProcessReaderWin process_reader;
|
ProcessReaderWin process_reader;
|
||||||
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
#if !defined(ARCH_CPU_64_BITS)
|
#if !defined(ARCH_CPU_64_BITS)
|
||||||
EXPECT_FALSE(process_reader.Is64Bit());
|
EXPECT_FALSE(process_reader.Is64Bit());
|
||||||
@ -55,7 +57,8 @@ class ProcessReaderChild final : public WinMultiprocess {
|
|||||||
private:
|
private:
|
||||||
void WinMultiprocessParent() override {
|
void WinMultiprocessParent() override {
|
||||||
ProcessReaderWin process_reader;
|
ProcessReaderWin process_reader;
|
||||||
ASSERT_TRUE(process_reader.Initialize(ChildProcess()));
|
ASSERT_TRUE(process_reader.Initialize(ChildProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
#if !defined(ARCH_CPU_64_BITS)
|
#if !defined(ARCH_CPU_64_BITS)
|
||||||
EXPECT_FALSE(process_reader.Is64Bit());
|
EXPECT_FALSE(process_reader.Is64Bit());
|
||||||
@ -90,7 +93,8 @@ TEST(ProcessReaderWin, ChildBasic) {
|
|||||||
|
|
||||||
TEST(ProcessReaderWin, SelfOneThread) {
|
TEST(ProcessReaderWin, SelfOneThread) {
|
||||||
ProcessReaderWin process_reader;
|
ProcessReaderWin process_reader;
|
||||||
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
const std::vector<ProcessReaderWin::Thread>& threads =
|
const std::vector<ProcessReaderWin::Thread>& threads =
|
||||||
process_reader.Threads();
|
process_reader.Threads();
|
||||||
@ -110,6 +114,56 @@ TEST(ProcessReaderWin, SelfOneThread) {
|
|||||||
EXPECT_EQ(0, threads[0].suspend_count);
|
EXPECT_EQ(0, threads[0].suspend_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProcessReaderChildThreadSuspendCount final : public WinMultiprocess {
|
||||||
|
public:
|
||||||
|
ProcessReaderChildThreadSuspendCount() : WinMultiprocess() {}
|
||||||
|
~ProcessReaderChildThreadSuspendCount() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WinMultiprocessParent() override {
|
||||||
|
{
|
||||||
|
ProcessReaderWin process_reader;
|
||||||
|
ASSERT_TRUE(process_reader.Initialize(ChildProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
|
|
||||||
|
const auto& threads = process_reader.Threads();
|
||||||
|
ASSERT_FALSE(threads.empty());
|
||||||
|
for (const auto& thread : threads)
|
||||||
|
EXPECT_EQ(0u, thread.suspend_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopedProcessSuspend suspend(ChildProcess());
|
||||||
|
|
||||||
|
ProcessReaderWin process_reader;
|
||||||
|
ASSERT_TRUE(process_reader.Initialize(
|
||||||
|
ChildProcess(), ProcessSuspensionState::kSuspended));
|
||||||
|
|
||||||
|
// Confirm that thread counts are adjusted correctly for the process being
|
||||||
|
// suspended.
|
||||||
|
const auto& threads = process_reader.Threads();
|
||||||
|
ASSERT_FALSE(threads.empty());
|
||||||
|
for (const auto& thread : threads)
|
||||||
|
EXPECT_EQ(0u, thread.suspend_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WinMultiprocessChild() override {
|
||||||
|
WinVMAddress address = reinterpret_cast<WinVMAddress>(kTestMemory);
|
||||||
|
CheckedWriteFile(WritePipeHandle(), &address, sizeof(address));
|
||||||
|
|
||||||
|
// Wait for the parent to signal that it's OK to exit by closing its end of
|
||||||
|
// the pipe.
|
||||||
|
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ProcessReaderChildThreadSuspendCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(ProcessReaderWin, ChildThreadSuspendCounts) {
|
||||||
|
WinMultiprocess::Run<ProcessReaderChildThreadSuspendCount>();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -38,12 +38,13 @@ ProcessSnapshotWin::ProcessSnapshotWin()
|
|||||||
ProcessSnapshotWin::~ProcessSnapshotWin() {
|
ProcessSnapshotWin::~ProcessSnapshotWin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessSnapshotWin::Initialize(HANDLE process) {
|
bool ProcessSnapshotWin::Initialize(HANDLE process,
|
||||||
|
ProcessSuspensionState suspension_state) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
GetTimeOfDay(&snapshot_time_);
|
GetTimeOfDay(&snapshot_time_);
|
||||||
|
|
||||||
if (!process_reader_.Initialize(process))
|
if (!process_reader_.Initialize(process, suspension_state))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
system_.Initialize(&process_reader_);
|
system_.Initialize(&process_reader_);
|
||||||
|
@ -52,10 +52,14 @@ class ProcessSnapshotWin final : public ProcessSnapshot {
|
|||||||
//! \brief Initializes the object.
|
//! \brief Initializes the object.
|
||||||
//!
|
//!
|
||||||
//! \param[in] process The handle to create a snapshot from.
|
//! \param[in] process The handle to create a snapshot from.
|
||||||
|
//! \param[in] suspension_state Whether \a process has been suspended by the
|
||||||
|
//! caller.
|
||||||
//!
|
//!
|
||||||
//! \return `true` if the snapshot could be created, `false` otherwise with
|
//! \return `true` if the snapshot could be created, `false` otherwise with
|
||||||
//! an appropriate message logged.
|
//! an appropriate message logged.
|
||||||
bool Initialize(HANDLE process);
|
//!
|
||||||
|
//! \sa ScopedProcessSuspend
|
||||||
|
bool Initialize(HANDLE process, ProcessSuspensionState suspension_state);
|
||||||
|
|
||||||
//! \brief Initializes the object's exception.
|
//! \brief Initializes the object's exception.
|
||||||
//!
|
//!
|
||||||
|
@ -41,7 +41,8 @@ class SystemSnapshotWinTest : public testing::Test {
|
|||||||
|
|
||||||
// testing::Test:
|
// testing::Test:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess()));
|
ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning));
|
||||||
system_snapshot_.Initialize(&process_reader_);
|
system_snapshot_.Initialize(&process_reader_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,7 +182,10 @@ int GenerateDumpMain(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
ProcessSnapshotWin process_snapshot;
|
ProcessSnapshotWin process_snapshot;
|
||||||
if (!process_snapshot.Initialize(process.get())) {
|
if (!process_snapshot.Initialize(process.get(),
|
||||||
|
options.suspend
|
||||||
|
? ProcessSuspensionState::kSuspended
|
||||||
|
: ProcessSuspensionState::kRunning)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
|
Loading…
x
Reference in New Issue
Block a user