15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/debug/gdi_debug_util_win.h"
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <cmath>
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <psapi.h>
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <TlHelp32.h>
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/debug/alias.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/logging.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/win/scoped_handle.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void CollectChildGDIUsageAndDie(DWORD parent_pid) {
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if(snapshot == INVALID_HANDLE_VALUE)
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int child_count = 0;
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&child_count);
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int peak_gdi_count = 0;
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&peak_gdi_count);
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int sum_gdi_count = 0;
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&sum_gdi_count);
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int sum_user_count = 0;
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&sum_user_count);
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PROCESSENTRY32 proc_entry = {0};
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  proc_entry.dwSize = sizeof(PROCESSENTRY32) ;
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if(!Process32First(snapshot, &proc_entry))
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  do {
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (parent_pid != proc_entry.th32ParentProcessID)
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Got a child process. Compute GDI usage.
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::win::ScopedHandle process(
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ::OpenProcess(PROCESS_QUERY_INFORMATION,
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      FALSE,
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                      proc_entry.th32ParentProcessID));
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!process.IsValid())
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int num_gdi_handles = ::GetGuiResources(process.Get(), GR_GDIOBJECTS);
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int num_user_handles = ::GetGuiResources(process.Get(), GR_USEROBJECTS);
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Compute sum and peak counts.
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ++child_count;
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sum_user_count += num_user_handles;
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sum_gdi_count += num_gdi_handles;
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (peak_gdi_count < num_gdi_handles)
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      peak_gdi_count = num_gdi_handles;
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } while(Process32Next(snapshot, &proc_entry));
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ::CloseHandle(snapshot) ;
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK(false);
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace base {
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace debug {
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void GDIBitmapAllocFailure(BITMAPINFOHEADER* header, HANDLE shared_section) {
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Make sure parameters are saved in the minidump.
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DWORD last_error = ::GetLastError();
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  LONG width = header->biWidth;
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  LONG heigth = header->biHeight;
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&last_error);
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&width);
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&heigth);
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&shared_section);
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int num_user_handles = GetGuiResources(GetCurrentProcess(),
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         GR_USEROBJECTS);
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int num_gdi_handles = GetGuiResources(GetCurrentProcess(),
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                        GR_GDIOBJECTS);
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (num_gdi_handles == 0) {
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DWORD get_gui_resources_error = GetLastError();
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::debug::Alias(&get_gui_resources_error);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&num_gdi_handles);
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&num_user_handles);
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const DWORD kLotsOfHandles = 9990;
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (num_gdi_handles > kLotsOfHandles)
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PROCESS_MEMORY_COUNTERS_EX pmc;
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  pmc.cb = sizeof(pmc);
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!GetProcessMemoryInfo(GetCurrentProcess(),
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            sizeof(pmc))) {
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (pmc.PagefileUsage > kLotsOfMemory)
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (pmc.PrivateUsage > kLotsOfMemory)
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    CHECK(false);
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void* small_data = NULL;
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::debug::Alias(&small_data);
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (std::abs(heigth) * width > 100) {
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Huh, that's weird.  We don't have crazy handle count, we don't have
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // ridiculous memory usage. Try to allocate a small bitmap and see if that
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // fails too.
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    header->biWidth = 5;
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    header->biHeight = -5;
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    HBITMAP small_bitmap = CreateDIBSection(
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        NULL, reinterpret_cast<BITMAPINFO*>(&header),
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        0, &small_data, shared_section, 0);
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Maybe the child processes are the ones leaking GDI or USER resouces.
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CollectChildGDIUsageAndDie(::GetCurrentProcessId());
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace debug
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace base
130