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
ChromeCrashReporterClient()79 ChromeCrashReporterClient::ChromeCrashReporterClient() {}
80
~ChromeCrashReporterClient()81 ChromeCrashReporterClient::~ChromeCrashReporterClient() {}
82
SetCrashReporterClientIdFromGUID(const std::string & client_guid)83 void ChromeCrashReporterClient::SetCrashReporterClientIdFromGUID(
84 const std::string& client_guid) {
85 crash_keys::SetCrashClientIdFromGUID(client_guid);
86 }
87
88 #if defined(OS_WIN)
GetAlternativeCrashDumpLocation(base::FilePath * crash_dir)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
GetProductNameAndVersion(const base::FilePath & exe_path,base::string16 * product_name,base::string16 * version,base::string16 * special_build,base::string16 * channel_name)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
ShouldShowRestartDialog(base::string16 * title,base::string16 * message,bool * is_rtl_locale)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
AboutToRestart()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
GetDeferredUploadsSupported(bool is_per_user_install)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
GetIsPerUserInstall(const base::FilePath & exe_path)183 bool ChromeCrashReporterClient::GetIsPerUserInstall(
184 const base::FilePath& exe_path) {
185 return InstallUtil::IsPerUserInstall(exe_path.value().c_str());
186 }
187
GetShouldDumpLargerDumps(bool is_per_user_install)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
GetResultCodeRespawnFailed()199 int ChromeCrashReporterClient::GetResultCodeRespawnFailed() {
200 return chrome::RESULT_CODE_RESPAWN_FAILED;
201 }
202
InitBrowserCrashDumpsRegKey()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
RecordCrashDumpAttempt(bool is_real_crash)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
ReportingIsEnforcedByPolicy(bool * breakpad_enabled)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)
GetProductNameAndVersion(std::string * product_name,std::string * version)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
GetReporterLogFilename()307 base::FilePath ChromeCrashReporterClient::GetReporterLogFilename() {
308 return base::FilePath(CrashUploadList::kReporterLogFilename);
309 }
310 #endif
311
GetCrashDumpLocation(base::FilePath * crash_dir)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
RegisterCrashKeys()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
IsRunningUnattended()334 bool ChromeCrashReporterClient::IsRunningUnattended() {
335 scoped_ptr<base::Environment> env(base::Environment::Create());
336 return env->HasVar(env_vars::kHeadless);
337 }
338
GetCollectStatsConsent()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)
GetAndroidMinidumpDescriptor()368 int ChromeCrashReporterClient::GetAndroidMinidumpDescriptor() {
369 return kAndroidMinidumpDescriptor;
370 }
371 #endif
372
EnableBreakpadForProcess(const std::string & process_type)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