• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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