xref: /aosp_15_r20/external/cronet/base/profiler/module_cache_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2018 The Chromium Authors
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 "base/profiler/module_cache.h"
6 
7 #include <objbase.h>
8 
9 #include <psapi.h>
10 
11 #include <string>
12 
13 #include "base/debug/alias.h"
14 #include "base/process/process_handle.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/win/pe_image.h"
19 #include "base/win/scoped_handle.h"
20 #include "base/win/win_util.h"
21 
22 namespace base {
23 
24 namespace {
25 
26 // Gets the unique build ID and the corresponding debug path for a module.
27 // Windows build IDs are created by a concatenation of a GUID and AGE fields
28 // found in the headers of a module. The GUID is stored in the first 16 bytes
29 // and the AGE is stored in the last 4 bytes. Returns the empty string if the
30 // function fails to get the build ID. The debug path (pdb file) can be found
31 // in the PE file and is the build time path where the debug file was produced.
32 //
33 // Example:
34 // dumpbin chrome.exe /headers | find "Format:"
35 //   ... Format: RSDS, {16B2A428-1DED-442E-9A36-FCE8CBD29726}, 10, ...
36 //
37 // The resulting buildID string of this instance of chrome.exe is
38 // "16B2A4281DED442E9A36FCE8CBD2972610".
39 //
40 // Note that the AGE field is encoded in decimal, not hex.
GetDebugInfoForModule(HMODULE module_handle,std::string * build_id,FilePath * pdb_name)41 void GetDebugInfoForModule(HMODULE module_handle,
42                            std::string* build_id,
43                            FilePath* pdb_name) {
44   GUID guid;
45   DWORD age;
46   LPCSTR pdb_file = nullptr;
47   size_t pdb_file_length = 0;
48   if (!win::PEImage(module_handle)
49            .GetDebugId(&guid, &age, &pdb_file, &pdb_file_length)) {
50     return;
51   }
52 
53   FilePath::StringType pdb_filename;
54   if (!UTF8ToWide(pdb_file, pdb_file_length, &pdb_filename))
55     return;
56   *pdb_name = FilePath(std::move(pdb_filename)).BaseName();
57 
58   auto buffer = win::WStringFromGUID(guid);
59   RemoveChars(buffer, L"{}-", &buffer);
60   buffer.append(NumberToWString(age));
61   *build_id = WideToUTF8(buffer);
62 }
63 
64 // Returns true if the address is in the address space accessible to
65 // applications and DLLs, as reported by ::GetSystemInfo.
IsValidUserSpaceAddress(uintptr_t address)66 bool IsValidUserSpaceAddress(uintptr_t address) {
67   static LPVOID max_app_addr = 0;
68   static LPVOID min_app_addr = 0;
69   if (!max_app_addr) {
70     SYSTEM_INFO sys_info;
71     ::GetSystemInfo(&sys_info);
72     max_app_addr = sys_info.lpMaximumApplicationAddress;
73     min_app_addr = sys_info.lpMinimumApplicationAddress;
74   }
75   return reinterpret_cast<LPVOID>(address) >= min_app_addr &&
76          reinterpret_cast<LPVOID>(address) <= max_app_addr;
77 }
78 
79 // Traits class to adapt GenericScopedHandle for HMODULES.
80 class ModuleHandleTraits : public win::HandleTraits {
81  public:
82   using Handle = HMODULE;
83 
84   ModuleHandleTraits() = delete;
85   ModuleHandleTraits(const ModuleHandleTraits&) = delete;
86   ModuleHandleTraits& operator=(const ModuleHandleTraits&) = delete;
87 
CloseHandle(HMODULE handle)88   static bool CloseHandle(HMODULE handle) { return ::FreeLibrary(handle) != 0; }
IsHandleValid(HMODULE handle)89   static bool IsHandleValid(HMODULE handle) { return handle != nullptr; }
NullHandle()90   static HMODULE NullHandle() { return nullptr; }
91 };
92 
93 // HMODULE is not really a handle, and has reference count semantics, so the
94 // standard VerifierTraits does not apply.
95 using ScopedModuleHandle =
96     win::GenericScopedHandle<ModuleHandleTraits, win::DummyVerifierTraits>;
97 
98 class WindowsModule : public ModuleCache::Module {
99  public:
WindowsModule(ScopedModuleHandle module_handle,const MODULEINFO module_info,const std::string & id,const FilePath & debug_basename)100   WindowsModule(ScopedModuleHandle module_handle,
101                 const MODULEINFO module_info,
102                 const std::string& id,
103                 const FilePath& debug_basename)
104       : module_handle_(std::move(module_handle)),
105         module_info_(module_info),
106         id_(id),
107         debug_basename_(debug_basename) {}
108 
109   WindowsModule(const WindowsModule&) = delete;
110   WindowsModule& operator=(const WindowsModule&) = delete;
111 
112   // ModuleCache::Module
GetBaseAddress() const113   uintptr_t GetBaseAddress() const override {
114     return reinterpret_cast<uintptr_t>(module_info_.lpBaseOfDll);
115   }
116 
GetId() const117   std::string GetId() const override { return id_; }
GetDebugBasename() const118   FilePath GetDebugBasename() const override { return debug_basename_; }
GetSize() const119   size_t GetSize() const override { return module_info_.SizeOfImage; }
IsNative() const120   bool IsNative() const override { return true; }
121 
122  private:
123   ScopedModuleHandle module_handle_;
124   const MODULEINFO module_info_;
125   std::string id_;
126   FilePath debug_basename_;
127 };
128 
GetModuleHandleForAddress(uintptr_t address)129 ScopedModuleHandle GetModuleHandleForAddress(uintptr_t address) {
130   // Record the address in crash dumps to help understand the source of
131   // GetModuleHandleEx crashes on Windows 11 observed in
132   // https://crbug.com/1297776.
133   debug::Alias(&address);
134   if (!IsValidUserSpaceAddress(address))
135     return ScopedModuleHandle(nullptr);
136 
137   HMODULE module_handle = nullptr;
138 
139   // GetModuleHandleEx() increments the module reference count, which is then
140   // managed and ultimately decremented by ScopedModuleHandle.
141   if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
142                            reinterpret_cast<LPCTSTR>(address),
143                            &module_handle)) {
144     const DWORD error = ::GetLastError();
145     DCHECK_EQ(ERROR_MOD_NOT_FOUND, static_cast<int>(error));
146   }
147   return ScopedModuleHandle(module_handle);
148 }
149 
CreateModuleForHandle(ScopedModuleHandle module_handle)150 std::unique_ptr<ModuleCache::Module> CreateModuleForHandle(
151     ScopedModuleHandle module_handle) {
152   FilePath pdb_name;
153   std::string build_id;
154   GetDebugInfoForModule(module_handle.get(), &build_id, &pdb_name);
155 
156   MODULEINFO module_info;
157   if (!::GetModuleInformation(GetCurrentProcessHandle(), module_handle.get(),
158                               &module_info, sizeof(module_info))) {
159     return nullptr;
160   }
161 
162   return std::make_unique<WindowsModule>(std::move(module_handle), module_info,
163                                          build_id, pdb_name);
164 }
165 
166 }  // namespace
167 
168 // static
CreateModuleForAddress(uintptr_t address)169 std::unique_ptr<const ModuleCache::Module> ModuleCache::CreateModuleForAddress(
170     uintptr_t address) {
171   ScopedModuleHandle module_handle = GetModuleHandleForAddress(address);
172   if (!module_handle.is_valid())
173     return nullptr;
174   return CreateModuleForHandle(std::move(module_handle));
175 }
176 
177 }  // namespace base
178