Record UMA data to measure FaultTolerantHeap usage in the wild.

The FaultTolerantHeap is automatically engaged by Windows in order
to improve stability. Alternately, the FaultTolerantHeap hides
crashes and hurts performance, which is bad.

In order to assess the goodness/badness of the FTH we first need
to measure it. A combination of online resources and experimentation
led to these registry and module checks. See the bug for more details

[email protected]
BUG=479695

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

Cr-Commit-Position: refs/heads/master@{#328189}
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 1e791cc..a4a5e1a 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -24,6 +24,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/metro.h"
+#include "base/win/registry.h"
 #include "base/win/win_util.h"
 #include "base/win/windows_version.h"
 #include "base/win/wrapped_window_proc.h"
@@ -156,6 +157,65 @@
 }
 #endif
 
+void DetectFaultTolerantHeap() {
+  enum FTHFlags {
+    FTH_HKLM = 1,
+    FTH_HKCU = 2,
+    FTH_ACLAYERS_LOADED = 4,
+    FTH_ACXTRNAL_LOADED = 8,
+    FTH_FLAGS_COUNT = 16
+  };
+
+  // The Fault Tolerant Heap (FTH) is enabled on some customer machines and is
+  // affecting their performance. We need to know how many machines are
+  // affected in order to decide what to do.
+
+  // The main way that the FTH is enabled is by having a value set in
+  // HKLM\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
+  // whose name is the full path to the executable and whose data is the
+  // string FaultTolerantHeap. Some documents suggest that this data may also
+  // be found in HKCU. There have also been cases observed where this registry
+  // key is set but the FTH is not enabled, so we also look for AcXtrnal.dll
+  // and AcLayers.dll which are used to implement the FTH on Windows 7 and 8
+  // respectively.
+
+  // Get the module path so that we can look for it in the registry.
+  wchar_t module_path[MAX_PATH];
+  GetModuleFileName(NULL, module_path, ARRAYSIZE(module_path));
+  // Force null-termination, necessary on Windows XP.
+  module_path[ARRAYSIZE(module_path)-1] = 0;
+
+  const wchar_t* const kRegPath = L"Software\\Microsoft\\Windows NT\\"
+        L"CurrentVersion\\AppCompatFlags\\Layers";
+  const wchar_t* const kFTHData = L"FaultTolerantHeap";
+  // We always want to read from the 64-bit version of the registry if present,
+  // since that is what the OS looks at, even for 32-bit processes.
+  const DWORD kRegFlags = KEY_READ | KEY_WOW64_64KEY;
+
+  base::win::RegKey FTH_HKLM_reg(HKEY_LOCAL_MACHINE, kRegPath, kRegFlags);
+  FTHFlags detected = FTHFlags();
+  base::string16 chrome_app_compat;
+  if (FTH_HKLM_reg.ReadValue(module_path, &chrome_app_compat) == 0) {
+    // This *usually* indicates that the fault tolerant heap is enabled.
+    if (wcsicmp(chrome_app_compat.c_str(), kFTHData) == 0)
+      detected = static_cast<FTHFlags>(detected | FTH_HKLM);
+  }
+
+  base::win::RegKey FTH_HKCU_reg(HKEY_CURRENT_USER, kRegPath, kRegFlags);
+  if (FTH_HKCU_reg.ReadValue(module_path, &chrome_app_compat) == 0) {
+    if (wcsicmp(chrome_app_compat.c_str(), kFTHData) == 0)
+      detected = static_cast<FTHFlags>(detected | FTH_HKCU);
+  }
+
+  // Look for the DLLs used to implement the FTH and other compat hacks.
+  if (GetModuleHandleW(L"AcLayers.dll") != NULL)
+    detected = static_cast<FTHFlags>(detected | FTH_ACLAYERS_LOADED);
+  if (GetModuleHandleW(L"AcXtrnal.dll") != NULL)
+    detected = static_cast<FTHFlags>(detected | FTH_ACXTRNAL_LOADED);
+
+  UMA_HISTOGRAM_ENUMERATION("FaultTolerantHeap", detected, FTH_FLAGS_COUNT);
+}
+
 }  // namespace
 
 void ShowCloseBrowserFirstMessageBox() {
@@ -341,6 +401,14 @@
 #if defined(GOOGLE_CHROME_BUILD)
   did_run_updater_.reset(new DidRunUpdater);
 #endif
+
+  // Record UMA data about whether the fault-tolerant heap is enabled.
+  // Use a delayed task to minimize the impact on startup time.
+  content::BrowserThread::PostDelayedTask(
+      content::BrowserThread::UI,
+      FROM_HERE,
+      base::Bind(&DetectFaultTolerantHeap),
+      base::TimeDelta::FromMinutes(1));
 }
 
 // static