blob: 9088aabef48ffe8591dd22c148424a584a8515d3 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <psapi.h>
#include <tchar.h>
#include <base/numerics/safe_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/strings/sys_string_conversions.h>
#include <base/win/pe_image.h>
#include <base/win/win_util.h>
namespace memory_instrumentation {
namespace {
// Gets the unique build ID for a module. Windows build IDs are created by a
// concatenation of a GUID and AGE fields found in the headers of a module. The
// GUID is stored in the first 16 bytes and the AGE is stored in the last 4
// bytes. Returns the empty string if the function fails to get the build ID.
std::string MakeDebugID(const GUID& guid, DWORD age) {
return base::StringPrintf("%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%ld",
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0],
guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6],
guid.Data4[7], age);
}
} // namespace
// static
bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
mojom::RawOSMemDump* dump) {
// Creating process metrics for child processes in mac or windows requires
// additional information like ProcessHandle or port provider.
DCHECK_EQ(base::kNullProcessId, pid);
PROCESS_MEMORY_COUNTERS_EX pmc;
if (::GetProcessMemoryInfo(::GetCurrentProcess(),
reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
sizeof(pmc))) {
dump->platform_private_footprint->private_bytes = pmc.PrivateUsage;
dump->resident_set_kb =
base::saturated_cast<uint32_t>(pmc.WorkingSetSize / 1024);
}
return true;
}
// static
std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
base::ProcessId pid) {
std::vector<mojom::VmRegionPtr> maps;
std::vector<HMODULE> modules;
if (!base::win::GetLoadedModulesSnapshot(::GetCurrentProcess(), &modules))
return maps;
// Query the base address for each module, and attach it to the dump.
for (size_t i = 0; i < modules.size(); ++i) {
wchar_t module_name[MAX_PATH];
if (!::GetModuleFileName(modules[i], module_name, MAX_PATH))
continue;
MODULEINFO module_info;
if (!::GetModuleInformation(::GetCurrentProcess(), modules[i], &module_info,
sizeof(MODULEINFO))) {
continue;
}
mojom::VmRegionPtr region = mojom::VmRegion::New();
region->size_in_bytes = module_info.SizeOfImage;
region->mapped_file = base::SysWideToNativeMB(module_name);
region->start_address = reinterpret_cast<uint64_t>(module_info.lpBaseOfDll);
// The PE header field |TimeDateStamp| is required to build the PE code
// identifier which is used as a key to query symbols servers.
base::win::PEImage pe_image(module_info.lpBaseOfDll);
region->module_timestamp =
pe_image.GetNTHeaders()->FileHeader.TimeDateStamp;
GUID module_guid;
DWORD module_age;
const char* pdb_file = nullptr;
size_t pdb_file_length = 0;
if (pe_image.GetDebugId(&module_guid, &module_age, &pdb_file,
&pdb_file_length)) {
region->module_debugid = MakeDebugID(module_guid, module_age);
region->module_debug_path.assign(pdb_file, pdb_file_length);
}
maps.push_back(std::move(region));
}
return maps;
}
} // namespace memory_instrumentation