Return backed up TemplateURL on default search change
[chromium-blink-merge.git] / chrome / app / breakpad_win.cc
blob57f9d6f3e0ea66c77c50c1d44abea508de6c9cef
1 // Copyright (c) 2011 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.
5 #include "chrome/app/breakpad_win.h"
7 #include <windows.h>
8 #include <shellapi.h>
9 #include <tchar.h>
11 #include <algorithm>
12 #include <vector>
14 #include "base/base_switches.h"
15 #include "base/command_line.h"
16 #include "base/environment.h"
17 #include "base/file_util.h"
18 #include "base/file_version_info.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/string_split.h"
21 #include "base/string_util.h"
22 #include "base/stringprintf.h"
23 #include "base/utf_string_conversions.h"
24 #include "base/win/registry.h"
25 #include "base/win/win_util.h"
26 #include "breakpad/src/client/windows/handler/exception_handler.h"
27 #include "chrome/app/hard_error_handler_win.h"
28 #include "chrome/common/child_process_logging.h"
29 #include "chrome/common/chrome_result_codes.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/env_vars.h"
32 #include "chrome/installer/util/google_chrome_sxs_distribution.h"
33 #include "chrome/installer/util/google_update_settings.h"
34 #include "chrome/installer/util/install_util.h"
35 #include "policy/policy_constants.h"
37 namespace {
39 // Minidump with stacks, PEB, TEB, and unloaded module list.
40 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
41 MiniDumpWithProcessThreadData | // Get PEB and TEB.
42 MiniDumpWithUnloadedModules); // Get unloaded modules when available.
44 // Minidump with all of the above, plus memory referenced from stack.
45 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
46 MiniDumpWithProcessThreadData | // Get PEB and TEB.
47 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
48 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
50 // Large dump with all process memory.
51 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
52 MiniDumpWithFullMemory | // Full memory from process.
53 MiniDumpWithProcessThreadData | // Get PEB and TEB.
54 MiniDumpWithHandleData | // Get all handle information.
55 MiniDumpWithUnloadedModules); // Get unloaded modules when available.
57 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
58 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
60 // This is the well known SID for the system principal.
61 const wchar_t kSystemPrincipalSid[] =L"S-1-5-18";
63 google_breakpad::ExceptionHandler* g_breakpad = NULL;
65 // A pointer to the custom entries that we send in the event of a crash. We need
66 // this pointer, along with the offsets into it below, so that we can keep the
67 // data updated as the state of the browser changes.
68 static std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL;
69 static size_t g_url_chunks_offset;
70 static size_t g_num_of_extensions_offset;
71 static size_t g_extension_ids_offset;
72 static size_t g_client_id_offset;
73 static size_t g_gpu_info_offset;
74 static size_t g_num_of_views_offset;
75 static size_t g_num_switches_offset;
76 static size_t g_switches_offset;
78 // Maximum length for plugin path to include in plugin crash reports.
79 const size_t kMaxPluginPathLength = 256;
81 // Dumps the current process memory.
82 extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
83 if (g_breakpad)
84 g_breakpad->WriteMinidump();
87 // Reduces the size of the string |str| to a max of 64 chars. Required because
88 // breakpad's CustomInfoEntry raises an invalid_parameter error if the string
89 // we want to set is longer.
90 std::wstring TrimToBreakpadMax(const std::wstring& str) {
91 std::wstring shorter(str);
92 return shorter.substr(0,
93 google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
96 static void SetIntegerValue(size_t offset, int value) {
97 if (!g_custom_entries)
98 return;
100 base::wcslcpy((*g_custom_entries)[offset].value,
101 base::StringPrintf(L"%d", value).c_str(),
102 google_breakpad::CustomInfoEntry::kValueMaxLength);
105 bool IsBoringCommandLineSwitch(const std::wstring& flag) {
106 return StartsWith(flag, L"--channel=", true) ||
108 // No point to including this since we already have a ptype field.
109 StartsWith(flag, L"--type=", true) ||
111 // Not particularly interesting
112 StartsWith(flag, L"--flash-broker=", true) ||
114 // Just about everything has this, don't bother.
115 StartsWith(flag, L"/prefetch:", true) ||
117 // We handle the plugin path separately since it is usually too big
118 // to fit in the switches (limited to 63 characters).
119 StartsWith(flag, L"--plugin-path=", true) ||
121 // This is too big so we end up truncating it anyway.
122 StartsWith(flag, L"--force-fieldtest=", true) ||
124 // These surround the flags that were added by about:flags, it lets
125 // you distinguish which flags were added manually via the command
126 // line versus those added through about:flags. For the most part
127 // we don't care how an option was enabled, so we strip these.
128 // (If you need to know can always look at the PEB).
129 flag == L"--flag-switches-begin" ||
130 flag == L"--flag-switches-end";
133 extern "C" void __declspec(dllexport) __cdecl SetCommandLine(
134 const CommandLine* command_line) {
135 if (!g_custom_entries)
136 return;
138 const CommandLine::StringVector& argv = command_line->argv();
140 // Copy up to the kMaxSwitches arguments into the custom entries array. Skip
141 // past the first argument, as it is just the executable path.
142 size_t argv_i = 1;
143 size_t num_added = 0;
145 for (; argv_i < argv.size() && num_added < kMaxSwitches; ++argv_i) {
146 // Don't bother including boring command line switches in crash reports.
147 if (IsBoringCommandLineSwitch(argv[argv_i]))
148 continue;
150 base::wcslcpy((*g_custom_entries)[g_switches_offset + num_added].value,
151 argv[argv_i].c_str(),
152 google_breakpad::CustomInfoEntry::kValueMaxLength);
153 num_added++;
156 // Make note of the total number of switches. This is useful in case we have
157 // truncated at kMaxSwitches, to see how many were unaccounted for.
158 SetIntegerValue(g_num_switches_offset, static_cast<int>(argv.size()) - 1);
161 // Appends the plugin path to |g_custom_entries|.
162 void SetPluginPath(const std::wstring& path) {
163 DCHECK(g_custom_entries);
165 if (path.size() > kMaxPluginPathLength) {
166 // If the path is too long, truncate from the start rather than the end,
167 // since we want to be able to recover the DLL name.
168 SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
169 return;
172 // The chunk size without terminator.
173 const size_t kChunkSize = static_cast<size_t>(
174 google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
176 int chunk_index = 0;
177 size_t chunk_start = 0; // Current position inside |path|
179 for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
180 size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
182 g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
183 base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
184 path.substr(chunk_start, chunk_length).c_str()));
186 chunk_start += chunk_length;
190 // Returns the custom info structure based on the dll in parameter and the
191 // process type.
192 google_breakpad::CustomClientInfo* GetCustomInfo(const std::wstring& dll_path,
193 const std::wstring& type,
194 const std::wstring& channel) {
195 scoped_ptr<FileVersionInfo>
196 version_info(FileVersionInfo::CreateFileVersionInfo(FilePath(dll_path)));
198 std::wstring version, product;
199 std::wstring special_build;
200 if (version_info.get()) {
201 // Get the information from the file.
202 version = version_info->product_version();
203 if (!version_info->is_official_build())
204 version.append(L"-devel");
206 const CommandLine& command = *CommandLine::ForCurrentProcess();
207 if (command.HasSwitch(switches::kChromeFrame)) {
208 product = L"ChromeFrame";
209 } else {
210 product = version_info->product_short_name();
213 special_build = version_info->special_build();
214 } else {
215 // No version info found. Make up the values.
216 product = L"Chrome";
217 version = L"0.0.0.0-devel";
220 // We only expect this method to be called once per process.
221 DCHECK(!g_custom_entries);
222 g_custom_entries = new std::vector<google_breakpad::CustomInfoEntry>;
224 // Common g_custom_entries.
225 g_custom_entries->push_back(
226 google_breakpad::CustomInfoEntry(L"ver", version.c_str()));
227 g_custom_entries->push_back(
228 google_breakpad::CustomInfoEntry(L"prod", product.c_str()));
229 g_custom_entries->push_back(
230 google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
231 g_custom_entries->push_back(
232 google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
233 g_custom_entries->push_back(
234 google_breakpad::CustomInfoEntry(L"channel", channel.c_str()));
236 if (!special_build.empty())
237 g_custom_entries->push_back(
238 google_breakpad::CustomInfoEntry(L"special", special_build.c_str()));
240 g_num_of_extensions_offset = g_custom_entries->size();
241 g_custom_entries->push_back(
242 google_breakpad::CustomInfoEntry(L"num-extensions", L"N/A"));
244 g_extension_ids_offset = g_custom_entries->size();
245 for (int i = 0; i < kMaxReportedActiveExtensions; ++i) {
246 g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
247 base::StringPrintf(L"extension-%i", i + 1).c_str(), L""));
250 // Add empty values for the gpu_info. We'll put the actual values
251 // when we collect them at this location.
252 g_gpu_info_offset = g_custom_entries->size();
253 g_custom_entries->push_back(
254 google_breakpad::CustomInfoEntry(L"gpu-venid", L""));
255 g_custom_entries->push_back(
256 google_breakpad::CustomInfoEntry(L"gpu-devid", L""));
257 g_custom_entries->push_back(
258 google_breakpad::CustomInfoEntry(L"gpu-driver", L""));
259 g_custom_entries->push_back(
260 google_breakpad::CustomInfoEntry(L"gpu-psver", L""));
261 g_custom_entries->push_back(
262 google_breakpad::CustomInfoEntry(L"gpu-vsver", L""));
264 // Read the id from registry. If reporting has never been enabled
265 // the result will be empty string. Its OK since when user enables reporting
266 // we will insert the new value at this location.
267 std::wstring guid;
268 GoogleUpdateSettings::GetMetricsId(&guid);
269 g_client_id_offset = g_custom_entries->size();
270 g_custom_entries->push_back(
271 google_breakpad::CustomInfoEntry(L"guid", guid.c_str()));
273 // Add empty values for the command line switches. We will fill them with
274 // actual values as part of SetCommandLine().
275 g_num_switches_offset = g_custom_entries->size();
276 g_custom_entries->push_back(
277 google_breakpad::CustomInfoEntry(L"num-switches", L""));
279 g_switches_offset = g_custom_entries->size();
280 for (int i = 0; i < kMaxSwitches; ++i) {
281 g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
282 base::StringPrintf(L"switch-%i", i + 1).c_str(), L""));
285 // Fill in the command line arguments using CommandLine::ForCurrentProcess().
286 // The browser process may call SetCommandLine() again later on with a command
287 // line that has been augmented with the about:flags experiments.
288 SetCommandLine(CommandLine::ForCurrentProcess());
290 if (type == L"renderer" || type == L"plugin" || type == L"gpu-process") {
291 g_num_of_views_offset = g_custom_entries->size();
292 g_custom_entries->push_back(
293 google_breakpad::CustomInfoEntry(L"num-views", L""));
294 // Create entries for the URL. Currently we only allow each chunk to be 64
295 // characters, which isn't enough for a URL. As a hack we create 8 entries
296 // and split the URL across the g_custom_entries.
297 g_url_chunks_offset = g_custom_entries->size();
298 for (int i = 0; i < kMaxUrlChunks; ++i) {
299 g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
300 base::StringPrintf(L"url-chunk-%i", i + 1).c_str(), L""));
303 if (type == L"plugin") {
304 std::wstring plugin_path =
305 CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path");
306 if (!plugin_path.empty())
307 SetPluginPath(plugin_path);
309 } else {
310 g_custom_entries->push_back(
311 google_breakpad::CustomInfoEntry(L"num-views", L"N/A"));
314 static google_breakpad::CustomClientInfo custom_client_info;
315 custom_client_info.entries = &g_custom_entries->front();
316 custom_client_info.count = g_custom_entries->size();
318 return &custom_client_info;
321 // Contains the information needed by the worker thread.
322 struct CrashReporterInfo {
323 google_breakpad::CustomClientInfo* custom_info;
324 std::wstring dll_path;
325 std::wstring process_type;
328 // This callback is executed when the browser process has crashed, after
329 // the crash dump has been created. We need to minimize the amount of work
330 // done here since we have potentially corrupted process. Our job is to
331 // spawn another instance of chrome which will show a 'chrome has crashed'
332 // dialog. This code needs to live in the exe and thus has no access to
333 // facilities such as the i18n helpers.
334 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
335 EXCEPTION_POINTERS* ex_info,
336 MDRawAssertionInfo*, bool) {
337 // If the exception is because there was a problem loading a delay-loaded
338 // module, then show the user a dialog explaining the problem and then exit.
339 if (DelayLoadFailureExceptionMessageBox(ex_info))
340 return true;
342 // We set CHROME_CRASHED env var. If the CHROME_RESTART is present.
343 // This signals the child process to show the 'chrome has crashed' dialog.
344 scoped_ptr<base::Environment> env(base::Environment::Create());
345 if (!env->HasVar(env_vars::kRestartInfo)) {
346 return true;
348 env->SetVar(env_vars::kShowRestart, "1");
349 // Now we just start chrome browser with the same command line.
350 STARTUPINFOW si = {sizeof(si)};
351 PROCESS_INFORMATION pi;
352 if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE,
353 CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) {
354 ::CloseHandle(pi.hProcess);
355 ::CloseHandle(pi.hThread);
357 // After this return we will be terminated. The actual return value is
358 // not used at all.
359 return true;
362 // flag to indicate that we are already handling an exception.
363 volatile LONG handling_exception = 0;
365 // This callback is executed when the Chrome process has crashed and *before*
366 // the crash dump is created. To prevent duplicate crash reports we
367 // make every thread calling this method, except the very first one,
368 // go to sleep.
369 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
370 // Capture every thread except the first one in the sleep. We don't
371 // want multiple threads to concurrently report exceptions.
372 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) {
373 ::Sleep(INFINITE);
375 return true;
378 // Previous unhandled filter. Will be called if not null when we
379 // intercept a crash.
380 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL;
382 // Exception filter used when breakpad is not enabled. We just display
383 // the "Do you want to restart" message and then we call the previous filter.
384 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
385 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
387 if (previous_filter)
388 return previous_filter(info);
390 return EXCEPTION_EXECUTE_HANDLER;
393 // Exception filter for the service process used when breakpad is not enabled.
394 // We just display the "Do you want to restart" message and then die
395 // (without calling the previous filter).
396 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
397 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
398 return EXCEPTION_EXECUTE_HANDLER;
401 extern "C" void __declspec(dllexport) __cdecl SetActiveURL(
402 const wchar_t* url_cstring) {
403 DCHECK(url_cstring);
405 if (!g_custom_entries)
406 return;
408 std::wstring url(url_cstring);
409 size_t chunk_index = 0;
410 size_t url_size = url.size();
412 // Split the url across all the chunks.
413 for (size_t url_offset = 0;
414 chunk_index < kMaxUrlChunks && url_offset < url_size; ++chunk_index) {
415 size_t current_chunk_size = std::min(url_size - url_offset,
416 static_cast<size_t>(
417 google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
419 wchar_t* entry_value =
420 (*g_custom_entries)[g_url_chunks_offset + chunk_index].value;
421 url._Copy_s(entry_value,
422 google_breakpad::CustomInfoEntry::kValueMaxLength,
423 current_chunk_size, url_offset);
424 entry_value[current_chunk_size] = L'\0';
425 url_offset += current_chunk_size;
428 // And null terminate any unneeded chunks.
429 for (; chunk_index < kMaxUrlChunks; ++chunk_index)
430 (*g_custom_entries)[g_url_chunks_offset + chunk_index].value[0] = L'\0';
433 extern "C" void __declspec(dllexport) __cdecl SetClientId(
434 const wchar_t* client_id) {
435 if (client_id == NULL)
436 return;
438 if (!g_custom_entries)
439 return;
441 base::wcslcpy((*g_custom_entries)[g_client_id_offset].value,
442 client_id,
443 google_breakpad::CustomInfoEntry::kValueMaxLength);
446 extern "C" void __declspec(dllexport) __cdecl SetNumberOfExtensions(
447 int number_of_extensions) {
448 SetIntegerValue(g_num_of_extensions_offset, number_of_extensions);
451 extern "C" void __declspec(dllexport) __cdecl SetExtensionID(
452 int index, const wchar_t* id) {
453 DCHECK(id);
454 DCHECK(index < kMaxReportedActiveExtensions);
456 if (!g_custom_entries)
457 return;
459 base::wcslcpy((*g_custom_entries)[g_extension_ids_offset + index].value,
461 google_breakpad::CustomInfoEntry::kValueMaxLength);
464 extern "C" void __declspec(dllexport) __cdecl SetGpuInfo(
465 const wchar_t* vendor_id, const wchar_t* device_id,
466 const wchar_t* driver_version, const wchar_t* pixel_shader_version,
467 const wchar_t* vertex_shader_version) {
468 if (!g_custom_entries)
469 return;
471 base::wcslcpy((*g_custom_entries)[g_gpu_info_offset].value,
472 vendor_id,
473 google_breakpad::CustomInfoEntry::kValueMaxLength);
474 base::wcslcpy((*g_custom_entries)[g_gpu_info_offset+1].value,
475 device_id,
476 google_breakpad::CustomInfoEntry::kValueMaxLength);
477 base::wcslcpy((*g_custom_entries)[g_gpu_info_offset+2].value,
478 driver_version,
479 google_breakpad::CustomInfoEntry::kValueMaxLength);
480 base::wcslcpy((*g_custom_entries)[g_gpu_info_offset+3].value,
481 pixel_shader_version,
482 google_breakpad::CustomInfoEntry::kValueMaxLength);
483 base::wcslcpy((*g_custom_entries)[g_gpu_info_offset+4].value,
484 vertex_shader_version,
485 google_breakpad::CustomInfoEntry::kValueMaxLength);
488 extern "C" void __declspec(dllexport) __cdecl SetNumberOfViews(
489 int number_of_views) {
490 SetIntegerValue(g_num_of_views_offset, number_of_views);
493 } // namespace
495 bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption,
496 UINT flags, bool* exit_now) {
497 // We wrap the call to MessageBoxW with a SEH handler because it some
498 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
499 // uncontrollably here. Being this a best effort deal we better go away.
500 __try {
501 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
502 } __except(EXCEPTION_EXECUTE_HANDLER) {
503 // Its not safe to continue executing, exit silently here.
504 ::ExitProcess(chrome::RESULT_CODE_RESPAWN_FAILED);
507 return true;
510 // This function is executed by the child process that DumpDoneCallback()
511 // spawned and basically just shows the 'chrome has crashed' dialog if
512 // the CHROME_CRASHED environment variable is present.
513 bool ShowRestartDialogIfCrashed(bool* exit_now) {
514 if (!::GetEnvironmentVariableW(ASCIIToWide(env_vars::kShowRestart).c_str(),
515 NULL, 0)) {
516 return false;
519 DWORD len = ::GetEnvironmentVariableW(
520 ASCIIToWide(env_vars::kRestartInfo).c_str(), NULL, 0);
521 if (!len)
522 return true;
524 wchar_t* restart_data = new wchar_t[len + 1];
525 ::GetEnvironmentVariableW(ASCIIToWide(env_vars::kRestartInfo).c_str(),
526 restart_data, len);
527 restart_data[len] = 0;
528 // The CHROME_RESTART var contains the dialog strings separated by '|'.
529 // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment()
530 // for details.
531 std::vector<std::wstring> dlg_strings;
532 base::SplitString(restart_data, L'|', &dlg_strings);
533 delete[] restart_data;
534 if (dlg_strings.size() < 3)
535 return true;
537 // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
538 // flags so that an RTL message box is displayed.
539 UINT flags = MB_OKCANCEL | MB_ICONWARNING;
540 if (dlg_strings[2] == ASCIIToWide(env_vars::kRtlLocale))
541 flags |= MB_RIGHT | MB_RTLREADING;
543 return WrapMessageBoxWithSEH(dlg_strings[1].c_str(), dlg_strings[0].c_str(),
544 flags, exit_now);
547 // Crashes the process after generating a dump for the provided exception. Note
548 // that the crash reporter should be initialized before calling this function
549 // for it to do anything.
550 extern "C" int __declspec(dllexport) CrashForException(
551 EXCEPTION_POINTERS* info) {
552 if (g_breakpad) {
553 g_breakpad->WriteMinidumpForException(info);
554 ::ExitProcess(content::RESULT_CODE_KILLED);
556 return EXCEPTION_CONTINUE_SEARCH;
559 // Determine whether configuration management allows loading the crash reporter.
560 // Since the configuration management infrastructure is not initialized at this
561 // point, we read the corresponding registry key directly. The return status
562 // indicates whether policy data was successfully read. If it is true, |result|
563 // contains the value set by policy.
564 static bool MetricsReportingControlledByPolicy(bool* result) {
565 std::wstring key_name = UTF8ToWide(policy::key::kMetricsReportingEnabled);
566 DWORD value = 0;
567 base::win::RegKey hklm_policy_key(HKEY_LOCAL_MACHINE,
568 policy::kRegistryMandatorySubKey, KEY_READ);
569 if (hklm_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
570 *result = value != 0;
571 return true;
574 base::win::RegKey hkcu_policy_key(HKEY_CURRENT_USER,
575 policy::kRegistryMandatorySubKey, KEY_READ);
576 if (hkcu_policy_key.ReadValueDW(key_name.c_str(), &value) == ERROR_SUCCESS) {
577 *result = value != 0;
578 return true;
581 return false;
584 static DWORD __stdcall InitCrashReporterThread(void* param) {
585 scoped_ptr<CrashReporterInfo> info(
586 reinterpret_cast<CrashReporterInfo*>(param));
588 bool is_per_user_install =
589 InstallUtil::IsPerUserInstall(info->dll_path.c_str());
591 std::wstring channel_string;
592 GoogleUpdateSettings::GetChromeChannelAndModifiers(!is_per_user_install,
593 &channel_string);
595 // GetCustomInfo can take a few milliseconds to get the file information, so
596 // we do it here so it can run in a separate thread.
597 info->custom_info = GetCustomInfo(info->dll_path, info->process_type,
598 channel_string);
600 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
601 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
602 // We install the post-dump callback only for the browser and service
603 // processes. It spawns a new browser/service process.
604 if (info->process_type == L"browser") {
605 callback = &DumpDoneCallback;
606 default_filter = &ChromeExceptionFilter;
607 } else if (info->process_type == L"service") {
608 callback = &DumpDoneCallback;
609 default_filter = &ServiceExceptionFilter;
612 // Check whether configuration management controls crash reporting.
613 bool crash_reporting_enabled = true;
614 bool controlled_by_policy =
615 MetricsReportingControlledByPolicy(&crash_reporting_enabled);
617 const CommandLine& command = *CommandLine::ForCurrentProcess();
618 bool use_crash_service = !controlled_by_policy &&
619 ((command.HasSwitch(switches::kNoErrorDialogs) ||
620 GetEnvironmentVariable(
621 ASCIIToWide(env_vars::kHeadless).c_str(), NULL, 0)));
623 std::wstring pipe_name;
624 if (use_crash_service) {
625 // Crash reporting is done by crash_service.exe.
626 const wchar_t* var_name = L"CHROME_BREAKPAD_PIPE_NAME";
627 DWORD value_length = ::GetEnvironmentVariableW(var_name, NULL, 0);
628 if (value_length == 0) {
629 pipe_name = kChromePipeName;
630 } else {
631 scoped_array<wchar_t> value(new wchar_t[value_length]);
632 ::GetEnvironmentVariableW(var_name, value.get(), value_length);
633 pipe_name = value.get();
635 } else {
636 // We want to use the Google Update crash reporting. We need to check if the
637 // user allows it first (in case the administrator didn't already decide
638 // via policy).
639 if (!controlled_by_policy)
640 crash_reporting_enabled = GoogleUpdateSettings::GetCollectStatsConsent();
642 if (!crash_reporting_enabled) {
643 // Configuration managed or the user did not allow Google Update to send
644 // crashes, we need to use our default crash handler instead, but only
645 // for the browser/service processes.
646 if (default_filter)
647 InitDefaultCrashCallback(default_filter);
648 return 0;
651 // Build the pipe name. It can be either:
652 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
653 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
654 std::wstring user_sid;
655 if (is_per_user_install) {
656 if (!base::win::GetUserSidString(&user_sid)) {
657 if (default_filter)
658 InitDefaultCrashCallback(default_filter);
659 return -1;
661 } else {
662 user_sid = kSystemPrincipalSid;
665 pipe_name = kGoogleUpdatePipeName;
666 pipe_name += user_sid;
668 #ifdef _WIN64
669 pipe_name += L"-x64";
670 #endif
672 // Get the alternate dump directory. We use the temp path.
673 wchar_t temp_dir[MAX_PATH] = {0};
674 ::GetTempPathW(MAX_PATH, temp_dir);
676 MINIDUMP_TYPE dump_type = kSmallDumpType;
677 // Capture full memory if explicitly instructed to.
678 if (command.HasSwitch(switches::kFullMemoryCrashReport)) {
679 dump_type = kFullDumpType;
680 } else {
681 std::wstring channel_name(
682 GoogleUpdateSettings::GetChromeChannel(!is_per_user_install));
684 // Capture more detail in crash dumps for beta and dev channel builds.
685 if (channel_name == L"dev" || channel_name == L"beta" ||
686 channel_name == GoogleChromeSxSDistribution::ChannelName())
687 dump_type = kLargerDumpType;
690 g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback,
691 callback, NULL,
692 google_breakpad::ExceptionHandler::HANDLER_ALL,
693 dump_type, pipe_name.c_str(), info->custom_info);
695 if (!g_breakpad->IsOutOfProcess()) {
696 // The out-of-process handler is unavailable.
697 scoped_ptr<base::Environment> env(base::Environment::Create());
698 env->SetVar(env_vars::kNoOOBreakpad, WideToUTF8(info->process_type));
699 } else {
700 // Tells breakpad to handle breakpoint and single step exceptions.
701 // This might break JIT debuggers, but at least it will always
702 // generate a crashdump for these exceptions.
703 g_breakpad->set_handle_debug_exceptions(true);
706 return 0;
709 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
710 previous_filter = SetUnhandledExceptionFilter(filter);
713 void InitCrashReporterWithDllPath(const std::wstring& dll_path) {
714 const CommandLine& command = *CommandLine::ForCurrentProcess();
715 if (!command.HasSwitch(switches::kDisableBreakpad)) {
716 // Disable the message box for assertions.
717 _CrtSetReportMode(_CRT_ASSERT, 0);
719 // Query the custom_info now because if we do it in the thread it's going to
720 // fail in the sandbox. The thread will delete this object.
721 CrashReporterInfo* info(new CrashReporterInfo);
722 info->process_type = command.GetSwitchValueNative(switches::kProcessType);
723 if (info->process_type.empty())
724 info->process_type = L"browser";
726 info->dll_path = dll_path;
728 // If this is not the browser, we can't be sure that we will be able to
729 // initialize the crash_handler in another thread, so we run it right away.
730 // This is important to keep the thread for the browser process because
731 // it may take some times to initialize the crash_service process. We use
732 // the Windows worker pool to make better reuse of the thread.
733 if (info->process_type != L"browser") {
734 InitCrashReporterThread(info);
735 } else {
736 if (QueueUserWorkItem(
737 &InitCrashReporterThread,
738 info,
739 WT_EXECUTELONGFUNCTION) == 0) {
740 // We failed to queue to the worker pool, initialize in this thread.
741 InitCrashReporterThread(info);