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