Commit ead1e8ab authored by Jesse McKenna's avatar Jesse McKenna Committed by Commit Bot

Check for GDI exhaustion if window creation fails

This change makes gfx::CheckWindowCreated() check whether the system
is out of Graphics Device Interface (GDI) resources if window creation
fails. Exhaustion of GDI resources indicates a GDI leak, either by
Chrome or by another program, caused by the acquisition of handles to
many GDI objects without the release of those handles.

Explicitly checking for this case will make it easier to diagnose
crashes in gfx::CheckWindowCreated(), e.g., crbug.com/1091696, which
is suspected to be GDI-leak-related but impossible to diagnose with
certainty without understanding the GDI-object counts at the time of
the crash.

Bug: 1091696
Change-Id: Ifaeceae8f7c511a9c7d0f80f37709fbc1c5bb9d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2244124
Commit-Queue: Jesse McKenna <jessemckenna@google.com>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarBruce Dawson <brucedawson@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782096}
parent 883f0ce5
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/win/windows_types.h"
#include "base/win/wrapped_window_proc.h" #include "base/win/wrapped_window_proc.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/ui/views/status_icons/status_icon_win.h" #include "chrome/browser/ui/views/status_icons/status_icon_win.h"
...@@ -122,7 +123,7 @@ StatusTrayWin::StatusTrayWin() ...@@ -122,7 +123,7 @@ StatusTrayWin::StatusTrayWin()
// "TaskbarCreated". // "TaskbarCreated".
window_ = CreateWindow(MAKEINTATOM(atom_), window_ = CreateWindow(MAKEINTATOM(atom_),
0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0); 0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0);
gfx::CheckWindowCreated(window_); gfx::CheckWindowCreated(window_, ::GetLastError());
gfx::SetWindowUserData(window_, this); gfx::SetWindowUserData(window_, this);
} }
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "ui/gfx/win/hwnd_util.h" #include "ui/gfx/win/hwnd_util.h"
#include <windows.h>
#include "base/debug/alias.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
...@@ -51,17 +54,31 @@ void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) { ...@@ -51,17 +54,31 @@ void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
// Don't inline these functions so they show up in crash reports. // Don't inline these functions so they show up in crash reports.
NOINLINE void CrashOutOfMemory() { NOINLINE void CrashOutOfMemory(DWORD last_error) {
PLOG(FATAL); // Record Graphics Device Interface (GDI) object counts so they are visible in
// the crash's minidump. By default, GDI and USER handles are limited to
// 10,000 each per process and 65,535 each globally, exceeding which typically
// indicates a leak of GDI resources.
const HANDLE process = ::GetCurrentProcess();
DWORD num_process_gdi_handles = ::GetGuiResources(process, GR_GDIOBJECTS);
DWORD num_process_user_handles = ::GetGuiResources(process, GR_USEROBJECTS);
DWORD num_global_gdi_handles = ::GetGuiResources(GR_GLOBAL, GR_GDIOBJECTS);
DWORD num_global_user_handles = ::GetGuiResources(GR_GLOBAL, GR_USEROBJECTS);
base::debug::Alias(&num_process_gdi_handles);
base::debug::Alias(&num_process_user_handles);
base::debug::Alias(&num_global_gdi_handles);
base::debug::Alias(&num_global_user_handles);
LOG(FATAL) << last_error;
} }
NOINLINE void CrashAccessDenied() { NOINLINE void CrashAccessDenied(DWORD last_error) {
PLOG(FATAL); LOG(FATAL) << last_error;
} }
// Crash isn't one of the ones we commonly see. // Crash isn't one of the ones we commonly see.
NOINLINE void CrashOther() { NOINLINE void CrashOther(DWORD last_error) {
PLOG(FATAL); LOG(FATAL) << last_error;
} }
} // namespace } // namespace
...@@ -183,20 +200,20 @@ void CenterAndSizeWindow(HWND parent, ...@@ -183,20 +200,20 @@ void CenterAndSizeWindow(HWND parent,
AdjustWindowToFit(window, window_bounds, !parent); AdjustWindowToFit(window, window_bounds, !parent);
} }
void CheckWindowCreated(HWND hwnd) { void CheckWindowCreated(HWND hwnd, DWORD last_error) {
if (!hwnd) { if (!hwnd) {
switch (GetLastError()) { switch (last_error) {
case ERROR_NOT_ENOUGH_MEMORY: case ERROR_NOT_ENOUGH_MEMORY:
CrashOutOfMemory(); CrashOutOfMemory(last_error);
break; break;
case ERROR_ACCESS_DENIED: case ERROR_ACCESS_DENIED:
CrashAccessDenied(); CrashAccessDenied(last_error);
break; break;
default: default:
CrashOther(); CrashOther(last_error);
break; break;
} }
PLOG(FATAL); LOG(FATAL) << last_error;
} }
} }
......
...@@ -35,9 +35,9 @@ GFX_EXPORT void CenterAndSizeWindow(HWND parent, ...@@ -35,9 +35,9 @@ GFX_EXPORT void CenterAndSizeWindow(HWND parent,
HWND window, HWND window,
const gfx::Size& pref); const gfx::Size& pref);
// If |hwnd| is NULL logs various thing and CHECKs. Invoke right after calling // If |hwnd| is nullptr logs various thing and CHECKs. |last_error| must contain
// CreateWindow. // the result of ::GetLastError(), called immediately after CreateWindow().
GFX_EXPORT void CheckWindowCreated(HWND hwnd); GFX_EXPORT void CheckWindowCreated(HWND hwnd, DWORD last_error);
// Returns the window you can use to parent a top level window. // Returns the window you can use to parent a top level window.
// Note that in some cases we create child windows not parented to its final // Note that in some cases we create child windows not parented to its final
......
...@@ -217,6 +217,8 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) { ...@@ -217,6 +217,8 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
reinterpret_cast<wchar_t*>(atom), NULL, reinterpret_cast<wchar_t*>(atom), NULL,
window_style_, x, y, width, height, window_style_, x, y, width, height,
parent, NULL, NULL, this); parent, NULL, NULL, this);
const DWORD create_window_error = ::GetLastError();
// First nccalcszie (during CreateWindow) for captioned windows is // First nccalcszie (during CreateWindow) for captioned windows is
// deliberately ignored so force a second one here to get the right // deliberately ignored so force a second one here to get the right
// non-client set up. // non-client set up.
...@@ -226,7 +228,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) { ...@@ -226,7 +228,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW);
} }
if (!hwnd_ && GetLastError() == 0) { if (!hwnd_ && create_window_error == 0) {
base::debug::Alias(&destroyed); base::debug::Alias(&destroyed);
base::debug::Alias(&hwnd); base::debug::Alias(&hwnd);
bool got_create = got_create_; bool got_create = got_create_;
...@@ -248,7 +250,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) { ...@@ -248,7 +250,7 @@ void WindowImpl::Init(HWND parent, const Rect& bounds) {
if (!destroyed) if (!destroyed)
destroyed_ = NULL; destroyed_ = NULL;
CheckWindowCreated(hwnd_); CheckWindowCreated(hwnd_, create_window_error);
// The window procedure should have set the data for us. // The window procedure should have set the data for us.
CHECK_EQ(this, GetWindowUserData(hwnd)); CHECK_EQ(this, GetWindowUserData(hwnd));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment