mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
This test was added in https://codereview.chromium.org/1052813002. It was previously checking the timestamp from in-memory module traversal vs. the disk mtime. This is flaky (of course) because it depends on the linker writing the header and closing the file during the same time quantum. So the bots occasionally failed with: [ RUN ] ProcessInfo.Self e:\b\build\slave\chromium_win_dbg\build\crashpad\util\win\process_info_test.cc(86): error: Value of: GetTimestampForModule(GetModuleHandleW(nullptr)) Actual: 1431650338 Expected: modules[0].timestamp Which is: 1431650337 Instead, use imagehlp to pull the timestamp out of the header so that it matches the header value that will be the in-memory timestamp. R=cpu@chromium.org TBR=mark@chromium.org Review URL: https://codereview.chromium.org/1139103003
171 lines
6.2 KiB
C++
171 lines
6.2 KiB
C++
// 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"
|
|
|
|
#include <imagehlp.h>
|
|
#include <rpc.h>
|
|
#include <wchar.h>
|
|
|
|
#include "base/files/file_path.h"
|
|
#include "build/build_config.h"
|
|
#include "gtest/gtest.h"
|
|
#include "test/paths.h"
|
|
#include "util/file/file_io.h"
|
|
#include "util/misc/uuid.h"
|
|
#include "util/win/scoped_handle.h"
|
|
|
|
namespace crashpad {
|
|
namespace test {
|
|
namespace {
|
|
|
|
const wchar_t kNtdllName[] = L"\\ntdll.dll";
|
|
|
|
time_t GetTimestampForModule(HMODULE module) {
|
|
char filename[MAX_PATH];
|
|
// `char` and GetModuleFileNameA because ImageLoad is ANSI only.
|
|
if (!GetModuleFileNameA(module, filename, arraysize(filename)))
|
|
return 0;
|
|
LOADED_IMAGE* loaded_image = ImageLoad(filename, nullptr);
|
|
return loaded_image->FileHeader->FileHeader.TimeDateStamp;
|
|
}
|
|
|
|
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);
|
|
|
|
std::vector<ProcessInfo::Module> modules;
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
ASSERT_GE(modules.size(), 2u);
|
|
const wchar_t kSelfName[] = L"\\crashpad_util_test.exe";
|
|
ASSERT_GE(modules[0].name.size(), wcslen(kSelfName));
|
|
EXPECT_EQ(kSelfName,
|
|
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);
|
|
}
|
|
|
|
void TestOtherProcess(const std::wstring& child_name_suffix) {
|
|
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());
|
|
|
|
base::FilePath test_executable = Paths::Executable();
|
|
std::wstring child_test_executable =
|
|
test_executable.RemoveFinalExtension().value() +
|
|
L"_process_info_test_child_" + child_name_suffix + L".exe";
|
|
// 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());
|
|
|
|
std::vector<ProcessInfo::Module> modules;
|
|
EXPECT_TRUE(process_info.Modules(&modules));
|
|
ASSERT_GE(modules.size(), 3u);
|
|
std::wstring child_name = L"\\crashpad_util_test_process_info_test_child_" +
|
|
child_name_suffix + L".exe";
|
|
ASSERT_GE(modules[0].name.size(), child_name.size());
|
|
EXPECT_EQ(child_name,
|
|
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)));
|
|
// lz32.dll is an uncommonly-used-but-always-available module that the test
|
|
// binary manually loads.
|
|
const wchar_t kLz32dllName[] = L"\\lz32.dll";
|
|
ASSERT_GE(modules.back().name.size(), wcslen(kLz32dllName));
|
|
EXPECT_EQ(kLz32dllName,
|
|
modules.back().name.substr(modules.back().name.size() -
|
|
wcslen(kLz32dllName)));
|
|
}
|
|
|
|
TEST(ProcessInfo, OtherProcessX64) {
|
|
TestOtherProcess(L"x64");
|
|
}
|
|
|
|
TEST(ProcessInfo, OtherProcessX86) {
|
|
TestOtherProcess(L"x86");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace test
|
|
} // namespace crashpad
|