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 54 namespace chrome { 55 56 namespace { 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. 61 const 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. 66 const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x"; 67 const size_t kBrowserCrashDumpPrefixLength = 63; 68 char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {}; 69 70 // These registry key to which we'll write a value for each crash dump attempt. 71 HKEY g_browser_crash_dump_regkey = NULL; 72 73 // A atomic counter to make each crash dump value name unique. 74 base::subtle::Atomic32 g_browser_crash_dump_count = 0; 75 #endif 76 77 } // namespace 78 79 ChromeCrashReporterClient::ChromeCrashReporterClient() {} 80 81 ChromeCrashReporterClient::~ChromeCrashReporterClient() {} 82 83 void ChromeCrashReporterClient::SetCrashReporterClientIdFromGUID( 84 const std::string& client_guid) { 85 crash_keys::SetCrashClientIdFromGUID(client_guid); 86 } 87 88 #if defined(OS_WIN) 89 bool 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 103 void 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 135 bool 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 163 bool 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 172 bool 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 183 bool ChromeCrashReporterClient::GetIsPerUserInstall( 184 const base::FilePath& exe_path) { 185 return InstallUtil::IsPerUserInstall(exe_path.value().c_str()); 186 } 187 188 bool 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 199 int ChromeCrashReporterClient::GetResultCodeRespawnFailed() { 200 return chrome::RESULT_CODE_RESPAWN_FAILED; 201 } 202 203 void 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 234 void 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 258 bool 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) 287 void 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 307 base::FilePath ChromeCrashReporterClient::GetReporterLogFilename() { 308 return base::FilePath(CrashUploadList::kReporterLogFilename); 309 } 310 #endif 311 312 bool 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 327 size_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 334 bool ChromeCrashReporterClient::IsRunningUnattended() { 335 scoped_ptr<base::Environment> env(base::Environment::Create()); 336 return env->HasVar(env_vars::kHeadless); 337 } 338 339 bool 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) 368 int ChromeCrashReporterClient::GetAndroidMinidumpDescriptor() { 369 return kAndroidMinidumpDescriptor; 370 } 371 #endif 372 373 bool 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