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 "policy/policy_constants.h"
33#endif
34
35#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
36#include "chrome/browser/crash_upload_list.h"
37#include "chrome/common/chrome_version_info_posix.h"
38#endif
39
40#if defined(OS_POSIX)
41#include "chrome/common/dump_without_crashing.h"
42#endif
43
44#if defined(OS_ANDROID)
45#include "chrome/common/descriptors_android.h"
46#endif
47
48#if defined(OS_CHROMEOS)
49#include "chrome/common/chrome_version_info.h"
50#include "chromeos/chromeos_switches.h"
51#endif
52
53namespace chrome {
54
55namespace {
56
57#if defined(OS_WIN)
58// This is the minimum version of google update that is required for deferred
59// crash uploads to work.
60const char kMinUpdateVersion[] = "1.3.21.115";
61
62// The value name prefix will be of the form {chrome-version}-{pid}-{timestamp}
63// (i.e., "#####.#####.#####.#####-########-########") which easily fits into a
64// 63 character buffer.
65const char kBrowserCrashDumpPrefixTemplate[] = "%s-%08x-%08x";
66const size_t kBrowserCrashDumpPrefixLength = 63;
67char g_browser_crash_dump_prefix[kBrowserCrashDumpPrefixLength + 1] = {};
68
69// These registry key to which we'll write a value for each crash dump attempt.
70HKEY g_browser_crash_dump_regkey = NULL;
71
72// A atomic counter to make each crash dump value name unique.
73base::subtle::Atomic32 g_browser_crash_dump_count = 0;
74#endif
75
76}  // namespace
77
78ChromeBreakpadClient::ChromeBreakpadClient() {}
79
80ChromeBreakpadClient::~ChromeBreakpadClient() {}
81
82void ChromeBreakpadClient::SetClientID(const std::string& client_id) {
83  crash_keys::SetClientID(client_id);
84}
85
86#if defined(OS_WIN)
87bool ChromeBreakpadClient::GetAlternativeCrashDumpLocation(
88    base::FilePath* crash_dir) {
89  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
90  // location to write breakpad crash dumps can be set.
91  scoped_ptr<base::Environment> env(base::Environment::Create());
92  std::string alternate_crash_dump_location;
93  if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
94    *crash_dir = base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
95    return true;
96  }
97
98  return false;
99}
100
101void ChromeBreakpadClient::GetProductNameAndVersion(
102    const base::FilePath& exe_path,
103    base::string16* product_name,
104    base::string16* version,
105    base::string16* special_build,
106    base::string16* channel_name) {
107  DCHECK(product_name);
108  DCHECK(version);
109  DCHECK(special_build);
110  DCHECK(channel_name);
111
112  scoped_ptr<FileVersionInfo> version_info(
113      FileVersionInfo::CreateFileVersionInfo(exe_path));
114
115  if (version_info.get()) {
116    // Get the information from the file.
117    *version = version_info->product_version();
118    if (!version_info->is_official_build())
119      version->append(base::ASCIIToUTF16("-devel"));
120
121    const CommandLine& command = *CommandLine::ForCurrentProcess();
122    if (command.HasSwitch(switches::kChromeFrame)) {
123      *product_name = base::ASCIIToUTF16("ChromeFrame");
124    } else {
125      *product_name = version_info->product_short_name();
126    }
127
128    *special_build = version_info->special_build();
129  } else {
130    // No version info found. Make up the values.
131    *product_name = base::ASCIIToUTF16("Chrome");
132    *version = base::ASCIIToUTF16("0.0.0.0-devel");
133  }
134
135  std::wstring channel_string;
136  GoogleUpdateSettings::GetChromeChannelAndModifiers(
137      !GetIsPerUserInstall(exe_path), &channel_string);
138  *channel_name = base::WideToUTF16(channel_string);
139}
140
141bool ChromeBreakpadClient::ShouldShowRestartDialog(base::string16* title,
142                                                   base::string16* message,
143                                                   bool* is_rtl_locale) {
144  scoped_ptr<base::Environment> env(base::Environment::Create());
145  if (!env->HasVar(env_vars::kShowRestart) ||
146      !env->HasVar(env_vars::kRestartInfo) ||
147      env->HasVar(env_vars::kMetroConnected)) {
148    return false;
149  }
150
151  std::string restart_info;
152  env->GetVar(env_vars::kRestartInfo, &restart_info);
153
154  // The CHROME_RESTART var contains the dialog strings separated by '|'.
155  // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment()
156  // for details.
157  std::vector<std::string> dlg_strings;
158  base::SplitString(restart_info, '|', &dlg_strings);
159
160  if (dlg_strings.size() < 3)
161    return false;
162
163  *title = base::UTF8ToUTF16(dlg_strings[0]);
164  *message = base::UTF8ToUTF16(dlg_strings[1]);
165  *is_rtl_locale = dlg_strings[2] == env_vars::kRtlLocale;
166  return true;
167}
168
169bool ChromeBreakpadClient::AboutToRestart() {
170  scoped_ptr<base::Environment> env(base::Environment::Create());
171  if (!env->HasVar(env_vars::kRestartInfo))
172    return false;
173
174  env->SetVar(env_vars::kShowRestart, "1");
175  return true;
176}
177
178bool ChromeBreakpadClient::GetDeferredUploadsSupported(
179    bool is_per_user_install) {
180  Version update_version = GoogleUpdateSettings::GetGoogleUpdateVersion(
181      !is_per_user_install);
182  if (!update_version.IsValid() ||
183      update_version.IsOlderThan(std::string(kMinUpdateVersion)))
184    return false;
185
186  return true;
187}
188
189bool ChromeBreakpadClient::GetIsPerUserInstall(const base::FilePath& exe_path) {
190  return InstallUtil::IsPerUserInstall(exe_path.value().c_str());
191}
192
193bool ChromeBreakpadClient::GetShouldDumpLargerDumps(bool is_per_user_install) {
194  base::string16 channel_name(base::WideToUTF16(
195      GoogleUpdateSettings::GetChromeChannel(!is_per_user_install)));
196
197  // Capture more detail in crash dumps for beta and dev channel builds.
198  if (channel_name == base::ASCIIToUTF16("dev") ||
199      channel_name == base::ASCIIToUTF16("beta") ||
200      channel_name == GoogleChromeSxSDistribution::ChannelName())
201    return true;
202  return false;
203}
204
205int ChromeBreakpadClient::GetResultCodeRespawnFailed() {
206  return chrome::RESULT_CODE_RESPAWN_FAILED;
207}
208
209void ChromeBreakpadClient::InitBrowserCrashDumpsRegKey() {
210  DCHECK(g_browser_crash_dump_regkey == NULL);
211
212  base::win::RegKey regkey;
213  if (regkey.Create(HKEY_CURRENT_USER,
214                    chrome::kBrowserCrashDumpAttemptsRegistryPath,
215                    KEY_ALL_ACCESS) != ERROR_SUCCESS) {
216    return;
217  }
218
219  // We use the current process id and the current tick count as a (hopefully)
220  // unique combination for the crash dump value. There's a small chance that
221  // across a reboot we might have a crash dump signal written, and the next
222  // browser process might have the same process id and tick count, but crash
223  // before consuming the signal (overwriting the signal with an identical one).
224  // For now, we're willing to live with that risk.
225  int length = base::strings::SafeSPrintf(g_browser_crash_dump_prefix,
226                                          kBrowserCrashDumpPrefixTemplate,
227                                          chrome::kChromeVersion,
228                                          ::GetCurrentProcessId(),
229                                          ::GetTickCount());
230  if (length <= 0) {
231    NOTREACHED();
232    g_browser_crash_dump_prefix[0] = '\0';
233    return;
234  }
235
236  // Hold the registry key in a global for update on crash dump.
237  g_browser_crash_dump_regkey = regkey.Take();
238}
239
240void ChromeBreakpadClient::RecordCrashDumpAttempt(bool is_real_crash) {
241  // If we're not a browser (or the registry is unavailable to us for some
242  // reason) then there's nothing to do.
243  if (g_browser_crash_dump_regkey == NULL)
244    return;
245
246  // Generate the final value name we'll use (appends the crash number to the
247  // base value name).
248  const size_t kMaxValueSize = 2 * kBrowserCrashDumpPrefixLength;
249  char value_name[kMaxValueSize + 1] = {};
250  int length = base::strings::SafeSPrintf(
251      value_name,
252      "%s-%x",
253      g_browser_crash_dump_prefix,
254      base::subtle::NoBarrier_AtomicIncrement(&g_browser_crash_dump_count, 1));
255
256  if (length > 0) {
257    DWORD value_dword = is_real_crash ? 1 : 0;
258    ::RegSetValueExA(g_browser_crash_dump_regkey, value_name, 0, REG_DWORD,
259                     reinterpret_cast<BYTE*>(&value_dword),
260                     sizeof(value_dword));
261  }
262}
263
264bool ChromeBreakpadClient::ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
265// Determine whether configuration management allows loading the crash reporter.
266// Since the configuration management infrastructure is not initialized at this
267// point, we read the corresponding registry key directly. The return status
268// indicates whether policy data was successfully read. If it is true,
269// |breakpad_enabled| contains the value set by policy.
270  string16 key_name = UTF8ToUTF16(policy::key::kMetricsReportingEnabled);
271  DWORD value = 0;
272  base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE,
273                                    policy::kRegistryChromePolicyKey, KEY_READ);
274  if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
275    *breakpad_enabled = value != 0;
276    return true;
277  }
278
279  base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER,
280                                    policy::kRegistryChromePolicyKey, KEY_READ);
281  if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
282    *breakpad_enabled = value != 0;
283    return true;
284  }
285
286  return false;
287}
288#endif
289
290#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_IOS)
291void ChromeBreakpadClient::GetProductNameAndVersion(std::string* product_name,
292                                                    std::string* version) {
293  DCHECK(product_name);
294  DCHECK(version);
295#if defined(OS_ANDROID)
296  *product_name = "Chrome_Android";
297#elif defined(OS_CHROMEOS)
298  *product_name = "Chrome_ChromeOS";
299#else  // OS_LINUX
300#if !defined(ADDRESS_SANITIZER)
301  *product_name = "Chrome_Linux";
302#else
303  *product_name = "Chrome_Linux_ASan";
304#endif
305#endif
306
307  *version = PRODUCT_VERSION;
308}
309
310base::FilePath ChromeBreakpadClient::GetReporterLogFilename() {
311  return base::FilePath(CrashUploadList::kReporterLogFilename);
312}
313#endif
314
315bool ChromeBreakpadClient::GetCrashDumpLocation(base::FilePath* crash_dir) {
316  // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate
317  // location to write breakpad crash dumps can be set.
318  scoped_ptr<base::Environment> env(base::Environment::Create());
319  std::string alternate_crash_dump_location;
320  if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) {
321    base::FilePath crash_dumps_dir_path =
322        base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location);
323    PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path);
324  }
325
326  return PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir);
327}
328
329#if defined(OS_POSIX)
330void ChromeBreakpadClient::SetDumpWithoutCrashingFunction(void (*function)()) {
331  logging::SetDumpWithoutCrashingFunction(function);
332}
333#endif
334
335size_t ChromeBreakpadClient::RegisterCrashKeys() {
336  // Note: This is not called on Windows because Breakpad is initialized in the
337  // EXE module, but code that uses crash keys is in the DLL module.
338  // RegisterChromeCrashKeys() will be called after the DLL is loaded.
339  return crash_keys::RegisterChromeCrashKeys();
340}
341
342bool ChromeBreakpadClient::IsRunningUnattended() {
343  scoped_ptr<base::Environment> env(base::Environment::Create());
344  return env->HasVar(env_vars::kHeadless);
345}
346
347bool ChromeBreakpadClient::GetCollectStatsConsent() {
348  // Convert #define to a variable so that we can use if() rather than
349  // #if below and so at least compile-test the Chrome code in
350  // Chromium builds.
351#if defined(GOOGLE_CHROME_BUILD)
352  bool is_chrome_build = true;
353#else
354  bool is_chrome_build = false;
355#endif
356
357#if defined(OS_CHROMEOS)
358  bool is_guest_session = CommandLine::ForCurrentProcess()->HasSwitch(
359      chromeos::switches::kGuestSession);
360  bool is_stable_channel =
361      chrome::VersionInfo::GetChannel() == chrome::VersionInfo::CHANNEL_STABLE;
362
363  if (is_guest_session && is_stable_channel)
364    return false;
365#endif  // defined(OS_CHROMEOS)
366
367#if defined(OS_ANDROID)
368  // TODO(jcivelli): we should not initialize the crash-reporter when it was not
369  // enabled. Right now if it is disabled we still generate the minidumps but we
370  // do not upload them.
371  return is_chrome_build;
372#else  // !defined(OS_ANDROID)
373  return is_chrome_build && GoogleUpdateSettings::GetCollectStatsConsent();
374#endif  // defined(OS_ANDROID)
375}
376
377#if defined(OS_ANDROID)
378int ChromeBreakpadClient::GetAndroidMinidumpDescriptor() {
379  return kAndroidMinidumpDescriptor;
380}
381#endif
382
383bool ChromeBreakpadClient::EnableBreakpadForProcess(
384    const std::string& process_type) {
385  return process_type == switches::kRendererProcess ||
386         process_type == switches::kPluginProcess ||
387         process_type == switches::kPpapiPluginProcess ||
388         process_type == switches::kZygoteProcess ||
389         process_type == switches::kGpuProcess;
390}
391
392}  // namespace chrome
393