Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Unified Diff: third_party/crashpad/crashpad/util/win/process_info.cc

Issue 1505213004: Copy Crashpad into the Chrome tree instead of importing it via DEPS (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments, update README.chromium Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698