Skip to content

Commit

Permalink
More instrumentation for GDI allocation failures
Browse files Browse the repository at this point in the history
The problem is that ::CreateDIBSection fails and we don't know why
the current instrumentation tells us that everything is nice in the
current process, so we are adding code to check on the child
processes of the current process.

Its a big crasher in dev soon to be beta so I am rushing this.

TBR=brettw,jamesr,thakis
BUG=341593

Review URL: https://codereview.chromium.org/159523003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@250357 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
cpu@chromium.org committed Feb 11, 2014
1 parent cae8dd4 commit 4ddbd14
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 54 deletions.
2 changes: 2 additions & 0 deletions base/base.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
129 changes: 129 additions & 0 deletions base/debug/gdi_debug_util_win.cc
Original file line number Diff line number Diff line change
@@ -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 <cmath>

#include <psapi.h>
#include <TlHelp32.h>

#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<PROCESS_MEMORY_COUNTERS*>(&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<BITMAPINFO*>(&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
23 changes: 23 additions & 0 deletions base/debug/gdi_debug_util_win.h
Original file line number Diff line number Diff line change
@@ -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 <windows.h>

#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_
60 changes: 6 additions & 54 deletions skia/ext/bitmap_platform_device_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#include <windows.h>
#include <psapi.h>

#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"
Expand Down Expand Up @@ -41,61 +41,13 @@ HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&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<PROCESS_MEMORY_COUNTERS*>(&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<BITMAPINFO*>(&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;
}

Expand Down

0 comments on commit 4ddbd14

Please sign in to comment.