1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/app/chrome_breakpad_client.h" 6 7#include "base/atomicops.h" 8#include "base/command_line.h" 9#include "base/environment.h" 10#include "base/files/file_path.h" 11#include "base/logging.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/path_service.h" 14#include "base/strings/safe_sprintf.h" 15#include "base/strings/string_split.h" 16#include "base/strings/utf_string_conversions.h" 17#include "chrome/common/chrome_constants.h" 18#include "chrome/common/chrome_paths.h" 19#include "chrome/common/chrome_result_codes.h" 20#include "chrome/common/chrome_switches.h" 21#include "chrome/common/crash_keys.h" 22#include "chrome/common/env_vars.h" 23#include "chrome/installer/util/google_update_settings.h" 24 25#if defined(OS_WIN) 26#include <windows.h> 27 28#include "base/file_version_info.h" 29#include "base/win/registry.h" 30#include "chrome/installer/util/google_chrome_sxs_distribution.h" 31#include "chrome/installer/util/install_util.h" 32#include "chrome/installer/util/util_constants.h" 33#include "policy/policy_constants.h" 34#endif 35 36#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) 37#include "chrome/browser/crash_upload_list.h" 38#include "chrome/common/chrome_version_info_posix.h" 39#endif 40 41#if defined(OS_POSIX) 42#include "base/debug/dump_without_crashing.h" 43#endif 44 45#if defined(OS_ANDROID) 46#include "chrome/common/descriptors_android.h" 47#endif 48 49#if defined(OS_CHROMEOS) 50#include "chrome/common/chrome_version_info.h" 51#include "chromeos/chromeos_switches.h" 52#endif 53 54namespace chrome { 55 56namespace { 57 58#if defined(OS_WIN) 59// This is the minimum version of google update that is required for deferred 60// crash uploads to work. 61const char kMinUpdateVersion[] = "1.3.21.115"; 62 63// The value name prefix will be of the form {chrome-version}-{pid}-{timestamp} 64// (i.e., "#####.#####.#####.#####-########-########") which easily fits into a 65// 63 character buffer. 66const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x"; 67const size_t kBrowserCrashDumpPrefixLength = 63; 68char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {}; 69 70// These registry key to which we'll write a value for each crash dump attempt. 71HKEY g_browser_crash_dump_regkey = NULL; 72 73// A atomic counter to make each crash dump value name unique. 74base::subtle::Atomic32 g_browser_crash_dump_count = 0; 75#endif 76 77} // namespace 78 79ChromeBreakpadClient::ChromeBreakpadClient() {} 80 81ChromeBreakpadClient::~ChromeBreakpadClient() {} 82 83void ChromeBreakpadClient::SetClientID(const std::string& client_id) { 84 crash_keys::SetClientID(client_id); 85} 86 87#if defined(OS_WIN) 88bool ChromeBreakpadClient::GetAlternativeCrashDumpLocation( 89 base::FilePath* crash_dir) { 90 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate 91 // location to write breakpad crash dumps can be set. 92 scoped_ptr<base::Environment> env(base::Environment::Create()); 93 std::string alternate_crash_dump_location; 94 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { 95 *crash_dir = base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); 96 return true; 97 } 98 99 return false; 100} 101 102void ChromeBreakpadClient::GetProductNameAndVersion( 103 const base::FilePath& exe_path, 104 base::string16* product_name, 105 base::string16* version, 106 base::string16* special_build, 107 base::string16* channel_name) { 108 DCHECK(product_name); 109 DCHECK(version); 110 DCHECK(special_build); 111 DCHECK(channel_name); 112 113 scoped_ptr<FileVersionInfo> version_info( 114 FileVersionInfo::CreateFileVersionInfo(exe_path)); 115 116 if (version_info.get()) { 117 // Get the information from the file. 118 *version = version_info->product_version(); 119 if (!version_info->is_official_build()) 120 version->append(base::ASCIIToUTF16("-devel")); 121 122 *product_name = version_info->product_short_name(); 123 *special_build = version_info->special_build(); 124 } else { 125 // No version info found. Make up the values. 126 *product_name = base::ASCIIToUTF16("Chrome"); 127 *version = base::ASCIIToUTF16("0.0.0.0-devel"); 128 } 129 130 GoogleUpdateSettings::GetChromeChannelAndModifiers( 131 !GetIsPerUserInstall(exe_path), channel_name); 132} 133 134bool ChromeBreakpadClient::ShouldShowRestartDialog(base::string16* title, 135 base::string16* message, 136 bool* is_rtl_locale) { 137 scoped_ptr<base::Environment> env(base::Environment::Create()); 138 if (!env->HasVar(env_vars::kShowRestart) || 139 !env->HasVar(env_vars::kRestartInfo) || 140 env->HasVar(env_vars::kMetroConnected)) { 141 return false; 142 } 143 144 std::string restart_info; 145 env->GetVar(env_vars::kRestartInfo, &restart_info); 146 147 // The CHROME_RESTART var contains the dialog strings separated by '|'. 148 // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment() 149 // for details. 150 std::vector<std::string> dlg_strings; 151 base::SplitString(restart_info, '|', &dlg_strings); 152 153 if (dlg_strings.size() < 3) 154 return false; 155 156 *title = base::UTF8ToUTF16(dlg_strings[0]); 157 *message = base::UTF8ToUTF16(dlg_strings[1]); 158 *is_rtl_locale = dlg_strings[2] == env_vars::kRtlLocale; 159 return true; 160} 161 162bool ChromeBreakpadClient::AboutToRestart() { 163 scoped_ptr<base::Environment> env(base::Environment::Create()); 164 if (!env->HasVar(env_vars::kRestartInfo)) 165 return false; 166 167 env->SetVar(env_vars::kShowRestart, "1"); 168 return true; 169} 170 171bool ChromeBreakpadClient::GetDeferredUploadsSupported( 172 bool is_per_user_install) { 173 Version update_version = GoogleUpdateSettings::GetGoogleUpdateVersion( 174 !is_per_user_install); 175 if (!update_version.IsValid() || 176 update_version.IsOlderThan(std::string(kMinUpdateVersion))) 177 return false; 178 179 return true; 180} 181 182bool ChromeBreakpadClient::GetIsPerUserInstall(const base::FilePath& exe_path) { 183 return InstallUtil::IsPerUserInstall(exe_path.value().c_str()); 184} 185 186bool ChromeBreakpadClient::GetShouldDumpLargerDumps(bool is_per_user_install) { 187 base::string16 channel_name = 188 GoogleUpdateSettings::GetChromeChannel(!is_per_user_install); 189 190 // Capture more detail in crash dumps for beta and dev channel builds. 191 return (channel_name == installer::kChromeChannelDev || 192 channel_name == installer::kChromeChannelBeta || 193 channel_name == GoogleChromeSxSDistribution::ChannelName()); 194} 195 196int ChromeBreakpadClient::GetResultCodeRespawnFailed() { 197 return chrome::RESULT_CODE_RESPAWN_FAILED; 198} 199 200void ChromeBreakpadClient::InitBrowserCrashDumpsRegKey() { 201 DCHECK(g_browser_crash_dump_regkey == NULL); 202 203 base::win::RegKey regkey; 204 if (regkey.Create(HKEY_CURRENT_USER, 205 chrome::kBrowserCrashDumpAttemptsRegistryPath, 206 KEY_ALL_ACCESS) != ERROR_SUCCESS) { 207 return; 208 } 209 210 // We use the current process id and the current tick count as a (hopefully) 211 // unique combination for the crash dump value. There's a small chance that 212 // across a reboot we might have a crash dump signal written, and the next 213 // browser process might have the same process id and tick count, but crash 214 // before consuming the signal (overwriting the signal with an identical one). 215 // For now, we're willing to live with that risk. 216 int length = base::strings::SafeSPrintf(g_browser_crash_dump_prefix, 217 kBrowserCrashDumpPrefixTemplate, 218 chrome::kChromeVersion, 219 ::GetCurrentProcessId(), 220 ::GetTickCount()); 221 if (length <= 0) { 222 NOTREACHED(); 223 g_browser_crash_dump_prefix[0] = '\0'; 224 return; 225 } 226 227 // Hold the registry key in a global for update on crash dump. 228 g_browser_crash_dump_regkey = regkey.Take(); 229} 230 231void ChromeBreakpadClient::RecordCrashDumpAttempt(bool is_real_crash) { 232 // If we're not a browser (or the registry is unavailable to us for some 233 // reason) then there's nothing to do. 234 if (g_browser_crash_dump_regkey == NULL) 235 return; 236 237 // Generate the final value name we'll use (appends the crash number to the 238 // base value name). 239 const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength; 240 char value_name[kMaxValueSize + 1] = {}; 241 int length = base::strings::SafeSPrintf( 242 value_name, 243 "%s-%x", 244 g_browser_crash_dump_prefix, 245 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1)); 246 247 if (length > 0) { 248 DWORD value_dword = is_real_crash ? 1 : 0; 249 ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD, 250 reinterpret_cast<BYTE*>(&value_dword), 251 sizeof(value_dword)); 252 } 253} 254 255bool ChromeBreakpadClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) { 256// Determine whether configuration management allows loading the crash reporter. 257// Since the configuration management infrastructure is not initialized at this 258// point, we read the corresponding registry key directly. The return status 259// indicates whether policy data was successfully read. If it is true, 260// |breakpad_enabled| contains the value set by policy. 261 base::string16 key_name = 262 base::UTF8ToUTF16(policy::key::kMetricsReportingEnabled); 263 DWORD value = 0; 264 base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE, 265 policy::kRegistryChromePolicyKey, KEY_READ); 266 if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { 267 *breakpad_enabled = value != 0; 268 return true; 269 } 270 271 base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER, 272 policy::kRegistryChromePolicyKey, KEY_READ); 273 if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { 274 *breakpad_enabled = value != 0; 275 return true; 276 } 277 278 return false; 279} 280#endif // defined(OS_WIN) 281 282#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) 283void ChromeBreakpadClient::GetProductNameAndVersion(std::string* product_name, 284 std::string* version) { 285 DCHECK(product_name); 286 DCHECK(version); 287#if defined(OS_ANDROID) 288 *product_name = "Chrome_Android"; 289#elif defined(OS_CHROMEOS) 290 *product_name = "Chrome_ChromeOS"; 291#else // OS_LINUX 292#if !defined(ADDRESS_SANITIZER) 293 *product_name = "Chrome_Linux"; 294#else 295 *product_name = "Chrome_Linux_ASan"; 296#endif 297#endif 298 299 *version = PRODUCT_VERSION; 300} 301 302base::FilePath ChromeBreakpadClient::GetReporterLogFilename() { 303 return base::FilePath(CrashUploadList::kReporterLogFilename); 304} 305#endif 306 307bool ChromeBreakpadClient::GetCrashDumpLocation(base::FilePath* crash_dir) { 308 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate 309 // location to write breakpad crash dumps can be set. 310 scoped_ptr<base::Environment> env(base::Environment::Create()); 311 std::string alternate_crash_dump_location; 312 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { 313 base::FilePath crash_dumps_dir_path = 314 base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); 315 PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path); 316 } 317 318 return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir); 319} 320 321size_t ChromeBreakpadClient::RegisterCrashKeys() { 322 // Note: This is not called on Windows because Breakpad is initialized in the 323 // EXE module, but code that uses crash keys is in the DLL module. 324 // RegisterChromeCrashKeys() will be called after the DLL is loaded. 325 return crash_keys::RegisterChromeCrashKeys(); 326} 327 328bool ChromeBreakpadClient::IsRunningUnattended() { 329 scoped_ptr<base::Environment> env(base::Environment::Create()); 330 return env->HasVar(env_vars::kHeadless); 331} 332 333bool ChromeBreakpadClient::GetCollectStatsConsent() { 334#if defined(GOOGLE_CHROME_BUILD) 335 bool is_official_chrome_build = true; 336#else 337 bool is_official_chrome_build = false; 338#endif 339 340#if defined(OS_CHROMEOS) 341 bool is_guest_session = CommandLine::ForCurrentProcess()->HasSwitch( 342 chromeos::switches::kGuestSession); 343 bool is_stable_channel = 344 chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE; 345 346 if (is_guest_session && is_stable_channel) 347 return false; 348#endif // defined(OS_CHROMEOS) 349 350#if defined(OS_ANDROID) 351 // TODO(jcivelli): we should not initialize the crash-reporter when it was not 352 // enabled. Right now if it is disabled we still generate the minidumps but we 353 // do not upload them. 354 return is_official_chrome_build; 355#else // !defined(OS_ANDROID) 356 return is_official_chrome_build && 357 GoogleUpdateSettings::GetCollectStatsConsent(); 358#endif // defined(OS_ANDROID) 359} 360 361#if defined(OS_ANDROID) 362int ChromeBreakpadClient::GetAndroidMinidumpDescriptor() { 363 return kAndroidMinidumpDescriptor; 364} 365#endif 366 367bool ChromeBreakpadClient::EnableBreakpadForProcess( 368 const std::string& process_type) { 369 return process_type == switches::kRendererProcess || 370 process_type == switches::kPluginProcess || 371 process_type == switches::kPpapiPluginProcess || 372 process_type == switches::kZygoteProcess || 373 process_type == switches::kGpuProcess; 374} 375 376} // namespace chrome 377