blob: 047717e9806e88ede548e620047641b024d736bc [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2014 The Chromium Authors
[email protected]4ddbd1422014-02-11 05:12:072// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4#include "base/debug/gdi_debug_util_win.h"
5
Takuto Ikutac8d6b16f2024-04-15 16:59:196#include <windows.h>
Peter Kasting134ef9af2024-12-28 02:30:097#include <winternl.h>
Takuto Ikutac8d6b16f2024-04-15 16:59:198
Robert Liao58bb1db2019-06-08 02:45:239#include <TlHelp32.h>
[email protected]4ddbd1422014-02-11 05:12:0710#include <psapi.h>
aviebe805c2015-12-24 08:20:2811#include <stddef.h>
[email protected]4ddbd1422014-02-11 05:12:0712
Arthur Sonzognie5fff99c2024-02-21 15:58:2413#include <algorithm>
14#include <cmath>
15#include <optional>
16
[email protected]4ddbd1422014-02-11 05:12:0717#include "base/debug/alias.h"
18#include "base/logging.h"
Peter Boström54119652024-11-14 00:16:3819#include "base/notreached.h"
Robert Liao58bb1db2019-06-08 02:45:2320#include "base/process/process.h"
Peter Kasting9d13b9702025-01-31 15:15:5121#include "base/win/scoped_gdi_object.h"
[email protected]4ddbd1422014-02-11 05:12:0722#include "base/win/scoped_handle.h"
anpol80e1d0d2016-11-02 11:07:0323#include "base/win/win_util.h"
Robert Liao58bb1db2019-06-08 02:45:2324#include "base/win/windows_version.h"
[email protected]4ddbd1422014-02-11 05:12:0725
26namespace {
27
Robert Liao58bb1db2019-06-08 02:45:2328// A partial PEB up until GdiSharedHandleTable.
29// Derived from the ntdll symbols (ntdll!_PEB).
30template <typename PointerType>
31struct PartialWinPeb {
32 unsigned char InheritedAddressSpace;
33 unsigned char ReadImageFileExecOptions;
34 unsigned char BeingDebugged;
35 unsigned char ImageUsesLargePages : 1;
36 unsigned char IsProtectedProcess : 1;
37 unsigned char IsLegacyProcess : 1;
38 unsigned char IsImageDynamicallyRelocated : 1;
39 unsigned char SkipPatchingUser32Forwarders : 1;
40 unsigned char IsAppContainer : 1;
41 unsigned char IsProtectedProcessLight : 1;
42 unsigned char IsLongPathAwareProcess : 1;
43 PointerType Mutant;
44 PointerType ImageBaseAddress;
45 PointerType Ldr;
46 PointerType ProcessParamters;
47 PointerType SubSystemData;
48 PointerType ProcessHeap;
49 PointerType FastPebLock;
50 PointerType AtlThunkSListPtr;
51 PointerType IFEOKey;
52 uint32_t ProcessInJob : 1;
53 uint32_t ProcessInitializing : 1;
54 uint32_t ProcessUsingVEH : 1;
55 uint32_t ProcessUsingVCH : 1;
56 uint32_t ProcessUsingFTH : 1;
57 uint32_t ProcessPreviouslyThrottled : 1;
58 uint32_t ProcessCurrentlyThrottled : 1;
59 uint32_t ProcessImagesHotPatched : 1;
60 PointerType KernelCallbackTable;
61 uint32_t SystemReserved;
62 uint32_t AtlThunkSListPtr32;
63 PointerType ApiSetMap;
64 uint32_t TlsExpansionCounter;
65 PointerType TlsBitmap;
66 uint32_t TlsBitmapBits[2];
67 PointerType ReadOnlySharedMemoryBase;
68 PointerType HotpatchInformation;
69 PointerType ReadOnlyStaticServerData;
70 PointerType AnsiCodePageData;
71 PointerType OemCodePageData;
72 PointerType UnicodeCaseTableData;
73 uint32_t NumberOfProcessors;
74 uint32_t NtGlobalFlag;
75 uint64_t CriticalSectionTimeout;
76 PointerType HeapSegmentReserve;
77 PointerType HeapSegmentCommit;
78 PointerType HeapDeCommitTotalFreeThreshold;
79 PointerType HeapDeCommitFreeBlockThreshold;
80 uint32_t NumberOfHeaps;
81 uint32_t MaximumNumberOfHeaps;
82 PointerType ProcessHeaps;
83 PointerType GdiSharedHandleTable;
84};
85
86// Found from
87// https://stackoverflow.com/questions/13905661/how-to-get-list-of-gdi-handles.
88enum GdiHandleType : USHORT {
89 kDC = 1,
90 kRegion = 4,
91 kBitmap = 5,
92 kPalette = 8,
93 kFont = 10,
94 kBrush = 16,
95 kPen = 48,
96};
97
98// Adapted from GDICELL.
99template <typename PointerType>
100struct GdiTableEntry {
101 PointerType pKernelAddress;
102 USHORT wProcessId;
103 USHORT wCount;
104 USHORT wUpper;
105 GdiHandleType wType;
106 PointerType pUserAddress;
107};
108
109// Types and names used for regular processes.
110struct RegularProcessTypes {
111 using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
112 static const char* query_information_process_name;
113 // PROCESS_BASIC_INFORMATION
114 struct ProcessBasicInformation {
115 PVOID Reserved1;
116 PVOID PebBaseAddress;
117 PVOID Reserved2[2];
118 ULONG_PTR UniqueProcessId;
119 PVOID Reserved3;
120 };
121
122 using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
123 IN PVOID BaseAddress,
124 OUT PVOID Buffer,
125 IN SIZE_T Size,
126 OUT PSIZE_T NumberOfBytesRead);
127 static const char* read_virtual_memory_func_name;
128 using NativePointerType = PVOID;
129};
130
131// static
132const char* RegularProcessTypes::query_information_process_name =
133 "NtQueryInformationProcess";
134
135// static
136const char* RegularProcessTypes::read_virtual_memory_func_name =
137 "NtReadVirtualMemory";
138
139// Types and names used for WOW based processes.
140struct WowProcessTypes {
141 // http://crbug.com/972185: Clang doesn't handle PVOID64 correctly, so we use
142 // uint64_t as a substitute.
143
144 // NtWow64QueryInformationProcess64 and NtQueryInformationProcess share the
145 // same signature.
146 using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
147 static const char* query_information_process_name;
148 // PROCESS_BASIC_INFORMATION_WOW64
149 struct ProcessBasicInformation {
150 PVOID Reserved1[2];
151 uint64_t PebBaseAddress;
152 PVOID Reserved2[4];
153 ULONG_PTR UniqueProcessId[2];
154 PVOID Reserved3[2];
155 };
156
157 using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
158 IN uint64_t BaseAddress,
159 OUT PVOID Buffer,
160 IN ULONG64 Size,
161 OUT PULONG64 NumberOfBytesRead);
162 static const char* read_virtual_memory_func_name;
163 using NativePointerType = uint64_t;
164};
165
166// static
167const char* WowProcessTypes::query_information_process_name =
168 "NtWow64QueryInformationProcess64";
169
170// static
171const char* WowProcessTypes::read_virtual_memory_func_name =
172 "NtWow64ReadVirtualMemory64";
173
174// To prevent from having to write a regular and WOW codepaths that do the same
175// thing with different structures and functions, GetGdiTableEntries is
176// templated to expect either RegularProcessTypes or WowProcessTypes.
177template <typename ProcessType>
178std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
179GetGdiTableEntries(const base::Process& process) {
180 using GdiTableEntryVector =
181 std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>;
182 HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
Peter Kasting134ef9af2024-12-28 02:30:09183 if (!ntdll) {
Robert Liao58bb1db2019-06-08 02:45:23184 return GdiTableEntryVector();
Peter Kasting134ef9af2024-12-28 02:30:09185 }
Robert Liao58bb1db2019-06-08 02:45:23186
187 static auto query_information_process_func =
188 reinterpret_cast<typename ProcessType::QueryInformationProcessFunc*>(
189 GetProcAddress(ntdll, ProcessType::query_information_process_name));
190 if (!query_information_process_func) {
191 LOG(ERROR) << ProcessType::query_information_process_name << " Missing";
192 return GdiTableEntryVector();
193 }
194
195 typename ProcessType::ProcessBasicInformation basic_info;
196 NTSTATUS result =
197 query_information_process_func(process.Handle(), ProcessBasicInformation,
198 &basic_info, sizeof(basic_info), nullptr);
199 if (result != 0) {
200 LOG(ERROR) << ProcessType::query_information_process_name << " Failed "
201 << std::hex << result;
202 return GdiTableEntryVector();
203 }
204
205 static auto read_virtual_mem_func =
206 reinterpret_cast<typename ProcessType::ReadVirtualMemoryFunc*>(
207 GetProcAddress(ntdll, ProcessType::read_virtual_memory_func_name));
208 if (!read_virtual_mem_func) {
209 LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " Missing";
210 return GdiTableEntryVector();
211 }
212
213 PartialWinPeb<typename ProcessType::NativePointerType> peb;
214 result = read_virtual_mem_func(process.Handle(), basic_info.PebBaseAddress,
215 &peb, sizeof(peb), nullptr);
216 if (result != 0) {
217 LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " PEB Failed "
218 << std::hex << result;
219 return GdiTableEntryVector();
220 }
221
222 // Estimated size derived from address space allocation of the table:
223 // Windows 10
224 // 32-bit Size: 1052672 bytes
225 // 64-bit Size: 1576960 bytes
226 // sizeof(GdiTableEntry)
227 // 32-bit: 16 bytes
228 // 64-bit: 24 bytes
229 // Entry Count
230 // 32-bit: 65792
231 // 64-bit: 65706ish
Peter Kasting134ef9af2024-12-28 02:30:09232 // So we'll take a look at 65536 entries since that's the maximum handle
233 // count.
Robert Liao58bb1db2019-06-08 02:45:23234 constexpr int kGdiTableEntryCount = 65536;
235 GdiTableEntryVector entries;
236 entries.resize(kGdiTableEntryCount);
237 result = read_virtual_mem_func(
238 process.Handle(), peb.GdiSharedHandleTable, &entries[0],
239 sizeof(typename GdiTableEntryVector::value_type) * entries.size(),
240 nullptr);
241 if (result != 0) {
242 LOG(ERROR) << ProcessType::read_virtual_memory_func_name
243 << " GDI Handle Table Failed " << std::hex << result;
244 return GdiTableEntryVector();
245 }
246
247 return entries;
248}
249
250// Iterates through |gdi_table| and finds handles that belong to |pid|,
251// incrementing the appropriate fields in |base::debug::GdiHandleCounts|.
252template <typename PointerType>
253base::debug::GdiHandleCounts CountHandleTypesFromTable(
254 DWORD pid,
255 const std::vector<GdiTableEntry<PointerType>>& gdi_table) {
256 base::debug::GdiHandleCounts counts{};
257 for (const auto& entry : gdi_table) {
Peter Kasting134ef9af2024-12-28 02:30:09258 if (entry.wProcessId != pid) {
Robert Liao58bb1db2019-06-08 02:45:23259 continue;
Peter Kasting134ef9af2024-12-28 02:30:09260 }
Robert Liao58bb1db2019-06-08 02:45:23261
262 switch (entry.wType & 0x7F) {
263 case GdiHandleType::kDC:
264 ++counts.dcs;
265 break;
266 case GdiHandleType::kRegion:
267 ++counts.regions;
268 break;
269 case GdiHandleType::kBitmap:
270 ++counts.bitmaps;
271 break;
272 case GdiHandleType::kPalette:
273 ++counts.palettes;
274 break;
275 case GdiHandleType::kFont:
276 ++counts.fonts;
277 break;
278 case GdiHandleType::kBrush:
279 ++counts.brushes;
280 break;
281 case GdiHandleType::kPen:
282 ++counts.pens;
283 break;
284 default:
285 ++counts.unknown;
286 break;
287 }
288 }
289 counts.total_tracked = counts.dcs + counts.regions + counts.bitmaps +
290 counts.palettes + counts.fonts + counts.brushes +
291 counts.pens + counts.unknown;
292 return counts;
293}
294
295template <typename ProcessType>
Arthur Sonzognie5fff99c2024-02-21 15:58:24296std::optional<base::debug::GdiHandleCounts> CollectGdiHandleCountsImpl(
Robert Liao58bb1db2019-06-08 02:45:23297 DWORD pid) {
298 base::Process process = base::Process::OpenWithExtraPrivileges(pid);
Peter Kasting134ef9af2024-12-28 02:30:09299 if (!process.IsValid()) {
Arthur Sonzognie5fff99c2024-02-21 15:58:24300 return std::nullopt;
Peter Kasting134ef9af2024-12-28 02:30:09301 }
Robert Liao58bb1db2019-06-08 02:45:23302
303 std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
304 gdi_entries = GetGdiTableEntries<ProcessType>(process);
305 return CountHandleTypesFromTable(pid, gdi_entries);
306}
307
308// Returns the GDI Handle counts from the GDI Shared handle table. Empty on
309// failure.
Arthur Sonzognie5fff99c2024-02-21 15:58:24310std::optional<base::debug::GdiHandleCounts> CollectGdiHandleCounts(DWORD pid) {
Sarah Murphye91a5a302021-10-19 00:23:17311 if (base::win::OSInfo::GetInstance()->IsWowX86OnAMD64()) {
Robert Liao58bb1db2019-06-08 02:45:23312 return CollectGdiHandleCountsImpl<WowProcessTypes>(pid);
313 }
314
315 return CollectGdiHandleCountsImpl<RegularProcessTypes>(pid);
316}
317
Albert J. Wongf6f1e9d22019-05-09 20:58:22318constexpr size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
319
Peter Kastingf541f7782023-03-10 23:44:46320NOINLINE HANDLE GetToolhelpSnapshot() {
pkasting767ea6b2014-09-29 20:41:20321 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
322 CHECK_NE(INVALID_HANDLE_VALUE, snapshot);
Albert J. Wongf6f1e9d22019-05-09 20:58:22323 return snapshot;
324}
325
Peter Kastingf541f7782023-03-10 23:44:46326NOINLINE void GetFirstProcess(HANDLE snapshot, PROCESSENTRY32* proc_entry) {
Albert J. Wongf6f1e9d22019-05-09 20:58:22327 proc_entry->dwSize = sizeof(PROCESSENTRY32);
328 CHECK(Process32First(snapshot, proc_entry));
329}
330
Peter Kastingf541f7782023-03-10 23:44:46331NOINLINE void CrashIfExcessiveHandles(DWORD num_gdi_handles) {
Albert J. Wongf6f1e9d22019-05-09 20:58:22332 // By default, Windows 10 allows a max of 10,000 GDI handles per process.
333 // Number found by inspecting
334 //
335 // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\
336 // CurrentVersion\Windows\GDIProcessHandleQuota
337 //
338 // on a Windows 10 laptop.
339 static constexpr DWORD kLotsOfHandles = 9990;
340 CHECK_LE(num_gdi_handles, kLotsOfHandles);
341}
342
Peter Kastingf541f7782023-03-10 23:44:46343NOINLINE void CrashIfPagefileUsageTooLarge(
344 const PROCESS_MEMORY_COUNTERS_EX& pmc) {
Albert J. Wongf6f1e9d22019-05-09 20:58:22345 CHECK_LE(pmc.PagefileUsage, kLotsOfMemory);
346}
347
Peter Kastingf541f7782023-03-10 23:44:46348NOINLINE void CrashIfPrivateUsageTooLarge(
349 const PROCESS_MEMORY_COUNTERS_EX& pmc) {
Albert J. Wongf6f1e9d22019-05-09 20:58:22350 CHECK_LE(pmc.PrivateUsage, kLotsOfMemory);
351}
352
Peter Kastingf541f7782023-03-10 23:44:46353NOINLINE void CrashIfCannotAllocateSmallBitmap(BITMAPINFOHEADER* header,
Albert J. Wongf6f1e9d22019-05-09 20:58:22354 HANDLE shared_section) {
355 void* small_data = nullptr;
356 base::debug::Alias(&small_data);
357 header->biWidth = 5;
358 header->biHeight = -5;
Peter Kasting9d13b9702025-01-31 15:15:51359 base::win::ScopedGDIObject<HBITMAP> small_bitmap(
Albert J. Wongf6f1e9d22019-05-09 20:58:22360 CreateDIBSection(nullptr, reinterpret_cast<BITMAPINFO*>(&header), 0,
Peter Kasting9d13b9702025-01-31 15:15:51361 &small_data, shared_section, 0));
362 CHECK(small_bitmap.is_valid());
Albert J. Wongf6f1e9d22019-05-09 20:58:22363}
364
Peter Kastingf541f7782023-03-10 23:44:46365NOINLINE void GetProcessMemoryInfo(PROCESS_MEMORY_COUNTERS_EX* pmc) {
Albert J. Wongf6f1e9d22019-05-09 20:58:22366 pmc->cb = sizeof(*pmc);
367 CHECK(GetProcessMemoryInfo(GetCurrentProcess(),
368 reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(pmc),
369 sizeof(*pmc)));
370}
371
Peter Kastingf541f7782023-03-10 23:44:46372NOINLINE DWORD GetNumGdiHandles() {
Albert J. Wongf6f1e9d22019-05-09 20:58:22373 DWORD num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
374 if (num_gdi_handles == 0) {
375 DWORD get_gui_resources_error = GetLastError();
376 base::debug::Alias(&get_gui_resources_error);
Peter Boström54119652024-11-14 00:16:38377 NOTREACHED();
Albert J. Wongf6f1e9d22019-05-09 20:58:22378 }
379 return num_gdi_handles;
380}
381
382void CollectChildGDIUsageAndDie(DWORD parent_pid) {
383 HANDLE snapshot = GetToolhelpSnapshot();
[email protected]4ddbd1422014-02-11 05:12:07384
anpol9625fdd2016-11-05 09:56:20385 int total_process_count = 0;
386 base::debug::Alias(&total_process_count);
Peter Kastingd892d342022-05-27 21:56:36387 DWORD total_peak_gdi_count = 0;
anpol9625fdd2016-11-05 09:56:20388 base::debug::Alias(&total_peak_gdi_count);
Peter Kastingd892d342022-05-27 21:56:36389 DWORD total_gdi_count = 0;
anpol9625fdd2016-11-05 09:56:20390 base::debug::Alias(&total_gdi_count);
Peter Kastingd892d342022-05-27 21:56:36391 DWORD total_user_count = 0;
anpol9625fdd2016-11-05 09:56:20392 base::debug::Alias(&total_user_count);
393
[email protected]4ddbd1422014-02-11 05:12:07394 int child_count = 0;
395 base::debug::Alias(&child_count);
Peter Kastingd892d342022-05-27 21:56:36396 DWORD peak_gdi_count = 0;
[email protected]4ddbd1422014-02-11 05:12:07397 base::debug::Alias(&peak_gdi_count);
Peter Kastingd892d342022-05-27 21:56:36398 DWORD sum_gdi_count = 0;
[email protected]4ddbd1422014-02-11 05:12:07399 base::debug::Alias(&sum_gdi_count);
Peter Kastingd892d342022-05-27 21:56:36400 DWORD sum_user_count = 0;
[email protected]4ddbd1422014-02-11 05:12:07401 base::debug::Alias(&sum_user_count);
402
Albert J. Wongf6f1e9d22019-05-09 20:58:22403 PROCESSENTRY32 proc_entry = {};
404 GetFirstProcess(snapshot, &proc_entry);
[email protected]4ddbd1422014-02-11 05:12:07405
406 do {
Peter Kasting134ef9af2024-12-28 02:30:09407 base::win::ScopedHandle process(OpenProcess(
408 PROCESS_QUERY_INFORMATION, FALSE, proc_entry.th32ProcessID));
409 if (!process.is_valid()) {
[email protected]4ddbd1422014-02-11 05:12:07410 continue;
Peter Kasting134ef9af2024-12-28 02:30:09411 }
[email protected]4ddbd1422014-02-11 05:12:07412
Peter Kastingd892d342022-05-27 21:56:36413 DWORD num_gdi_handles = GetGuiResources(process.get(), GR_GDIOBJECTS);
414 DWORD num_user_handles = GetGuiResources(process.get(), GR_USEROBJECTS);
[email protected]4ddbd1422014-02-11 05:12:07415
anpol9625fdd2016-11-05 09:56:20416 // Compute sum and peak counts for all processes.
417 ++total_process_count;
418 total_user_count += num_user_handles;
419 total_gdi_count += num_gdi_handles;
420 total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles);
421
Peter Kasting134ef9af2024-12-28 02:30:09422 if (parent_pid != proc_entry.th32ParentProcessID) {
anpol9625fdd2016-11-05 09:56:20423 continue;
Peter Kasting134ef9af2024-12-28 02:30:09424 }
anpol9625fdd2016-11-05 09:56:20425
426 // Compute sum and peak counts for child processes.
[email protected]4ddbd1422014-02-11 05:12:07427 ++child_count;
428 sum_user_count += num_user_handles;
429 sum_gdi_count += num_gdi_handles;
anpol9625fdd2016-11-05 09:56:20430 peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles);
pkasting767ea6b2014-09-29 20:41:20431 } while (Process32Next(snapshot, &proc_entry));
[email protected]4ddbd1422014-02-11 05:12:07432
pkasting767ea6b2014-09-29 20:41:20433 CloseHandle(snapshot);
Peter Boström54119652024-11-14 00:16:38434 NOTREACHED();
[email protected]4ddbd1422014-02-11 05:12:07435}
436
437} // namespace
438
439namespace base {
440namespace debug {
441
anpol80e1d0d2016-11-02 11:07:03442void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) {
[email protected]4ddbd1422014-02-11 05:12:07443 // Make sure parameters are saved in the minidump.
pkasting767ea6b2014-09-29 20:41:20444 DWORD last_error = GetLastError();
anpol80e1d0d2016-11-02 11:07:03445 bool is_gdi_available = base::win::IsUser32AndGdi32Available();
[email protected]4ddbd1422014-02-11 05:12:07446
anpol80e1d0d2016-11-02 11:07:03447 LONG width = header ? header->biWidth : 0;
448 LONG height = header ? header->biHeight : 0;
[email protected]4ddbd1422014-02-11 05:12:07449
450 base::debug::Alias(&last_error);
anpol80e1d0d2016-11-02 11:07:03451 base::debug::Alias(&is_gdi_available);
[email protected]4ddbd1422014-02-11 05:12:07452 base::debug::Alias(&width);
anpol80e1d0d2016-11-02 11:07:03453 base::debug::Alias(&height);
[email protected]4ddbd1422014-02-11 05:12:07454 base::debug::Alias(&shared_section);
455
pkasting189c4d962015-07-21 01:33:54456 DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
Albert J. Wongf6f1e9d22019-05-09 20:58:22457 DWORD num_gdi_handles = GetNumGdiHandles();
Robert Liao8812aed2021-04-06 16:47:14458 DWORD peak_gdi_handles =
459 GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS_PEAK);
Robert Liao9845ccd2021-04-06 20:20:39460 DWORD num_global_gdi_handles = GetGuiResources(GR_GLOBAL, GR_GDIOBJECTS);
461 DWORD num_global_user_handles = GetGuiResources(GR_GLOBAL, GR_USEROBJECTS);
[email protected]4ddbd1422014-02-11 05:12:07462
463 base::debug::Alias(&num_gdi_handles);
464 base::debug::Alias(&num_user_handles);
Robert Liao8812aed2021-04-06 16:47:14465 base::debug::Alias(&peak_gdi_handles);
Robert Liao9845ccd2021-04-06 20:20:39466 base::debug::Alias(&num_global_gdi_handles);
467 base::debug::Alias(&num_global_user_handles);
[email protected]4ddbd1422014-02-11 05:12:07468
Arthur Sonzognie5fff99c2024-02-21 15:58:24469 std::optional<GdiHandleCounts> optional_handle_counts =
Robert Liao58bb1db2019-06-08 02:45:23470 CollectGdiHandleCounts(GetCurrentProcessId());
471 bool handle_counts_set = optional_handle_counts.has_value();
472 GdiHandleCounts handle_counts =
473 optional_handle_counts.value_or(GdiHandleCounts());
474 int tracked_dcs = handle_counts.dcs;
475 int tracked_regions = handle_counts.regions;
476 int tracked_bitmaps = handle_counts.bitmaps;
477 int tracked_palettes = handle_counts.palettes;
478 int tracked_fonts = handle_counts.fonts;
479 int tracked_brushes = handle_counts.brushes;
480 int tracked_pens = handle_counts.pens;
481 int tracked_unknown_handles = handle_counts.unknown;
482 int tracked_total = handle_counts.total_tracked;
483
484 base::debug::Alias(&handle_counts_set);
485 base::debug::Alias(&tracked_dcs);
486 base::debug::Alias(&tracked_regions);
487 base::debug::Alias(&tracked_bitmaps);
488 base::debug::Alias(&tracked_palettes);
489 base::debug::Alias(&tracked_fonts);
490 base::debug::Alias(&tracked_brushes);
491 base::debug::Alias(&tracked_pens);
492 base::debug::Alias(&tracked_unknown_handles);
493 base::debug::Alias(&tracked_total);
494
Albert J. Wongf6f1e9d22019-05-09 20:58:22495 CrashIfExcessiveHandles(num_gdi_handles);
[email protected]4ddbd1422014-02-11 05:12:07496
497 PROCESS_MEMORY_COUNTERS_EX pmc;
Albert J. Wongf6f1e9d22019-05-09 20:58:22498 GetProcessMemoryInfo(&pmc);
499 CrashIfPagefileUsageTooLarge(pmc);
500 CrashIfPrivateUsageTooLarge(pmc);
[email protected]4ddbd1422014-02-11 05:12:07501
anpol80e1d0d2016-11-02 11:07:03502 if (std::abs(height) * width > 100) {
[email protected]4ddbd1422014-02-11 05:12:07503 // Huh, that's weird. We don't have crazy handle count, we don't have
504 // ridiculous memory usage. Try to allocate a small bitmap and see if that
505 // fails too.
Albert J. Wongf6f1e9d22019-05-09 20:58:22506 CrashIfCannotAllocateSmallBitmap(header, shared_section);
[email protected]4ddbd1422014-02-11 05:12:07507 }
508 // Maybe the child processes are the ones leaking GDI or USER resouces.
pkasting767ea6b2014-09-29 20:41:20509 CollectChildGDIUsageAndDie(GetCurrentProcessId());
[email protected]4ddbd1422014-02-11 05:12:07510}
511
Robert Liao58bb1db2019-06-08 02:45:23512GdiHandleCounts GetGDIHandleCountsInCurrentProcessForTesting() {
Arthur Sonzognie5fff99c2024-02-21 15:58:24513 std::optional<GdiHandleCounts> handle_counts =
Robert Liao58bb1db2019-06-08 02:45:23514 CollectGdiHandleCounts(GetCurrentProcessId());
515 DCHECK(handle_counts.has_value());
516 return handle_counts.value_or(GdiHandleCounts());
517}
518
[email protected]4ddbd1422014-02-11 05:12:07519} // namespace debug
520} // namespace base