win: Make CrashpadClient::DumpAndCrashTargetProcess() less chatty

CrashpadClient::DumpAndCrashTargetProcess() suspends the target process
and injects a thread to raise an exception. The injected thread is not
suspended, and may proceed to the point that the system recognizes the
process as terminating by the time the overall process suspension is
lifted. Previously, if this happened, an extraneous error was logged for
the attempt to resume a terminating process.

This introduces “termination tolerance” to ScopedProcessSuspend, which
allows an object to be configured to ignore this error and not log any
messages when this condition is expected.

This resolves log messages such as this one, produced frequently during
calls to CrashpadClient::DumpAndCrashTargetProcess() (including in
end_to_end_test.py):

> [pid:tid:yyyymmdd,hhmmss.mmm:ERROR scoped_process_suspend.cc:39]
> NtResumeProcess: An attempt was made to access an exiting process.
> (0xc000010a)

0xc000010a = STATUS_PROCESS_IS_TERMINATING

Test: end_to_end_test.py
Change-Id: Iab4c50fb21adce5502080ad25a6f734ec566d65c
Reviewed-on: https://chromium-review.googlesource.com/700715
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Scott Graham <scottmg@chromium.org>
This commit is contained in:
Mark Mentovai 2017-10-04 13:38:25 -04:00 committed by Commit Bot
parent f6aebd8baf
commit c6adcc2482
4 changed files with 27 additions and 6 deletions

View File

@ -1021,6 +1021,11 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process,
return false; return false;
} }
// The injected thread raises an exception and ultimately results in process
// termination. The suspension must be made aware that the process may be
// terminating, otherwise itll log an extraneous error.
suspend.TolerateTermination();
bool result = true; bool result = true;
if (WaitForSingleObject(injected_thread, 60 * 1000) != WAIT_OBJECT_0) { if (WaitForSingleObject(injected_thread, 60 * 1000) != WAIT_OBJECT_0) {
PLOG(ERROR) << "WaitForSingleObject"; PLOG(ERROR) << "WaitForSingleObject";

View File

@ -17,6 +17,12 @@
#include "util/win/process_structs.h" #include "util/win/process_structs.h"
// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of
// ntstatus.h.
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_PROCESS_IS_TERMINATING ((NTSTATUS)0xC000010AL)
namespace crashpad { namespace crashpad {
NTSTATUS NtClose(HANDLE handle); NTSTATUS NtClose(HANDLE handle);
@ -36,11 +42,6 @@ NtCreateThreadEx(PHANDLE thread_handle,
SIZE_T maximum_stack_size, SIZE_T maximum_stack_size,
PVOID /*PPS_ATTRIBUTE_LIST*/ attribute_list); PVOID /*PPS_ATTRIBUTE_LIST*/ attribute_list);
// Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of
// ntstatus.h.
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
// winternal.h defines THREADINFOCLASS, but not all members. // winternal.h defines THREADINFOCLASS, but not all members.
enum { ThreadBasicInformation = 0 }; enum { ThreadBasicInformation = 0 };

View File

@ -35,10 +35,15 @@ ScopedProcessSuspend::ScopedProcessSuspend(HANDLE process) {
ScopedProcessSuspend::~ScopedProcessSuspend() { ScopedProcessSuspend::~ScopedProcessSuspend() {
if (process_) { if (process_) {
NTSTATUS status = NtResumeProcess(process_); NTSTATUS status = NtResumeProcess(process_);
if (!NT_SUCCESS(status)) { if (!NT_SUCCESS(status) &&
(!tolerate_termination_ || status != STATUS_PROCESS_IS_TERMINATING)) {
NTSTATUS_LOG(ERROR, status) << "NtResumeProcess"; NTSTATUS_LOG(ERROR, status) << "NtResumeProcess";
} }
} }
} }
void ScopedProcessSuspend::TolerateTermination() {
tolerate_termination_ = true;
}
} // namespace crashpad } // namespace crashpad

View File

@ -35,8 +35,18 @@ class ScopedProcessSuspend {
explicit ScopedProcessSuspend(HANDLE process); explicit ScopedProcessSuspend(HANDLE process);
~ScopedProcessSuspend(); ~ScopedProcessSuspend();
//! \brief Informs the object that the suspended process may be terminating,
//! and that this should not be treated as an error.
//!
//! Normally, attempting to resume a terminating process during destruction
//! results in an error message being logged for
//! `STATUS_PROCESS_IS_TERMINATING`. When it is known that a process may be
//! terminating, this method may be called to suppress that error message.
void TolerateTermination();
private: private:
HANDLE process_; HANDLE process_;
bool tolerate_termination_ = false;
DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspend); DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspend);
}; };