| OLD | NEW |
| (Empty) |
| 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 | |
| 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 ChromeBreakpadClient::ChromeBreakpadClient() {} | |
| 80 | |
| 81 ChromeBreakpadClient::~ChromeBreakpadClient() {} | |
| 82 | |
| 83 void ChromeBreakpadClient::SetBreakpadClientIdFromGUID( | |
| 84 const std::string& client_guid) { | |
| 85 crash_keys::SetCrashClientIdFromGUID(client_guid); | |
| 86 } | |
| 87 | |
| 88 #if defined(OS_WIN) | |
| 89 bool ChromeBreakpadClient::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 ChromeBreakpadClient::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 ChromeBreakpadClient::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 ChromeBreakpadClient::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 ChromeBreakpadClient::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 ChromeBreakpadClient::GetIsPerUserInstall(const base::FilePath& exe_path) { | |
| 184 return InstallUtil::IsPerUserInstall(exe_path.value().c_str()); | |
| 185 } | |
| 186 | |
| 187 bool ChromeBreakpadClient::GetShouldDumpLargerDumps(bool is_per_user_install) { | |
| 188 base::string16 channel_name = | |
| 189 GoogleUpdateSettings::GetChromeChannel(!is_per_user_install); | |
| 190 | |
| 191 // Capture more detail in crash dumps for beta and dev channel builds. | |
| 192 return (channel_name == installer::kChromeChannelDev || | |
| 193 channel_name == installer::kChromeChannelBeta || | |
| 194 channel_name == GoogleChromeSxSDistribution::ChannelName()); | |
| 195 } | |
| 196 | |
| 197 int ChromeBreakpadClient::GetResultCodeRespawnFailed() { | |
| 198 return chrome::RESULT_CODE_RESPAWN_FAILED; | |
| 199 } | |
| 200 | |
| 201 void ChromeBreakpadClient::InitBrowserCrashDumpsRegKey() { | |
| 202 DCHECK(g_browser_crash_dump_regkey == NULL); | |
| 203 | |
| 204 base::win::RegKey regkey; | |
| 205 if (regkey.Create(HKEY_CURRENT_USER, | |
| 206 chrome::kBrowserCrashDumpAttemptsRegistryPath, | |
| 207 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 // We use the current process id and the current tick count as a (hopefully) | |
| 212 // unique combination for the crash dump value. There's a small chance that | |
| 213 // across a reboot we might have a crash dump signal written, and the next | |
| 214 // browser process might have the same process id and tick count, but crash | |
| 215 // before consuming the signal (overwriting the signal with an identical one). | |
| 216 // For now, we're willing to live with that risk. | |
| 217 int length = base::strings::SafeSPrintf(g_browser_crash_dump_prefix, | |
| 218 kBrowserCrashDumpPrefixTemplate, | |
| 219 chrome::kChromeVersion, | |
| 220 ::GetCurrentProcessId(), | |
| 221 ::GetTickCount()); | |
| 222 if (length <= 0) { | |
| 223 NOTREACHED(); | |
| 224 g_browser_crash_dump_prefix[0] = '\0'; | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 // Hold the registry key in a global for update on crash dump. | |
| 229 g_browser_crash_dump_regkey = regkey.Take(); | |
| 230 } | |
| 231 | |
| 232 void ChromeBreakpadClient::RecordCrashDumpAttempt(bool is_real_crash) { | |
| 233 // If we're not a browser (or the registry is unavailable to us for some | |
| 234 // reason) then there's nothing to do. | |
| 235 if (g_browser_crash_dump_regkey == NULL) | |
| 236 return; | |
| 237 | |
| 238 // Generate the final value name we'll use (appends the crash number to the | |
| 239 // base value name). | |
| 240 const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength; | |
| 241 char value_name[kMaxValueSize + 1] = {}; | |
| 242 int length = base::strings::SafeSPrintf( | |
| 243 value_name, | |
| 244 "%s-%x", | |
| 245 g_browser_crash_dump_prefix, | |
| 246 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1)); | |
| 247 | |
| 248 if (length > 0) { | |
| 249 DWORD value_dword = is_real_crash ? 1 : 0; | |
| 250 ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD, | |
| 251 reinterpret_cast<BYTE*>(&value_dword), | |
| 252 sizeof(value_dword)); | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 bool ChromeBreakpadClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) { | |
| 257 // Determine whether configuration management allows loading the crash reporter. | |
| 258 // Since the configuration management infrastructure is not initialized at this | |
| 259 // point, we read the corresponding registry key directly. The return status | |
| 260 // indicates whether policy data was successfully read. If it is true, | |
| 261 // |breakpad_enabled| contains the value set by policy. | |
| 262 base::string16 key_name = | |
| 263 base::UTF8ToUTF16(policy::key::kMetricsReportingEnabled); | |
| 264 DWORD value = 0; | |
| 265 base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE, | |
| 266 policy::kRegistryChromePolicyKey, KEY_READ); | |
| 267 if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { | |
| 268 *breakpad_enabled = value != 0; | |
| 269 return true; | |
| 270 } | |
| 271 | |
| 272 base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER, | |
| 273 policy::kRegistryChromePolicyKey, KEY_READ); | |
| 274 if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) { | |
| 275 *breakpad_enabled = value != 0; | |
| 276 return true; | |
| 277 } | |
| 278 | |
| 279 return false; | |
| 280 } | |
| 281 #endif // defined(OS_WIN) | |
| 282 | |
| 283 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS) | |
| 284 void ChromeBreakpadClient::GetProductNameAndVersion(std::string* product_name, | |
| 285 std::string* version) { | |
| 286 DCHECK(product_name); | |
| 287 DCHECK(version); | |
| 288 #if defined(OS_ANDROID) | |
| 289 *product_name = "Chrome_Android"; | |
| 290 #elif defined(OS_CHROMEOS) | |
| 291 *product_name = "Chrome_ChromeOS"; | |
| 292 #else // OS_LINUX | |
| 293 #if !defined(ADDRESS_SANITIZER) | |
| 294 *product_name = "Chrome_Linux"; | |
| 295 #else | |
| 296 *product_name = "Chrome_Linux_ASan"; | |
| 297 #endif | |
| 298 #endif | |
| 299 | |
| 300 *version = PRODUCT_VERSION; | |
| 301 } | |
| 302 | |
| 303 base::FilePath ChromeBreakpadClient::GetReporterLogFilename() { | |
| 304 return base::FilePath(CrashUploadList::kReporterLogFilename); | |
| 305 } | |
| 306 #endif | |
| 307 | |
| 308 bool ChromeBreakpadClient::GetCrashDumpLocation(base::FilePath* crash_dir) { | |
| 309 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate | |
| 310 // location to write breakpad crash dumps can be set. | |
| 311 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 312 std::string alternate_crash_dump_location; | |
| 313 if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { | |
| 314 base::FilePath crash_dumps_dir_path = | |
| 315 base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); | |
| 316 PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path); | |
| 317 } | |
| 318 | |
| 319 return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir); | |
| 320 } | |
| 321 | |
| 322 size_t ChromeBreakpadClient::RegisterCrashKeys() { | |
| 323 // Note: This is not called on Windows because Breakpad is initialized in the | |
| 324 // EXE module, but code that uses crash keys is in the DLL module. | |
| 325 // RegisterChromeCrashKeys() will be called after the DLL is loaded. | |
| 326 return crash_keys::RegisterChromeCrashKeys(); | |
| 327 } | |
| 328 | |
| 329 bool ChromeBreakpadClient::IsRunningUnattended() { | |
| 330 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 331 return env->HasVar(env_vars::kHeadless); | |
| 332 } | |
| 333 | |
| 334 bool ChromeBreakpadClient::GetCollectStatsConsent() { | |
| 335 #if defined(GOOGLE_CHROME_BUILD) | |
| 336 bool is_official_chrome_build = true; | |
| 337 #else | |
| 338 bool is_official_chrome_build = false; | |
| 339 #endif | |
| 340 | |
| 341 #if defined(OS_CHROMEOS) | |
| 342 bool is_guest_session = CommandLine::ForCurrentProcess()->HasSwitch( | |
| 343 chromeos::switches::kGuestSession); | |
| 344 bool is_stable_channel = | |
| 345 chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE; | |
| 346 | |
| 347 if (is_guest_session && is_stable_channel) | |
| 348 return false; | |
| 349 #endif // defined(OS_CHROMEOS) | |
| 350 | |
| 351 #if defined(OS_ANDROID) | |
| 352 // TODO(jcivelli): we should not initialize the crash-reporter when it was not | |
| 353 // enabled. Right now if it is disabled we still generate the minidumps but we | |
| 354 // do not upload them. | |
| 355 return is_official_chrome_build; | |
| 356 #else // !defined(OS_ANDROID) | |
| 357 return is_official_chrome_build && | |
| 358 GoogleUpdateSettings::GetCollectStatsConsent(); | |
| 359 #endif // defined(OS_ANDROID) | |
| 360 } | |
| 361 | |
| 362 #if defined(OS_ANDROID) | |
| 363 int ChromeBreakpadClient::GetAndroidMinidumpDescriptor() { | |
| 364 return kAndroidMinidumpDescriptor; | |
| 365 } | |
| 366 #endif | |
| 367 | |
| 368 bool ChromeBreakpadClient::EnableBreakpadForProcess( | |
| 369 const std::string& process_type) { | |
| 370 return process_type == switches::kRendererProcess || | |
| 371 process_type == switches::kPluginProcess || | |
| 372 process_type == switches::kPpapiPluginProcess || | |
| 373 process_type == switches::kZygoteProcess || | |
| 374 process_type == switches::kGpuProcess; | |
| 375 } | |
| 376 | |
| 377 } // namespace chrome | |
| OLD | NEW |