| Index: third_party/crashpad/crashpad/util/win/process_info.cc
|
| diff --git a/third_party/crashpad/crashpad/util/win/process_info.cc b/third_party/crashpad/crashpad/util/win/process_info.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2ac3a98bfacbe937f5bb60edda1e93bbb74c51be
|
| --- /dev/null
|
| +++ b/third_party/crashpad/crashpad/util/win/process_info.cc
|
| @@ -0,0 +1,692 @@
|
| +// 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 <winternl.h>
|
| +
|
| +#include <algorithm>
|
| +#include <limits>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "base/template_util.h"
|
| +#include "build/build_config.h"
|
| +#include "util/numeric/safe_assignment.h"
|
| +#include "util/win/get_function.h"
|
| +#include "util/win/handle.h"
|
| +#include "util/win/nt_internals.h"
|
| +#include "util/win/ntstatus_logging.h"
|
| +#include "util/win/process_structs.h"
|
| +#include "util/win/scoped_handle.h"
|
| +
|
| +namespace crashpad {
|
| +
|
| +namespace {
|
| +
|
| +NTSTATUS NtQueryInformationProcess(HANDLE process_handle,
|
| + PROCESSINFOCLASS process_information_class,
|
| + PVOID process_information,
|
| + ULONG process_information_length,
|
| + PULONG return_length) {
|
| + static const auto nt_query_information_process =
|
| + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::NtQueryInformationProcess);
|
| + return nt_query_information_process(process_handle,
|
| + process_information_class,
|
| + process_information,
|
| + process_information_length,
|
| + return_length);
|
| +}
|
| +
|
| +bool IsProcessWow64(HANDLE process_handle) {
|
| + static const auto is_wow64_process =
|
| + GET_FUNCTION(L"kernel32.dll", ::IsWow64Process);
|
| + if (!is_wow64_process)
|
| + return false;
|
| + BOOL is_wow64;
|
| + if (!is_wow64_process(process_handle, &is_wow64)) {
|
| + PLOG(ERROR) << "IsWow64Process";
|
| + return false;
|
| + }
|
| + return !!is_wow64;
|
| +}
|
| +
|
| +template <class T>
|
| +bool ReadUnicodeString(HANDLE process,
|
| + const process_types::UNICODE_STRING<T>& us,
|
| + std::wstring* result) {
|
| + if (us.Length == 0) {
|
| + result->clear();
|
| + return true;
|
| + }
|
| + DCHECK_EQ(us.Length % sizeof(wchar_t), 0u);
|
| + result->resize(us.Length / sizeof(wchar_t));
|
| + SIZE_T bytes_read;
|
| + if (!ReadProcessMemory(
|
| + process,
|
| + reinterpret_cast<const void*>(static_cast<uintptr_t>(us.Buffer)),
|
| + &result->operator[](0),
|
| + us.Length,
|
| + &bytes_read)) {
|
| + PLOG(ERROR) << "ReadProcessMemory UNICODE_STRING";
|
| + return false;
|
| + }
|
| + if (bytes_read != us.Length) {
|
| + LOG(ERROR) << "ReadProcessMemory UNICODE_STRING incorrect size";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +template <class T>
|
| +bool ReadStruct(HANDLE process, WinVMAddress at, T* into) {
|
| + SIZE_T bytes_read;
|
| + if (!ReadProcessMemory(process,
|
| + reinterpret_cast<const void*>(at),
|
| + into,
|
| + sizeof(T),
|
| + &bytes_read)) {
|
| + // We don't have a name for the type we're reading, so include the signature
|
| + // to get the type of T.
|
| + PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__;
|
| + return false;
|
| + }
|
| + if (bytes_read != sizeof(T)) {
|
| + LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size";
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool RegionIsAccessible(const MEMORY_BASIC_INFORMATION64& memory_info) {
|
| + return memory_info.State == MEM_COMMIT &&
|
| + (memory_info.Protect & PAGE_NOACCESS) == 0 &&
|
| + (memory_info.Protect & PAGE_GUARD) == 0;
|
| +}
|
| +
|
| +MEMORY_BASIC_INFORMATION64 MemoryBasicInformationToMemoryBasicInformation64(
|
| + const MEMORY_BASIC_INFORMATION& mbi) {
|
| + MEMORY_BASIC_INFORMATION64 mbi64 = {0};
|
| + mbi64.BaseAddress = reinterpret_cast<ULONGLONG>(mbi.BaseAddress);
|
| + mbi64.AllocationBase = reinterpret_cast<ULONGLONG>(mbi.AllocationBase);
|
| + mbi64.AllocationProtect = mbi.AllocationProtect;
|
| + mbi64.RegionSize = mbi.RegionSize;
|
| + mbi64.State = mbi.State;
|
| + mbi64.Protect = mbi.Protect;
|
| + mbi64.Type = mbi.Type;
|
| + return mbi64;
|
| +}
|
| +
|
| +// NtQueryObject with a retry for size mismatch as well as a minimum size to
|
| +// retrieve (and expect).
|
| +scoped_ptr<uint8_t[]> QueryObject(
|
| + HANDLE handle,
|
| + OBJECT_INFORMATION_CLASS object_information_class,
|
| + ULONG minimum_size) {
|
| + ULONG size = minimum_size;
|
| + ULONG return_length;
|
| + scoped_ptr<uint8_t[]> buffer(new uint8_t[size]);
|
| + NTSTATUS status = crashpad::NtQueryObject(
|
| + handle, object_information_class, buffer.get(), size, &return_length);
|
| + if (status == STATUS_INFO_LENGTH_MISMATCH) {
|
| + DCHECK_GT(return_length, size);
|
| + size = return_length;
|
| + buffer.reset(new uint8_t[size]);
|
| + status = crashpad::NtQueryObject(
|
| + handle, object_information_class, buffer.get(), size, &return_length);
|
| + }
|
| +
|
| + if (!NT_SUCCESS(status)) {
|
| + NTSTATUS_LOG(ERROR, status) << "NtQueryObject";
|
| + return nullptr;
|
| + }
|
| +
|
| + DCHECK_GE(return_length, minimum_size);
|
| + return buffer;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +template <class Traits>
|
| +bool GetProcessBasicInformation(HANDLE process,
|
| + bool is_wow64,
|
| + ProcessInfo* process_info,
|
| + WinVMAddress* peb_address,
|
| + WinVMSize* peb_size) {
|
| + ULONG bytes_returned;
|
| + process_types::PROCESS_BASIC_INFORMATION<Traits> process_basic_information;
|
| + NTSTATUS status =
|
| + crashpad::NtQueryInformationProcess(process,
|
| + ProcessBasicInformation,
|
| + &process_basic_information,
|
| + sizeof(process_basic_information),
|
| + &bytes_returned);
|
| + if (!NT_SUCCESS(status)) {
|
| + NTSTATUS_LOG(ERROR, status) << "NtQueryInformationProcess";
|
| + return false;
|
| + }
|
| + if (bytes_returned != sizeof(process_basic_information)) {
|
| + LOG(ERROR) << "NtQueryInformationProcess incorrect size";
|
| + return false;
|
| + }
|
| +
|
| + // API functions (e.g. OpenProcess) take only a DWORD, so there's no sense in
|
| + // maintaining the top bits.
|
| + process_info->process_id_ =
|
| + static_cast<DWORD>(process_basic_information.UniqueProcessId);
|
| + process_info->inherited_from_process_id_ = static_cast<DWORD>(
|
| + process_basic_information.InheritedFromUniqueProcessId);
|
| +
|
| + // We now want to read the PEB to gather the rest of our information. The
|
| + // PebBaseAddress as returned above is what we want for 64-on-64 and 32-on-32,
|
| + // but for Wow64, we want to read the 32 bit PEB (a Wow64 process has both).
|
| + // The address of this is found by a second call to NtQueryInformationProcess.
|
| + if (!is_wow64) {
|
| + *peb_address = process_basic_information.PebBaseAddress;
|
| + *peb_size = sizeof(process_types::PEB<Traits>);
|
| + } else {
|
| + ULONG_PTR wow64_peb_address;
|
| + status = crashpad::NtQueryInformationProcess(process,
|
| + ProcessWow64Information,
|
| + &wow64_peb_address,
|
| + sizeof(wow64_peb_address),
|
| + &bytes_returned);
|
| + if (!NT_SUCCESS(status)) {
|
| + NTSTATUS_LOG(ERROR, status), "NtQueryInformationProcess";
|
| + return false;
|
| + }
|
| + if (bytes_returned != sizeof(wow64_peb_address)) {
|
| + LOG(ERROR) << "NtQueryInformationProcess incorrect size";
|
| + return false;
|
| + }
|
| + *peb_address = wow64_peb_address;
|
| + *peb_size = sizeof(process_types::PEB<process_types::internal::Traits32>);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +template <class Traits>
|
| +bool ReadProcessData(HANDLE process,
|
| + WinVMAddress peb_address_vmaddr,
|
| + ProcessInfo* process_info) {
|
| + typename Traits::Pointer peb_address;
|
| + if (!AssignIfInRange(&peb_address, peb_address_vmaddr)) {
|
| + LOG(ERROR) << base::StringPrintf("peb address 0x%x out of range",
|
| + peb_address_vmaddr);
|
| + return false;
|
| + }
|
| +
|
| + // Try to read the process environment block.
|
| + process_types::PEB<Traits> peb;
|
| + if (!ReadStruct(process, peb_address, &peb))
|
| + return false;
|
| +
|
| + process_types::RTL_USER_PROCESS_PARAMETERS<Traits> process_parameters;
|
| + if (!ReadStruct(process, peb.ProcessParameters, &process_parameters))
|
| + return false;
|
| +
|
| + if (!ReadUnicodeString(process,
|
| + process_parameters.CommandLine,
|
| + &process_info->command_line_)) {
|
| + return false;
|
| + }
|
| +
|
| + process_types::PEB_LDR_DATA<Traits> peb_ldr_data;
|
| + if (!ReadStruct(process, peb.Ldr, &peb_ldr_data))
|
| + return false;
|
| +
|
| + process_types::LDR_DATA_TABLE_ENTRY<Traits> ldr_data_table_entry;
|
| +
|
| + // Include the first module in the memory order list to get our the main
|
| + // executable's name, as it's not included in initialization order below.
|
| + if (!ReadStruct(process,
|
| + static_cast<WinVMAddress>(
|
| + peb_ldr_data.InMemoryOrderModuleList.Flink) -
|
| + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
|
| + InMemoryOrderLinks),
|
| + &ldr_data_table_entry)) {
|
| + return false;
|
| + }
|
| + ProcessInfo::Module module;
|
| + if (!ReadUnicodeString(
|
| + process, ldr_data_table_entry.FullDllName, &module.name)) {
|
| + return false;
|
| + }
|
| + module.dll_base = ldr_data_table_entry.DllBase;
|
| + module.size = ldr_data_table_entry.SizeOfImage;
|
| + module.timestamp = ldr_data_table_entry.TimeDateStamp;
|
| + process_info->modules_.push_back(module);
|
| +
|
| + // Walk the PEB LDR structure (doubly-linked list) to get the list of loaded
|
| + // modules. We use this method rather than EnumProcessModules to get the
|
| + // modules in initialization order rather than memory order.
|
| + typename Traits::Pointer last =
|
| + peb_ldr_data.InInitializationOrderModuleList.Blink;
|
| + for (typename Traits::Pointer cur =
|
| + peb_ldr_data.InInitializationOrderModuleList.Flink;
|
| + ;
|
| + cur = ldr_data_table_entry.InInitializationOrderLinks.Flink) {
|
| + // |cur| is the pointer to the LIST_ENTRY embedded in the
|
| + // LDR_DATA_TABLE_ENTRY, in the target process's address space. So we need
|
| + // to read from the target, and also offset back to the beginning of the
|
| + // structure.
|
| + if (!ReadStruct(process,
|
| + static_cast<WinVMAddress>(cur) -
|
| + offsetof(process_types::LDR_DATA_TABLE_ENTRY<Traits>,
|
| + InInitializationOrderLinks),
|
| + &ldr_data_table_entry)) {
|
| + break;
|
| + }
|
| + // TODO(scottmg): Capture Checksum, etc. too?
|
| + if (!ReadUnicodeString(
|
| + process, ldr_data_table_entry.FullDllName, &module.name)) {
|
| + break;
|
| + }
|
| + module.dll_base = ldr_data_table_entry.DllBase;
|
| + module.size = ldr_data_table_entry.SizeOfImage;
|
| + module.timestamp = ldr_data_table_entry.TimeDateStamp;
|
| + process_info->modules_.push_back(module);
|
| + if (cur == last)
|
| + break;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool ReadMemoryInfo(HANDLE process, bool is_64_bit, ProcessInfo* process_info) {
|
| + DCHECK(process_info->memory_info_.empty());
|
| +
|
| + const WinVMAddress min_address = 0;
|
| + // We can't use GetSystemInfo() to get the address space range for another
|
| + // process. VirtualQueryEx() will fail with ERROR_INVALID_PARAMETER if the
|
| + // address is above the highest memory address accessible to the process, so
|
| + // we just probe the entire potential range (2^32 for x86, or 2^64 for x64).
|
| + const WinVMAddress max_address = is_64_bit
|
| + ? std::numeric_limits<uint64_t>::max()
|
| + : std::numeric_limits<uint32_t>::max();
|
| + MEMORY_BASIC_INFORMATION memory_basic_information;
|
| + for (WinVMAddress address = min_address; address <= max_address;
|
| + address += memory_basic_information.RegionSize) {
|
| + size_t result = VirtualQueryEx(process,
|
| + reinterpret_cast<void*>(address),
|
| + &memory_basic_information,
|
| + sizeof(memory_basic_information));
|
| + if (result == 0) {
|
| + if (GetLastError() == ERROR_INVALID_PARAMETER)
|
| + break;
|
| + PLOG(ERROR) << "VirtualQueryEx";
|
| + return false;
|
| + }
|
| +
|
| + process_info->memory_info_.push_back(
|
| + MemoryBasicInformationToMemoryBasicInformation64(
|
| + memory_basic_information));
|
| +
|
| + if (memory_basic_information.RegionSize == 0) {
|
| + LOG(ERROR) << "RegionSize == 0";
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +std::vector<ProcessInfo::Handle> ProcessInfo::BuildHandleVector(
|
| + HANDLE process) const {
|
| + ULONG buffer_size = 2 * 1024 * 1024;
|
| + scoped_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]);
|
| +
|
| + // Typically if the buffer were too small, STATUS_INFO_LENGTH_MISMATCH would
|
| + // return the correct size in the final argument, but it does not for
|
| + // SystemExtendedHandleInformation, so we loop and attempt larger sizes.
|
| + NTSTATUS status;
|
| + ULONG returned_length;
|
| + for (int tries = 0; tries < 5; ++tries) {
|
| + status = crashpad::NtQuerySystemInformation(
|
| + static_cast<SYSTEM_INFORMATION_CLASS>(SystemExtendedHandleInformation),
|
| + buffer.get(),
|
| + buffer_size,
|
| + &returned_length);
|
| + if (NT_SUCCESS(status) || status != STATUS_INFO_LENGTH_MISMATCH)
|
| + break;
|
| +
|
| + buffer_size *= 2;
|
| + buffer.reset();
|
| + buffer.reset(new uint8_t[buffer_size]);
|
| + }
|
| +
|
| + if (!NT_SUCCESS(status)) {
|
| + NTSTATUS_LOG(ERROR, status)
|
| + << "NtQuerySystemInformation SystemExtendedHandleInformation";
|
| + return std::vector<Handle>();
|
| + }
|
| +
|
| + const auto& system_handle_information_ex =
|
| + *reinterpret_cast<process_types::SYSTEM_HANDLE_INFORMATION_EX*>(
|
| + buffer.get());
|
| +
|
| + DCHECK_LE(offsetof(process_types::SYSTEM_HANDLE_INFORMATION_EX, Handles) +
|
| + system_handle_information_ex.NumberOfHandles *
|
| + sizeof(system_handle_information_ex.Handles[0]),
|
| + returned_length);
|
| +
|
| + std::vector<Handle> handles;
|
| +
|
| + for (size_t i = 0; i < system_handle_information_ex.NumberOfHandles; ++i) {
|
| + const auto& handle = system_handle_information_ex.Handles[i];
|
| + if (handle.UniqueProcessId != process_id_)
|
| + continue;
|
| +
|
| + Handle result_handle;
|
| + result_handle.handle = HandleToInt(handle.HandleValue);
|
| + result_handle.attributes = handle.HandleAttributes;
|
| + result_handle.granted_access = handle.GrantedAccess;
|
| +
|
| + // TODO(scottmg): Could special case for self.
|
| + HANDLE dup_handle;
|
| + if (DuplicateHandle(process,
|
| + handle.HandleValue,
|
| + GetCurrentProcess(),
|
| + &dup_handle,
|
| + 0,
|
| + false,
|
| + DUPLICATE_SAME_ACCESS)) {
|
| + // Some handles cannot be duplicated, for example, handles of type
|
| + // EtwRegistration. If we fail to duplicate, then we can't gather any more
|
| + // information, but include the information that we do have already.
|
| + ScopedKernelHANDLE scoped_dup_handle(dup_handle);
|
| +
|
| + scoped_ptr<uint8_t[]> object_basic_information_buffer =
|
| + QueryObject(dup_handle,
|
| + ObjectBasicInformation,
|
| + sizeof(PUBLIC_OBJECT_BASIC_INFORMATION));
|
| + if (object_basic_information_buffer) {
|
| + PUBLIC_OBJECT_BASIC_INFORMATION* object_basic_information =
|
| + reinterpret_cast<PUBLIC_OBJECT_BASIC_INFORMATION*>(
|
| + object_basic_information_buffer.get());
|
| + // The Attributes and GrantedAccess sometimes differ slightly between
|
| + // the data retrieved in SYSTEM_HANDLE_INFORMATION_EX and
|
| + // PUBLIC_OBJECT_TYPE_INFORMATION. We prefer the values in
|
| + // SYSTEM_HANDLE_INFORMATION_EX because they were retrieved from the
|
| + // target process, rather than on the duplicated handle, so don't use
|
| + // them here.
|
| +
|
| + // Subtract one to account for our DuplicateHandle() and another for
|
| + // NtQueryObject() while the query was being executed.
|
| + DCHECK_GT(object_basic_information->PointerCount, 2u);
|
| + result_handle.pointer_count =
|
| + object_basic_information->PointerCount - 2;
|
| +
|
| + // Subtract one to account for our DuplicateHandle().
|
| + DCHECK_GT(object_basic_information->HandleCount, 1u);
|
| + result_handle.handle_count = object_basic_information->HandleCount - 1;
|
| + }
|
| +
|
| + scoped_ptr<uint8_t[]> object_type_information_buffer =
|
| + QueryObject(dup_handle,
|
| + ObjectTypeInformation,
|
| + sizeof(PUBLIC_OBJECT_TYPE_INFORMATION));
|
| + if (object_type_information_buffer) {
|
| + PUBLIC_OBJECT_TYPE_INFORMATION* object_type_information =
|
| + reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION*>(
|
| + object_type_information_buffer.get());
|
| +
|
| + DCHECK_EQ(object_type_information->TypeName.Length %
|
| + sizeof(result_handle.type_name[0]),
|
| + 0u);
|
| + result_handle.type_name =
|
| + std::wstring(object_type_information->TypeName.Buffer,
|
| + object_type_information->TypeName.Length /
|
| + sizeof(result_handle.type_name[0]));
|
| + }
|
| + }
|
| +
|
| + handles.push_back(result_handle);
|
| + }
|
| + return handles;
|
| +}
|
| +
|
| +ProcessInfo::Module::Module() : name(), dll_base(0), size(0), timestamp() {
|
| +}
|
| +
|
| +ProcessInfo::Module::~Module() {
|
| +}
|
| +
|
| +ProcessInfo::Handle::Handle()
|
| + : type_name(),
|
| + handle(0),
|
| + attributes(0),
|
| + granted_access(0),
|
| + pointer_count(0),
|
| + handle_count(0) {
|
| +}
|
| +
|
| +ProcessInfo::Handle::~Handle() {
|
| +}
|
| +
|
| +ProcessInfo::ProcessInfo()
|
| + : process_id_(),
|
| + inherited_from_process_id_(),
|
| + process_(),
|
| + command_line_(),
|
| + peb_address_(0),
|
| + peb_size_(0),
|
| + modules_(),
|
| + memory_info_(),
|
| + handles_(),
|
| + is_64_bit_(false),
|
| + is_wow64_(false),
|
| + initialized_() {
|
| +}
|
| +
|
| +ProcessInfo::~ProcessInfo() {
|
| +}
|
| +
|
| +bool ProcessInfo::Initialize(HANDLE process) {
|
| + INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
| +
|
| + process_ = process;
|
| +
|
| + is_wow64_ = IsProcessWow64(process);
|
| +
|
| + if (is_wow64_) {
|
| + // If it's WoW64, then it's 32-on-64.
|
| + is_64_bit_ = false;
|
| + } else {
|
| + // Otherwise, it's either 32 on 32, or 64 on 64. Use GetSystemInfo() to
|
| + // distinguish between these two cases.
|
| + SYSTEM_INFO system_info;
|
| + GetSystemInfo(&system_info);
|
| + is_64_bit_ =
|
| + system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
|
| + }
|
| +
|
| +#if defined(ARCH_CPU_32_BITS)
|
| + if (is_64_bit_) {
|
| + LOG(ERROR) << "Reading x64 process from x86 process not supported";
|
| + return false;
|
| + }
|
| +#endif // ARCH_CPU_32_BITS
|
| +
|
| +#if defined(ARCH_CPU_64_BITS)
|
| + bool result = GetProcessBasicInformation<process_types::internal::Traits64>(
|
| + process, is_wow64_, this, &peb_address_, &peb_size_);
|
| +#else
|
| + bool result = GetProcessBasicInformation<process_types::internal::Traits32>(
|
| + process, false, this, &peb_address_, &peb_size_);
|
| +#endif // ARCH_CPU_64_BITS
|
| +
|
| + if (!result) {
|
| + LOG(ERROR) << "GetProcessBasicInformation failed";
|
| + return false;
|
| + }
|
| +
|
| + result = is_64_bit_ ? ReadProcessData<process_types::internal::Traits64>(
|
| + process, peb_address_, this)
|
| + : ReadProcessData<process_types::internal::Traits32>(
|
| + process, peb_address_, this);
|
| + if (!result) {
|
| + LOG(ERROR) << "ReadProcessData failed";
|
| + return false;
|
| + }
|
| +
|
| + if (!ReadMemoryInfo(process, is_64_bit_, this)) {
|
| + LOG(ERROR) << "ReadMemoryInfo failed";
|
| + return false;
|
| + }
|
| +
|
| + INITIALIZATION_STATE_SET_VALID(initialized_);
|
| + return true;
|
| +}
|
| +
|
| +bool ProcessInfo::Is64Bit() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return is_64_bit_;
|
| +}
|
| +
|
| +bool ProcessInfo::IsWow64() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return is_wow64_;
|
| +}
|
| +
|
| +pid_t ProcessInfo::ProcessID() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return process_id_;
|
| +}
|
| +
|
| +pid_t ProcessInfo::ParentProcessID() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return inherited_from_process_id_;
|
| +}
|
| +
|
| +bool ProcessInfo::CommandLine(std::wstring* command_line) const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + *command_line = command_line_;
|
| + return true;
|
| +}
|
| +
|
| +void ProcessInfo::Peb(WinVMAddress* peb_address, WinVMSize* peb_size) const {
|
| + *peb_address = peb_address_;
|
| + *peb_size = peb_size_;
|
| +}
|
| +
|
| +bool ProcessInfo::Modules(std::vector<Module>* modules) const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + *modules = modules_;
|
| + return true;
|
| +}
|
| +
|
| +const std::vector<MEMORY_BASIC_INFORMATION64>& ProcessInfo::MemoryInfo() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + return memory_info_;
|
| +}
|
| +
|
| +std::vector<CheckedRange<WinVMAddress, WinVMSize>>
|
| +ProcessInfo::GetReadableRanges(
|
| + const CheckedRange<WinVMAddress, WinVMSize>& range) const {
|
| + return GetReadableRangesOfMemoryMap(range, MemoryInfo());
|
| +}
|
| +
|
| +bool ProcessInfo::LoggingRangeIsFullyReadable(
|
| + const CheckedRange<WinVMAddress, WinVMSize>& range) const {
|
| + const auto ranges = GetReadableRanges(range);
|
| + if (ranges.size() != 1) {
|
| + LOG(ERROR) << base::StringPrintf(
|
| + "range at 0x%llx, size 0x%llx fully unreadable",
|
| + range.base(),
|
| + range.size());
|
| + return false;
|
| + }
|
| + if (ranges[0].base() != range.base() || ranges[0].size() != range.size()) {
|
| + LOG(ERROR) << base::StringPrintf(
|
| + "some of range at 0x%llx, size 0x%llx unreadable",
|
| + range.base(),
|
| + range.size());
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
|
| + INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
| + if (handles_.empty())
|
| + handles_ = BuildHandleVector(process_);
|
| + return handles_;
|
| +}
|
| +
|
| +std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRangesOfMemoryMap(
|
| + const CheckedRange<WinVMAddress, WinVMSize>& range,
|
| + const std::vector<MEMORY_BASIC_INFORMATION64>& memory_info) {
|
| + using Range = CheckedRange<WinVMAddress, WinVMSize>;
|
| +
|
| + // Find all the ranges that overlap the target range, maintaining their order.
|
| + std::vector<MEMORY_BASIC_INFORMATION64> overlapping;
|
| + for (const auto& mi : memory_info) {
|
| + static_assert(base::is_same<decltype(mi.BaseAddress), WinVMAddress>::value,
|
| + "expected range address to be WinVMAddress");
|
| + static_assert(base::is_same<decltype(mi.RegionSize), WinVMSize>::value,
|
| + "expected range size to be WinVMSize");
|
| + if (range.OverlapsRange(Range(mi.BaseAddress, mi.RegionSize)))
|
| + overlapping.push_back(mi);
|
| + }
|
| + if (overlapping.empty())
|
| + return std::vector<Range>();
|
| +
|
| + // For the first and last, trim to the boundary of the incoming range.
|
| + MEMORY_BASIC_INFORMATION64& front = overlapping.front();
|
| + WinVMAddress original_front_base_address = front.BaseAddress;
|
| + front.BaseAddress = std::max(front.BaseAddress, range.base());
|
| + front.RegionSize =
|
| + (original_front_base_address + front.RegionSize) - front.BaseAddress;
|
| +
|
| + MEMORY_BASIC_INFORMATION64& back = overlapping.back();
|
| + WinVMAddress back_end = back.BaseAddress + back.RegionSize;
|
| + back.RegionSize = std::min(range.end(), back_end) - back.BaseAddress;
|
| +
|
| + // Discard all non-accessible.
|
| + overlapping.erase(std::remove_if(overlapping.begin(),
|
| + overlapping.end(),
|
| + [](const MEMORY_BASIC_INFORMATION64& mbi) {
|
| + return !RegionIsAccessible(mbi);
|
| + }),
|
| + overlapping.end());
|
| + if (overlapping.empty())
|
| + return std::vector<Range>();
|
| +
|
| + // Convert to return type.
|
| + std::vector<Range> as_ranges;
|
| + for (const auto& mi : overlapping) {
|
| + as_ranges.push_back(Range(mi.BaseAddress, mi.RegionSize));
|
| + DCHECK(as_ranges.back().IsValid());
|
| + }
|
| +
|
| + // Coalesce remaining regions.
|
| + std::vector<Range> result;
|
| + result.push_back(as_ranges[0]);
|
| + for (size_t i = 1; i < as_ranges.size(); ++i) {
|
| + if (result.back().end() == as_ranges[i].base()) {
|
| + result.back().SetRange(result.back().base(),
|
| + result.back().size() + as_ranges[i].size());
|
| + } else {
|
| + result.push_back(as_ranges[i]);
|
| + }
|
| + DCHECK(result.back().IsValid());
|
| + }
|
| +
|
| + return result;
|
| +}
|
| +
|
| +} // namespace crashpad
|
|
|