diff --git a/base/base.gypi b/base/base.gypi index b5ced611dd144d..8ed1fe381a97d9 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -146,6 +146,8 @@ 'debug/debugger_win.cc', 'debug/dump_without_crashing.cc', 'debug/dump_without_crashing.h', + 'debug/gdi_debug_util_win.cc', + 'debug/gdi_debug_util_win.h', # This file depends on files from the 'allocator' target, # but this target does not depend on 'allocator' (see # allocator.gyp for details). diff --git a/base/debug/gdi_debug_util_win.cc b/base/debug/gdi_debug_util_win.cc new file mode 100644 index 00000000000000..4bac759d8c7856 --- /dev/null +++ b/base/debug/gdi_debug_util_win.cc @@ -0,0 +1,129 @@ +// 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 + +#include +#include + +#include "base/debug/alias.h" +#include "base/logging.h" +#include "base/win/scoped_handle.h" + +namespace { + +void CollectChildGDIUsageAndDie(DWORD parent_pid) { + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ; + if(snapshot == INVALID_HANDLE_VALUE) + CHECK(false); + + 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) ; + if(!Process32First(snapshot, &proc_entry)) + CHECK(false); + + do { + if (parent_pid != proc_entry.th32ParentProcessID) + continue; + // Got a child process. Compute GDI usage. + base::win::ScopedHandle process( + ::OpenProcess(PROCESS_QUERY_INFORMATION, + FALSE, + proc_entry.th32ParentProcessID)); + if (!process) + continue; + + int num_gdi_handles = ::GetGuiResources(process.Get(), GR_GDIOBJECTS); + int num_user_handles = ::GetGuiResources(process.Get(), GR_USEROBJECTS); + + // Compute sum and peak counts. + ++child_count; + sum_user_count += num_user_handles; + sum_gdi_count += num_gdi_handles; + if (peak_gdi_count < num_gdi_handles) + peak_gdi_count = num_gdi_handles; + + } while(Process32Next(snapshot, &proc_entry)); + + ::CloseHandle(snapshot) ; + CHECK(false); +} + +} // namespace + +namespace base { +namespace debug { + +void GDIBitmapAllocFailure(BITMAPINFOHEADER* header, HANDLE shared_section) { + // Make sure parameters are saved in the minidump. + DWORD last_error = ::GetLastError(); + + LONG width = header->biWidth; + LONG heigth = header->biHeight; + + base::debug::Alias(&last_error); + base::debug::Alias(&width); + base::debug::Alias(&heigth); + base::debug::Alias(&shared_section); + + int num_user_handles = GetGuiResources(GetCurrentProcess(), + GR_USEROBJECTS); + + int 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; + if (num_gdi_handles > kLotsOfHandles) + CHECK(false); + + PROCESS_MEMORY_COUNTERS_EX pmc; + pmc.cb = sizeof(pmc); + if (!GetProcessMemoryInfo(GetCurrentProcess(), + reinterpret_cast(&pmc), + sizeof(pmc))) { + CHECK(false); + } + const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB + if (pmc.PagefileUsage > kLotsOfMemory) + CHECK(false); + if (pmc.PrivateUsage > kLotsOfMemory) + CHECK(false); + + void* small_data = NULL; + base::debug::Alias(&small_data); + + if (std::abs(heigth) * 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( + NULL, reinterpret_cast(&header), + 0, &small_data, shared_section, 0); + } + // Maybe the child processes are the ones leaking GDI or USER resouces. + CollectChildGDIUsageAndDie(::GetCurrentProcessId()); +} + +} // namespace debug +} // namespace base diff --git a/base/debug/gdi_debug_util_win.h b/base/debug/gdi_debug_util_win.h new file mode 100644 index 00000000000000..5887ecb8464184 --- /dev/null +++ b/base/debug/gdi_debug_util_win.h @@ -0,0 +1,23 @@ +// 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. + +#ifndef BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ +#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ + +#include + +#include "base/base_export.h" + +namespace base { +namespace debug { + +// Crashes the process leaving valuable information on the dump via +// debug::alias so we can find what is causing the allocation failures. +void BASE_EXPORT GDIBitmapAllocFailure(BITMAPINFOHEADER* header, + HANDLE shared_section); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_ diff --git a/skia/ext/bitmap_platform_device_win.cc b/skia/ext/bitmap_platform_device_win.cc index dead9c7b5f8cdc..89e3834d5dd54a 100644 --- a/skia/ext/bitmap_platform_device_win.cc +++ b/skia/ext/bitmap_platform_device_win.cc @@ -5,8 +5,8 @@ #include #include +#include "base/debug/gdi_debug_util_win.h" #include "base/logging.h" -#include "base/debug/alias.h" #include "skia/ext/bitmap_platform_device_win.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkMatrix.h" @@ -41,61 +41,13 @@ HBITMAP CreateHBitmap(int width, int height, bool is_opaque, HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast(&hdr), 0, data, shared_section, 0); +#if !defined(_WIN64) // If this call fails, we're gonna crash hard. Try to get some useful // information out before we crash for post-mortem analysis. - if (!hbitmap) { - // Make sure parameters are saved in the minidump. - base::debug::Alias(&width); - base::debug::Alias(&height); - - int last_error = GetLastError(); - base::debug::Alias(&last_error); - - int num_gdi_handles = GetGuiResources(GetCurrentProcess(), - GR_GDIOBJECTS); - if (num_gdi_handles == 0) { - int get_gui_resources_error = GetLastError(); - base::debug::Alias(&get_gui_resources_error); - CHECK(false); - } - - base::debug::Alias(&num_gdi_handles); - const int kLotsOfHandles = 9990; - if (num_gdi_handles > kLotsOfHandles) - CHECK(false); - - PROCESS_MEMORY_COUNTERS_EX pmc; - pmc.cb = sizeof(pmc); - if (!GetProcessMemoryInfo(GetCurrentProcess(), - reinterpret_cast(&pmc), - sizeof(pmc))) { - CHECK(false); - } - const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB - if (pmc.PagefileUsage > kLotsOfMemory) - CHECK(false); - if (pmc.PrivateUsage > kLotsOfMemory) - CHECK(false); - - // 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. - hdr.biWidth = 5; - hdr.biHeight = 5; - void* small_data; - HBITMAP small_bitmap = CreateDIBSection( - NULL, reinterpret_cast(&hdr), - 0, &small_data, shared_section, 0); - if (!small_bitmap) - CHECK(false); - BITMAP bitmap_data; - if (GetObject(small_bitmap, sizeof(BITMAP), &bitmap_data)) { - if (!DeleteObject(small_bitmap)) - CHECK(false); - } - // No idea what's going on. Die! - CHECK(false); - } + if (!hbitmap) + base::debug::GDIBitmapAllocFailure(&hdr, shared_section); +#endif + return hbitmap; }