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