2015-03-05 22:07:38 -08:00
|
|
|
// 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/process_info.h"
|
|
|
|
|
2015-05-14 18:45:28 -07:00
|
|
|
#include <imagehlp.h>
|
2015-03-05 22:07:38 -08:00
|
|
|
#include <rpc.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
|
|
#include "base/files/file_path.h"
|
|
|
|
#include "build/build_config.h"
|
|
|
|
#include "gtest/gtest.h"
|
test: Move util/test to its own top-level directory, test.
After 9e79ea1da719, it no longer makes sense for crashpad_util_test_lib
to “hide” in util/util_test.gyp. All of util/test is moved to its own
top-level directory, test, which all other test code is allowed to
depend on. test, too, is allowed to depend on all other non-test code.
In a future change, when crashpad_util_test_lib gains a dependency on
crashpad_client, it won’t look so weird for something in util (even
though it’s in util/test) to depend on something in client, because the
thing that needs to depend on client will live in test, not util.
BUG=crashpad:33
R=scottmg@chromium.org
Review URL: https://codereview.chromium.org/1051533002
2015-03-31 17:44:14 -04:00
|
|
|
#include "test/paths.h"
|
2015-05-01 13:48:23 -07:00
|
|
|
#include "util/file/file_io.h"
|
2015-03-05 22:07:38 -08:00
|
|
|
#include "util/misc/uuid.h"
|
|
|
|
#include "util/win/scoped_handle.h"
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
namespace test {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
const wchar_t kNtdllName[] = L"\\ntdll.dll";
|
|
|
|
|
2015-05-01 13:48:23 -07:00
|
|
|
time_t GetTimestampForModule(HMODULE module) {
|
2015-05-14 18:45:28 -07:00
|
|
|
char filename[MAX_PATH];
|
|
|
|
// `char` and GetModuleFileNameA because ImageLoad is ANSI only.
|
|
|
|
if (!GetModuleFileNameA(module, filename, arraysize(filename)))
|
2015-05-01 13:48:23 -07:00
|
|
|
return 0;
|
2015-05-14 18:45:28 -07:00
|
|
|
LOADED_IMAGE* loaded_image = ImageLoad(filename, nullptr);
|
|
|
|
return loaded_image->FileHeader->FileHeader.TimeDateStamp;
|
2015-05-01 13:48:23 -07:00
|
|
|
}
|
|
|
|
|
2015-03-05 22:07:38 -08:00
|
|
|
TEST(ProcessInfo, Self) {
|
|
|
|
ProcessInfo process_info;
|
|
|
|
ASSERT_TRUE(process_info.Initialize(GetCurrentProcess()));
|
|
|
|
EXPECT_EQ(GetCurrentProcessId(), process_info.ProcessID());
|
|
|
|
EXPECT_GT(process_info.ParentProcessID(), 0u);
|
|
|
|
|
|
|
|
#if defined(ARCH_CPU_64_BITS)
|
|
|
|
EXPECT_TRUE(process_info.Is64Bit());
|
|
|
|
EXPECT_FALSE(process_info.IsWow64());
|
|
|
|
#else
|
|
|
|
EXPECT_FALSE(process_info.Is64Bit());
|
|
|
|
// Assume we won't be running these tests on a 32 bit host OS.
|
|
|
|
EXPECT_TRUE(process_info.IsWow64());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::wstring command_line;
|
|
|
|
EXPECT_TRUE(process_info.CommandLine(&command_line));
|
|
|
|
EXPECT_EQ(std::wstring(GetCommandLine()), command_line);
|
|
|
|
|
2015-05-01 13:48:23 -07:00
|
|
|
std::vector<ProcessInfo::Module> modules;
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
|
|
ASSERT_GE(modules.size(), 2u);
|
2015-03-08 16:25:34 -04:00
|
|
|
const wchar_t kSelfName[] = L"\\crashpad_util_test.exe";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules[0].name.size(), wcslen(kSelfName));
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_EQ(kSelfName,
|
2015-05-01 13:48:23 -07:00
|
|
|
modules[0].name.substr(modules[0].name.size() - wcslen(kSelfName)));
|
|
|
|
ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
|
|
|
|
EXPECT_EQ(
|
|
|
|
kNtdllName,
|
|
|
|
modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
|
|
|
|
|
|
|
|
EXPECT_EQ(modules[0].dll_base,
|
|
|
|
reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)));
|
|
|
|
EXPECT_EQ(modules[1].dll_base,
|
|
|
|
reinterpret_cast<uintptr_t>(GetModuleHandle(L"ntdll.dll")));
|
|
|
|
|
|
|
|
EXPECT_GT(modules[0].size, 0);
|
|
|
|
EXPECT_GT(modules[1].size, 0);
|
|
|
|
|
|
|
|
EXPECT_EQ(modules[0].timestamp,
|
|
|
|
GetTimestampForModule(GetModuleHandle(nullptr)));
|
|
|
|
// System modules are forced to particular stamps and the file header values
|
|
|
|
// don't match the on-disk times. Just make sure we got some data here.
|
|
|
|
EXPECT_GT(modules[1].timestamp, 0);
|
2015-03-05 22:07:38 -08:00
|
|
|
}
|
|
|
|
|
2015-03-09 16:37:43 -07:00
|
|
|
void TestOtherProcess(const std::wstring& child_name_suffix) {
|
2015-03-05 22:07:38 -08:00
|
|
|
ProcessInfo process_info;
|
|
|
|
|
|
|
|
::UUID system_uuid;
|
|
|
|
ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
|
|
|
|
UUID started_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
|
|
|
|
ASSERT_EQ(RPC_S_OK, UuidCreate(&system_uuid));
|
|
|
|
UUID done_uuid(reinterpret_cast<const uint8_t*>(&system_uuid.Data1));
|
|
|
|
|
|
|
|
ScopedKernelHANDLE started(
|
|
|
|
CreateEvent(nullptr, true, false, started_uuid.ToString16().c_str()));
|
|
|
|
ASSERT_TRUE(started.get());
|
|
|
|
ScopedKernelHANDLE done(
|
|
|
|
CreateEvent(nullptr, true, false, done_uuid.ToString16().c_str()));
|
|
|
|
ASSERT_TRUE(done.get());
|
|
|
|
|
2015-03-09 15:13:13 -04:00
|
|
|
base::FilePath test_executable = Paths::Executable();
|
2015-03-05 22:07:38 -08:00
|
|
|
std::wstring child_test_executable =
|
|
|
|
test_executable.RemoveFinalExtension().value() +
|
2015-03-09 16:37:43 -07:00
|
|
|
L"_process_info_test_child_" + child_name_suffix + L".exe";
|
2015-03-05 22:07:38 -08:00
|
|
|
// TODO(scottmg): Command line escaping utility.
|
|
|
|
std::wstring command_line = child_test_executable + L" " +
|
|
|
|
started_uuid.ToString16() + L" " +
|
|
|
|
done_uuid.ToString16();
|
|
|
|
STARTUPINFO startup_info = {0};
|
|
|
|
startup_info.cb = sizeof(startup_info);
|
|
|
|
PROCESS_INFORMATION process_information;
|
|
|
|
ASSERT_TRUE(CreateProcess(child_test_executable.c_str(),
|
|
|
|
&command_line[0],
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
false,
|
|
|
|
0,
|
|
|
|
nullptr,
|
|
|
|
nullptr,
|
|
|
|
&startup_info,
|
|
|
|
&process_information));
|
|
|
|
// Take ownership of the two process handles returned.
|
|
|
|
ScopedKernelHANDLE process_main_thread_handle(process_information.hThread);
|
|
|
|
ScopedKernelHANDLE process_handle(process_information.hProcess);
|
|
|
|
|
|
|
|
// Wait until the test has completed initialization.
|
|
|
|
ASSERT_EQ(WaitForSingleObject(started.get(), INFINITE), WAIT_OBJECT_0);
|
|
|
|
|
|
|
|
ASSERT_TRUE(process_info.Initialize(process_information.hProcess));
|
|
|
|
|
|
|
|
// Tell the test it's OK to shut down now that we've read our data.
|
|
|
|
SetEvent(done.get());
|
|
|
|
|
2015-05-01 13:48:23 -07:00
|
|
|
std::vector<ProcessInfo::Module> modules;
|
2015-03-05 22:07:38 -08:00
|
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
|
|
ASSERT_GE(modules.size(), 3u);
|
2015-03-09 16:37:43 -07:00
|
|
|
std::wstring child_name = L"\\crashpad_util_test_process_info_test_child_" +
|
|
|
|
child_name_suffix + L".exe";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules[0].name.size(), child_name.size());
|
2015-03-09 16:37:43 -07:00
|
|
|
EXPECT_EQ(child_name,
|
2015-05-01 13:48:23 -07:00
|
|
|
modules[0].name.substr(modules[0].name.size() - child_name.size()));
|
|
|
|
ASSERT_GE(modules[1].name.size(), wcslen(kNtdllName));
|
|
|
|
EXPECT_EQ(
|
|
|
|
kNtdllName,
|
|
|
|
modules[1].name.substr(modules[1].name.size() - wcslen(kNtdllName)));
|
2015-03-05 22:07:38 -08:00
|
|
|
// lz32.dll is an uncommonly-used-but-always-available module that the test
|
|
|
|
// binary manually loads.
|
|
|
|
const wchar_t kLz32dllName[] = L"\\lz32.dll";
|
2015-05-01 13:48:23 -07:00
|
|
|
ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName));
|
|
|
|
EXPECT_EQ(kLz32dllName,
|
|
|
|
modules.back().name.substr(modules.back().name.size() -
|
|
|
|
wcslen(kLz32dllName)));
|
2015-03-05 22:07:38 -08:00
|
|
|
}
|
|
|
|
|
2015-09-11 16:11:00 -07:00
|
|
|
// This test can't run the child if the host OS is x86, and can't read from the
|
|
|
|
// child if it is x86 and the child is x64, so it only makes sense to run this
|
|
|
|
// if we built as x64.
|
|
|
|
#if defined(ARCH_CPU_64_BITS)
|
2015-03-09 16:37:43 -07:00
|
|
|
TEST(ProcessInfo, OtherProcessX64) {
|
|
|
|
TestOtherProcess(L"x64");
|
|
|
|
}
|
2015-09-11 16:11:00 -07:00
|
|
|
#endif // ARCH_CPU_64_BITS
|
2015-03-09 16:37:43 -07:00
|
|
|
|
|
|
|
TEST(ProcessInfo, OtherProcessX86) {
|
|
|
|
TestOtherProcess(L"x86");
|
|
|
|
}
|
|
|
|
|
2015-03-05 22:07:38 -08:00
|
|
|
} // namespace
|
|
|
|
} // namespace test
|
|
|
|
} // namespace crashpad
|