|  | // Copyright 2014 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 "base/debug/gdi_debug_util_win.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cmath> | 
|  |  | 
|  | #include <psapi.h> | 
|  | #include <stddef.h> | 
|  | #include <TlHelp32.h> | 
|  |  | 
|  | #include "base/debug/alias.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/win/scoped_handle.h" | 
|  | #include "base/win/win_util.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void CollectChildGDIUsageAndDie(DWORD parent_pid) { | 
|  | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | 
|  | CHECK_NE(INVALID_HANDLE_VALUE, snapshot); | 
|  |  | 
|  | int total_process_count = 0; | 
|  | base::debug::Alias(&total_process_count); | 
|  | int total_peak_gdi_count = 0; | 
|  | base::debug::Alias(&total_peak_gdi_count); | 
|  | int total_gdi_count = 0; | 
|  | base::debug::Alias(&total_gdi_count); | 
|  | int total_user_count = 0; | 
|  | base::debug::Alias(&total_user_count); | 
|  |  | 
|  | int child_count = 0; | 
|  | base::debug::Alias(&child_count); | 
|  | int peak_gdi_count = 0; | 
|  | base::debug::Alias(&peak_gdi_count); | 
|  | int sum_gdi_count = 0; | 
|  | base::debug::Alias(&sum_gdi_count); | 
|  | int sum_user_count = 0; | 
|  | base::debug::Alias(&sum_user_count); | 
|  |  | 
|  | PROCESSENTRY32 proc_entry = {0}; | 
|  | proc_entry.dwSize = sizeof(PROCESSENTRY32); | 
|  | CHECK(Process32First(snapshot, &proc_entry)); | 
|  |  | 
|  | do { | 
|  | base::win::ScopedHandle process( | 
|  | OpenProcess(PROCESS_QUERY_INFORMATION, | 
|  | FALSE, | 
|  | proc_entry.th32ProcessID)); | 
|  | if (!process.IsValid()) | 
|  | continue; | 
|  |  | 
|  | int num_gdi_handles = GetGuiResources(process.Get(), GR_GDIOBJECTS); | 
|  | int num_user_handles = GetGuiResources(process.Get(), GR_USEROBJECTS); | 
|  |  | 
|  | // Compute sum and peak counts for all processes. | 
|  | ++total_process_count; | 
|  | total_user_count += num_user_handles; | 
|  | total_gdi_count += num_gdi_handles; | 
|  | total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles); | 
|  |  | 
|  | if (parent_pid != proc_entry.th32ParentProcessID) | 
|  | continue; | 
|  |  | 
|  | // Compute sum and peak counts for child processes. | 
|  | ++child_count; | 
|  | sum_user_count += num_user_handles; | 
|  | sum_gdi_count += num_gdi_handles; | 
|  | peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles); | 
|  |  | 
|  | } while (Process32Next(snapshot, &proc_entry)); | 
|  |  | 
|  | CloseHandle(snapshot); | 
|  | CHECK(false); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace base { | 
|  | namespace debug { | 
|  |  | 
|  | void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) { | 
|  | // Make sure parameters are saved in the minidump. | 
|  | DWORD last_error = GetLastError(); | 
|  | bool is_gdi_available = base::win::IsUser32AndGdi32Available(); | 
|  |  | 
|  | LONG width = header ? header->biWidth : 0; | 
|  | LONG height = header ? header->biHeight : 0; | 
|  |  | 
|  | base::debug::Alias(&last_error); | 
|  | base::debug::Alias(&is_gdi_available); | 
|  | base::debug::Alias(&width); | 
|  | base::debug::Alias(&height); | 
|  | base::debug::Alias(&shared_section); | 
|  |  | 
|  | DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS); | 
|  |  | 
|  | DWORD num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); | 
|  | if (num_gdi_handles == 0) { | 
|  | DWORD get_gui_resources_error = GetLastError(); | 
|  | base::debug::Alias(&get_gui_resources_error); | 
|  | CHECK(false); | 
|  | } | 
|  |  | 
|  | base::debug::Alias(&num_gdi_handles); | 
|  | base::debug::Alias(&num_user_handles); | 
|  |  | 
|  | const DWORD kLotsOfHandles = 9990; | 
|  | CHECK_LE(num_gdi_handles, kLotsOfHandles); | 
|  |  | 
|  | PROCESS_MEMORY_COUNTERS_EX pmc; | 
|  | pmc.cb = sizeof(pmc); | 
|  | CHECK(GetProcessMemoryInfo(GetCurrentProcess(), | 
|  | reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc), | 
|  | sizeof(pmc))); | 
|  | const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB | 
|  | CHECK_LE(pmc.PagefileUsage, kLotsOfMemory); | 
|  | CHECK_LE(pmc.PrivateUsage, kLotsOfMemory); | 
|  |  | 
|  | void* small_data = nullptr; | 
|  | base::debug::Alias(&small_data); | 
|  |  | 
|  | if (std::abs(height) * width > 100) { | 
|  | // Huh, that's weird.  We don't have crazy handle count, we don't have | 
|  | // ridiculous memory usage. Try to allocate a small bitmap and see if that | 
|  | // fails too. | 
|  | header->biWidth = 5; | 
|  | header->biHeight = -5; | 
|  | HBITMAP small_bitmap = CreateDIBSection( | 
|  | nullptr, reinterpret_cast<BITMAPINFO*>(&header), | 
|  | 0, &small_data, shared_section, 0); | 
|  | CHECK(small_bitmap != nullptr); | 
|  | DeleteObject(small_bitmap); | 
|  | } | 
|  | // Maybe the child processes are the ones leaking GDI or USER resouces. | 
|  | CollectChildGDIUsageAndDie(GetCurrentProcessId()); | 
|  | } | 
|  |  | 
|  | }  // namespace debug | 
|  | }  // namespace base |