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 |