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