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
54namespace chrome {
55
56namespace {
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.
61const 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.
66const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x";
67const size_t kBrowserCrashDumpPrefixLength = 63;
68char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {};
69
70// These registry key to which we'll write a value for each crash dump attempt.
71HKEY g_browser_crash_dump_regkey = NULL;
72
73// A atomic counter to make each crash dump value name unique.
74base::subtle::Atomic32 g_browser_crash_dump_count = 0;
75#endif
76
77}  // namespace
78
79ChromeCrashReporterClient::ChromeCrashReporterClient() {}
80
81ChromeCrashReporterClient::~ChromeCrashReporterClient() {}
82
83void ChromeCrashReporterClient::SetCrashReporterClientIdFromGUID(
84    const std::string& client_guid) {
85  crash_keys::SetCrashClientIdFromGUID(client_guid);
86}
87
88#if defined(OS_WIN)
89bool 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
103void 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
135bool 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
163bool 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
172bool 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
183bool ChromeCrashReporterClient::GetIsPerUserInstall(
184    const base::FilePath& exe_path) {
185  return InstallUtil::IsPerUserInstall(exe_path.value().c_str());
186}
187
188bool 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
199int ChromeCrashReporterClient::GetResultCodeRespawnFailed() {
200  return chrome::RESULT_CODE_RESPAWN_FAILED;
201}
202
203void 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
234void 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
258bool 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)
287void 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
307base::FilePath ChromeCrashReporterClient::GetReporterLogFilename() {
308  return base::FilePath(CrashUploadList::kReporterLogFilename);
309}
310#endif
311
312bool 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
327size_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
334bool ChromeCrashReporterClient::IsRunningUnattended() {
335  scoped_ptr<base::Environment> env(base::Environment::Create());
336  return env->HasVar(env_vars::kHeadless);
337}
338
339bool 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)
368int ChromeCrashReporterClient::GetAndroidMinidumpDescriptor() {
369  return kAndroidMinidumpDescriptor;
370}
371#endif
372
373bool 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