1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h"
6 
7 #include <windows.h>  // Must be in front of other Windows header files.
8 
9 #include <psapi.h>
10 #include <tchar.h>
11 
12 #include <base/numerics/safe_conversions.h>
13 #include <base/strings/stringprintf.h>
14 #include <base/strings/sys_string_conversions.h>
15 #include <base/win/pe_image.h>
16 #include <base/win/win_util.h>
17 
18 namespace memory_instrumentation {
19 
20 namespace {
21 
22 // Gets the unique build ID for a module. Windows build IDs are created by a
23 // concatenation of a GUID and AGE fields found in the headers of a module. The
24 // GUID is stored in the first 16 bytes and the AGE is stored in the last 4
25 // bytes. Returns the empty string if the function fails to get the build ID.
MakeDebugID(const GUID & guid,DWORD age)26 std::string MakeDebugID(const GUID& guid, DWORD age) {
27   return base::StringPrintf("%08lX%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%ld",
28                             guid.Data1, guid.Data2, guid.Data3, guid.Data4[0],
29                             guid.Data4[1], guid.Data4[2], guid.Data4[3],
30                             guid.Data4[4], guid.Data4[5], guid.Data4[6],
31                             guid.Data4[7], age);
32 }
33 
34 }  // namespace
35 
36 // static
FillOSMemoryDump(base::ProcessId pid,mojom::RawOSMemDump * dump)37 bool OSMetrics::FillOSMemoryDump(base::ProcessId pid,
38                                  mojom::RawOSMemDump* dump) {
39   // Creating process metrics for child processes in mac or windows requires
40   // additional information like ProcessHandle or port provider.
41   DCHECK_EQ(base::kNullProcessId, pid);
42 
43   PROCESS_MEMORY_COUNTERS_EX pmc;
44   if (::GetProcessMemoryInfo(::GetCurrentProcess(),
45                              reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
46                              sizeof(pmc))) {
47     dump->platform_private_footprint->private_bytes = pmc.PrivateUsage;
48     dump->resident_set_kb =
49         base::saturated_cast<uint32_t>(pmc.WorkingSetSize / 1024);
50   }
51   return true;
52 }
53 
54 // static
GetProcessMemoryMaps(base::ProcessId pid)55 std::vector<mojom::VmRegionPtr> OSMetrics::GetProcessMemoryMaps(
56     base::ProcessId pid) {
57   std::vector<mojom::VmRegionPtr> maps;
58   std::vector<HMODULE> modules;
59   if (!base::win::GetLoadedModulesSnapshot(::GetCurrentProcess(), &modules))
60     return maps;
61 
62   // Query the base address for each module, and attach it to the dump.
63   for (size_t i = 0; i < modules.size(); ++i) {
64     wchar_t module_name[MAX_PATH];
65     if (!::GetModuleFileName(modules[i], module_name, MAX_PATH))
66       continue;
67 
68     MODULEINFO module_info;
69     if (!::GetModuleInformation(::GetCurrentProcess(), modules[i], &module_info,
70                                 sizeof(MODULEINFO))) {
71       continue;
72     }
73     mojom::VmRegionPtr region = mojom::VmRegion::New();
74     region->size_in_bytes = module_info.SizeOfImage;
75     region->mapped_file = base::SysWideToNativeMB(module_name);
76     region->start_address = reinterpret_cast<uint64_t>(module_info.lpBaseOfDll);
77 
78     // The PE header field |TimeDateStamp| is required to build the PE code
79     // identifier which is used as a key to query symbols servers.
80     base::win::PEImage pe_image(module_info.lpBaseOfDll);
81     region->module_timestamp =
82         pe_image.GetNTHeaders()->FileHeader.TimeDateStamp;
83 
84     GUID module_guid;
85     DWORD module_age;
86     const char* pdb_file = nullptr;
87     size_t pdb_file_length = 0;
88     if (pe_image.GetDebugId(&module_guid, &module_age, &pdb_file,
89                             &pdb_file_length)) {
90       region->module_debugid = MakeDebugID(module_guid, module_age);
91       region->module_debug_path.assign(pdb_file, pdb_file_length);
92     }
93 
94     maps.push_back(std::move(region));
95   }
96   return maps;
97 }
98 
99 }  // namespace memory_instrumentation
100