| // 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 <TlHelp32.h> | |
| #include "base/debug/alias.h" | |
| #include "base/logging.h" | |
| #include "base/win/scoped_handle.h" | |
| #include "base/win/win_util.h" | |
| #include "starboard/types.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 |