mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 01:08:01 +08:00
win: Implement and use ScopedProcessSuspend
R=mark@chromium.org BUG=crashpad:1 Review URL: https://codereview.chromium.org/1303173011 .
This commit is contained in:
parent
28c5da9080
commit
5111a1823f
@ -21,6 +21,7 @@
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/win/registration_protocol_win.h"
|
||||
#include "util/win/scoped_process_suspend.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -44,7 +45,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
||||
WinVMAddress exception_information_address) {
|
||||
const unsigned int kFailedTerminationCode = 0xffff7002;
|
||||
|
||||
// TODO(scottmg): ScopedProcessSuspend
|
||||
ScopedProcessSuspend suspend(process);
|
||||
|
||||
ProcessSnapshotWin process_snapshot;
|
||||
if (!process_snapshot.Initialize(process)) {
|
||||
|
@ -41,6 +41,7 @@
|
||||
#elif defined(OS_WIN)
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "snapshot/win/process_snapshot_win.h"
|
||||
#include "util/win/scoped_process_suspend.h"
|
||||
#endif // OS_MACOSX
|
||||
|
||||
namespace crashpad {
|
||||
@ -168,9 +169,9 @@ int GenerateDumpMain(int argc, char* argv[]) {
|
||||
suspend.reset(new ScopedTaskSuspend(task));
|
||||
}
|
||||
#elif defined(OS_WIN)
|
||||
scoped_ptr<ScopedProcessSuspend> suspend;
|
||||
if (options.suspend) {
|
||||
LOG(ERROR) << "TODO(scottmg): --no-suspend is required for now.";
|
||||
return EXIT_FAILURE;
|
||||
suspend.reset(new ScopedProcessSuspend(process.get()));
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
|
||||
|
@ -162,6 +162,8 @@
|
||||
'win/registration_protocol_win.h',
|
||||
'win/scoped_handle.cc',
|
||||
'win/scoped_handle.h',
|
||||
'win/scoped_process_suspend.cc',
|
||||
'win/scoped_process_suspend.h',
|
||||
'win/time.cc',
|
||||
'win/time.h',
|
||||
],
|
||||
|
@ -81,6 +81,7 @@
|
||||
'thread/thread_test.cc',
|
||||
'win/exception_handler_server_test.cc',
|
||||
'win/process_info_test.cc',
|
||||
'win/scoped_process_suspend_test.cc',
|
||||
'win/time_test.cc',
|
||||
],
|
||||
'conditions': [
|
||||
|
41
util/win/scoped_process_suspend.cc
Normal file
41
util/win/scoped_process_suspend.cc
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 "util/win/scoped_process_suspend.h"
|
||||
|
||||
#include <winternl.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
ScopedProcessSuspend::ScopedProcessSuspend(HANDLE process) : process_(process) {
|
||||
typedef NTSTATUS(__stdcall * NtSuspendProcessFunc)(HANDLE);
|
||||
static NtSuspendProcessFunc func = reinterpret_cast<NtSuspendProcessFunc>(
|
||||
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtSuspendProcess"));
|
||||
NTSTATUS status = func(process_);
|
||||
if (status)
|
||||
LOG(ERROR) << "NtSuspendProcess, ntstatus=" << status;
|
||||
}
|
||||
|
||||
ScopedProcessSuspend::~ScopedProcessSuspend() {
|
||||
typedef NTSTATUS(__stdcall * NtResumeProcessFunc)(HANDLE);
|
||||
static NtResumeProcessFunc func = reinterpret_cast<NtResumeProcessFunc>(
|
||||
GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtResumeProcess"));
|
||||
NTSTATUS status = func(process_);
|
||||
if (status)
|
||||
LOG(ERROR) << "NtResumeProcess, ntstatus=" << status;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
46
util/win/scoped_process_suspend.h
Normal file
46
util/win/scoped_process_suspend.h
Normal file
@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
|
||||
#define CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Manages the suspension of another process.
|
||||
//!
|
||||
//! While an object of this class exists, the other process will be suspended.
|
||||
//! Once the object is destroyed, the other process will become eligible for
|
||||
//! resumption.
|
||||
//!
|
||||
//! If this process crashes while this object exists, there is no guarantee that
|
||||
//! the other process will be resumed.
|
||||
class ScopedProcessSuspend {
|
||||
public:
|
||||
//! Does not take ownership of \a process.
|
||||
explicit ScopedProcessSuspend(HANDLE process);
|
||||
~ScopedProcessSuspend();
|
||||
|
||||
private:
|
||||
HANDLE process_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspend);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_WIN_SCOPED_PROCESS_SUSPEND_H_
|
106
util/win/scoped_process_suspend_test.cc
Normal file
106
util/win/scoped_process_suspend_test.cc
Normal file
@ -0,0 +1,106 @@
|
||||
// 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 "util/win/scoped_process_suspend.h"
|
||||
|
||||
#include <tlhelp32.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/win/win_child_process.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
// There is no per-process suspend count on Windows, only a per-thread suspend
|
||||
// count. NtSuspendProcess just suspends all threads of a given process. So,
|
||||
// verify that all thread's suspend counts match the desired suspend count.
|
||||
bool SuspendCountMatches(HANDLE process, DWORD desired_suspend_count) {
|
||||
DWORD process_id = GetProcessId(process);
|
||||
|
||||
ScopedKernelHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
|
||||
if (!snapshot.is_valid())
|
||||
return false;
|
||||
|
||||
THREADENTRY32 te;
|
||||
te.dwSize = sizeof(te);
|
||||
if (!Thread32First(snapshot.get(), &te))
|
||||
return false;
|
||||
do {
|
||||
if (te.dwSize >= offsetof(THREADENTRY32, th32OwnerProcessID) +
|
||||
sizeof(te.th32OwnerProcessID) &&
|
||||
te.th32OwnerProcessID == process_id) {
|
||||
ScopedKernelHANDLE thread(
|
||||
OpenThread(THREAD_ALL_ACCESS, false, te.th32ThreadID));
|
||||
DWORD result = SuspendThread(thread.get());
|
||||
EXPECT_NE(result, static_cast<DWORD>(-1));
|
||||
if (result != static_cast<DWORD>(-1))
|
||||
ResumeThread(thread.get());
|
||||
if (result != desired_suspend_count)
|
||||
return false;
|
||||
}
|
||||
te.dwSize = sizeof(te);
|
||||
} while (Thread32Next(snapshot.get(), &te));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class ScopedProcessSuspendTest final : public WinChildProcess {
|
||||
public:
|
||||
ScopedProcessSuspendTest() : WinChildProcess() {}
|
||||
~ScopedProcessSuspendTest() {}
|
||||
|
||||
private:
|
||||
int Run() override {
|
||||
char c;
|
||||
// Wait for notification from parent.
|
||||
EXPECT_TRUE(LoggingReadFile(ReadPipeHandle(), &c, sizeof(c)));
|
||||
EXPECT_EQ(' ', c);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspendTest);
|
||||
};
|
||||
|
||||
TEST(ScopedProcessSuspend, ScopedProcessSuspend) {
|
||||
WinChildProcess::EntryPoint<ScopedProcessSuspendTest>();
|
||||
scoped_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch();
|
||||
|
||||
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));
|
||||
|
||||
{
|
||||
ScopedProcessSuspend suspend(handles->process.get());
|
||||
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));
|
||||
|
||||
{
|
||||
ScopedProcessSuspend suspend(handles->process.get());
|
||||
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 2));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));
|
||||
|
||||
// Tell the child it's OK to terminate.
|
||||
char c = ' ';
|
||||
EXPECT_TRUE(WriteFile(handles->write.get(), &c, sizeof(c)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
Loading…
x
Reference in New Issue
Block a user