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.
5 #include "chrome/app/chrome_crash_reporter_client.h"
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"
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"
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_values.h"
42 #include "base/debug/dump_without_crashing.h"
45 #if defined(OS_ANDROID)
46 #include "chrome/common/descriptors_android.h"
49 #if defined(OS_CHROMEOS)
50 #include "chrome/common/chrome_version_info.h"
51 #include "chromeos/chromeos_switches.h"
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";
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] = {};
70 // These registry key to which we'll write a value for each crash dump attempt.
71 HKEY g_browser_crash_dump_regkey
= NULL
;
73 // A atomic counter to make each crash dump value name unique.
74 base::subtle::Atomic32 g_browser_crash_dump_count
= 0;
79 ChromeCrashReporterClient::ChromeCrashReporterClient() {}
81 ChromeCrashReporterClient::~ChromeCrashReporterClient() {}
83 void ChromeCrashReporterClient::SetCrashReporterClientIdFromGUID(
84 const std::string
& client_guid
) {
85 crash_keys::SetCrashClientIdFromGUID(client_guid
);
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
);
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
);
111 DCHECK(special_build
);
112 DCHECK(channel_name
);
114 scoped_ptr
<FileVersionInfo
> version_info(
115 FileVersionInfo::CreateFileVersionInfo(exe_path
));
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"));
123 *product_name
= version_info
->product_short_name();
124 *special_build
= version_info
->special_build();
126 // No version info found. Make up the values.
127 *product_name
= base::ASCIIToUTF16("Chrome");
128 *version
= base::ASCIIToUTF16("0.0.0.0-devel");
131 GoogleUpdateSettings::GetChromeChannelAndModifiers(
132 !GetIsPerUserInstall(exe_path
), channel_name
);
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
)) {
145 std::string restart_info
;
146 env
->GetVar(env_vars::kRestartInfo
, &restart_info
);
148 // The CHROME_RESTART var contains the dialog strings separated by '|'.
149 // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment()
151 std::vector
<std::string
> dlg_strings
;
152 base::SplitString(restart_info
, '|', &dlg_strings
);
154 if (dlg_strings
.size() < 3)
157 *title
= base::UTF8ToUTF16(dlg_strings
[0]);
158 *message
= base::UTF8ToUTF16(dlg_strings
[1]);
159 *is_rtl_locale
= dlg_strings
[2] == env_vars::kRtlLocale
;
163 bool ChromeCrashReporterClient::AboutToRestart() {
164 scoped_ptr
<base::Environment
> env(base::Environment::Create());
165 if (!env
->HasVar(env_vars::kRestartInfo
))
168 env
->SetVar(env_vars::kShowRestart
, "1");
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
)))
183 bool ChromeCrashReporterClient::GetIsPerUserInstall(
184 const base::FilePath
& exe_path
) {
185 return InstallUtil::IsPerUserInstall(exe_path
);
188 bool ChromeCrashReporterClient::GetShouldDumpLargerDumps(
189 bool is_per_user_install
) {
190 base::string16 channel_name
=
191 GoogleUpdateSettings::GetChromeChannel(!is_per_user_install
);
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());
199 int ChromeCrashReporterClient::GetResultCodeRespawnFailed() {
200 return chrome::RESULT_CODE_RESPAWN_FAILED
;
203 void ChromeCrashReporterClient::InitBrowserCrashDumpsRegKey() {
204 DCHECK(g_browser_crash_dump_regkey
== NULL
);
206 base::win::RegKey regkey
;
207 if (regkey
.Create(HKEY_CURRENT_USER
,
208 chrome::kBrowserCrashDumpAttemptsRegistryPath
,
209 KEY_ALL_ACCESS
) != ERROR_SUCCESS
) {
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 if (base::strings::SafeSPrintf(g_browser_crash_dump_prefix
,
220 kBrowserCrashDumpPrefixTemplate
,
221 chrome::kChromeVersion
,
222 ::GetCurrentProcessId(),
223 ::GetTickCount()) <= 0) {
225 g_browser_crash_dump_prefix
[0] = '\0';
229 // Hold the registry key in a global for update on crash dump.
230 g_browser_crash_dump_regkey
= regkey
.Take();
233 void ChromeCrashReporterClient::RecordCrashDumpAttempt(bool is_real_crash
) {
234 // If we're not a browser (or the registry is unavailable to us for some
235 // reason) then there's nothing to do.
236 if (g_browser_crash_dump_regkey
== NULL
)
239 // Generate the final value name we'll use (appends the crash number to the
241 const size_t kMaxValueSize
= 2 * kBrowserCrashDumpPrefixLength
;
242 char value_name
[kMaxValueSize
+ 1] = {};
243 if (base::strings::SafeSPrintf(
244 value_name
, "%s-%x", g_browser_crash_dump_prefix
,
245 base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count
,
247 DWORD value_dword
= is_real_crash
? 1 : 0;
248 ::RegSetValueExA(g_browser_crash_dump_regkey
, value_name
, 0, REG_DWORD
,
249 reinterpret_cast<BYTE
*>(&value_dword
),
250 sizeof(value_dword
));
254 bool ChromeCrashReporterClient::ReportingIsEnforcedByPolicy(
255 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
);
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;
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;
280 #endif // defined(OS_WIN)
282 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
283 void ChromeCrashReporterClient::GetProductNameAndVersion(
284 const char** product_name
,
285 const char** version
) {
286 DCHECK(product_name
);
288 #if defined(OS_ANDROID)
289 *product_name
= "Chrome_Android";
290 #elif defined(OS_CHROMEOS)
291 *product_name
= "Chrome_ChromeOS";
293 #if !defined(ADDRESS_SANITIZER)
294 *product_name
= "Chrome_Linux";
296 *product_name
= "Chrome_Linux_ASan";
300 *version
= PRODUCT_VERSION
;
303 base::FilePath
ChromeCrashReporterClient::GetReporterLogFilename() {
304 return base::FilePath(CrashUploadList::kReporterLogFilename
);
308 bool ChromeCrashReporterClient::GetCrashDumpLocation(
309 base::FilePath
* crash_dir
) {
310 // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
311 // location to write breakpad crash dumps can be set.
312 scoped_ptr
<base::Environment
> env(base::Environment::Create());
313 std::string alternate_crash_dump_location
;
314 if (env
->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location
)) {
315 base::FilePath crash_dumps_dir_path
=
316 base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location
);
317 PathService::Override(chrome::DIR_CRASH_DUMPS
, crash_dumps_dir_path
);
320 return PathService::Get(chrome::DIR_CRASH_DUMPS
, crash_dir
);
323 size_t ChromeCrashReporterClient::RegisterCrashKeys() {
324 // Note: This is not called on Windows because Breakpad is initialized in the
325 // EXE module, but code that uses crash keys is in the DLL module.
326 // RegisterChromeCrashKeys() will be called after the DLL is loaded.
327 return crash_keys::RegisterChromeCrashKeys();
330 bool ChromeCrashReporterClient::IsRunningUnattended() {
331 scoped_ptr
<base::Environment
> env(base::Environment::Create());
332 return env
->HasVar(env_vars::kHeadless
);
335 bool ChromeCrashReporterClient::GetCollectStatsConsent() {
336 #if defined(GOOGLE_CHROME_BUILD)
337 bool is_official_chrome_build
= true;
339 bool is_official_chrome_build
= false;
342 #if defined(OS_CHROMEOS)
343 bool is_guest_session
= base::CommandLine::ForCurrentProcess()->HasSwitch(
344 chromeos::switches::kGuestSession
);
345 bool is_stable_channel
=
346 chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE
;
348 if (is_guest_session
&& is_stable_channel
)
350 #endif // defined(OS_CHROMEOS)
352 #if defined(OS_ANDROID)
353 // TODO(jcivelli): we should not initialize the crash-reporter when it was not
354 // enabled. Right now if it is disabled we still generate the minidumps but we
355 // do not upload them.
356 return is_official_chrome_build
;
357 #else // !defined(OS_ANDROID)
358 return is_official_chrome_build
&&
359 GoogleUpdateSettings::GetCollectStatsConsent();
360 #endif // defined(OS_ANDROID)
363 #if defined(OS_ANDROID)
364 int ChromeCrashReporterClient::GetAndroidMinidumpDescriptor() {
365 return kAndroidMinidumpDescriptor
;
369 bool ChromeCrashReporterClient::EnableBreakpadForProcess(
370 const std::string
& process_type
) {
371 return process_type
== switches::kRendererProcess
||
372 process_type
== switches::kPluginProcess
||
373 process_type
== switches::kPpapiPluginProcess
||
374 process_type
== switches::kZygoteProcess
||
375 process_type
== switches::kGpuProcess
;
378 } // namespace chrome