crashpad/util/win/scoped_process_suspend_test.cc

126 lines
3.9 KiB
C++
Raw Normal View History

// 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 <stddef.h>
#include <tlhelp32.h>
#include <algorithm>
#include <vector>
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/win/win_child_process.h"
#include "util/win/xp_compat.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()) {
ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot");
return false;
}
THREADENTRY32 te;
te.dwSize = sizeof(te);
BOOL ret = Thread32First(snapshot.get(), &te);
if (!ret) {
ADD_FAILURE() << ErrorMessage("Thread32First");
return false;
}
do {
if (te.dwSize >= offsetof(THREADENTRY32, th32OwnerProcessID) +
sizeof(te.th32OwnerProcessID) &&
te.th32OwnerProcessID == process_id) {
ScopedKernelHANDLE thread(
OpenThread(kXPThreadAllAccess, false, te.th32ThreadID));
EXPECT_TRUE(thread.is_valid()) << ErrorMessage("OpenThread");
DWORD result = SuspendThread(thread.get());
EXPECT_NE(result, static_cast<DWORD>(-1))
<< ErrorMessage("SuspendThread");
if (result != static_cast<DWORD>(-1)) {
EXPECT_NE(ResumeThread(thread.get()), static_cast<DWORD>(-1))
<< ErrorMessage("ResumeThread");
}
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.
Make file_io reads more rational and predictable ReadFile() attempted to continue reading after a short read. In most cases, this is fine. However, ReadFile() would keep trying to fill a partially-filled buffer until experiencing a 0-length read(), signaling end-of-file. For certain weird file descriptors like terminal input, EOF is an ephemeral condition, and attempting to read beyond EOF doesn’t actually return 0 (EOF) provided that they remain open, it will block waiting for more input. Consequently, ReadFile() and anything based on ReadFile() had an undocumented and quirky interface, which was that any short read that it returned (not an underlying short read) actually indicated EOF. This facet of ReadFile() was unexpected, so it’s being removed. The new behavior is that ReadFile() will return an underlying short read. The behavior of FileReaderInterface::Read() is updated in accordance with this change. Upon experiencing a short read, the caller can determine the best action. Most callers were already prepared for this behavior. Outside of util/file, only crashpad_database_util properly implemented EOF detection according to previous semantics, and adapting it to new semantics is trivial. Callers who require an exact-length read can use the new ReadFileExactly(), or the newly renamed LoggingReadFileExactly() or CheckedReadFileExactly(). These functions will retry following a short read. The renamed functions were previously called LoggingReadFile() and CheckedReadFile(), but those names implied that they were simply wrapping ReadFile(), which is not the case. They wrapped ReadFile() and further, insisted on a full read. Since ReadFile()’s semantics are now changing but these functions’ are not, they’re now even more distinct from ReadFile(), and must be renamed to avoid confusion. Test: * Change-Id: I06b77e0d6ad8719bd2eb67dab93a8740542dd908 Reviewed-on: https://chromium-review.googlesource.com/456676 Reviewed-by: Robert Sesek <rsesek@chromium.org>
2017-03-16 13:36:38 -04:00
EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
test: Use (actual, [un]expected) in gtest {ASSERT,EXPECT}_{EQ,NE} gtest used to require (expected, actual) ordering for arguments to EXPECT_EQ and ASSERT_EQ, and in failed test assertions would identify each side as “expected” or “actual.” Tests in Crashpad adhered to this traditional ordering. After a gtest change in February 2016, it is now agnostic with respect to the order of these arguments. This change mechanically updates all uses of these macros to (actual, expected) by reversing them. This provides consistency with our use of the logging CHECK_EQ and DCHECK_EQ macros, and makes for better readability by ordinary native speakers. The rough (but working!) conversion tool is https://chromium-review.googlesource.com/c/466727/1/rewrite_expectassert_eq.py, and “git cl format” cleaned up its output. EXPECT_NE and ASSERT_NE never had a preferred ordering. gtest never made a judgment that one side or the other needed to provide an “unexpected” value. Consequently, some code used (unexpected, actual) while other code used (actual, unexpected). For consistency with the new EXPECT_EQ and ASSERT_EQ usage, as well as consistency with CHECK_NE and DCHECK_NE, this change also updates these use sites to (actual, unexpected) where one side can be called “unexpected” as, for example, std::string::npos can be. Unfortunately, this portion was a manual conversion. References: https://github.com/google/googletest/blob/master/googletest/docs/Primer.md#binary-comparison https://github.com/google/googletest/commit/77d6b173380332b1c1bc540532641f410ec82d65 https://github.com/google/googletest/pull/713 Change-Id: I978fef7c94183b8b1ef63f12f5ab4d6693626be3 Reviewed-on: https://chromium-review.googlesource.com/466727 Reviewed-by: Scott Graham <scottmg@chromium.org>
2017-04-04 00:35:21 -04:00
EXPECT_EQ(c, ' ');
return EXIT_SUCCESS;
}
DISALLOW_COPY_AND_ASSIGN(ScopedProcessSuspendTest);
};
// https://crashpad.chromium.org/bug/205
#if defined(CRASHPAD_IN_CHROMIUM)
#define MAYBE_ScopedProcessSuspend DISABLED_ScopedProcessSuspend
#else // CRASHPAD_IN_CHROMIUM
#define MAYBE_ScopedProcessSuspend ScopedProcessSuspend
#endif // CRASHPAD_IN_CHROMIUM
TEST(ScopedProcessSuspend, MAYBE_ScopedProcessSuspend) {
WinChildProcess::EntryPoint<ScopedProcessSuspendTest>();
std::unique_ptr<WinChildProcess::Handles> handles = WinChildProcess::Launch();
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 0));
{
ScopedProcessSuspend suspend0(handles->process.get());
EXPECT_TRUE(SuspendCountMatches(handles->process.get(), 1));
{
ScopedProcessSuspend suspend1(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