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"
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"
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() {
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
)
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
)
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.
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
]))
150 base::wcslcpy((*g_custom_entries
)[g_switches_offset
+ num_added
].value
,
151 argv
[argv_i
].c_str(),
152 google_breakpad::CustomInfoEntry::kValueMaxLength
);
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
));
172 // The chunk size without terminator.
173 const size_t kChunkSize
= static_cast<size_t>(
174 google_breakpad::CustomInfoEntry::kValueMaxLength
- 1);
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
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";
210 product
= version_info
->product_short_name();
213 special_build
= version_info
->special_build();
215 // No version info found. Make up the values.
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.
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
);
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
))
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
)) {
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
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,
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) {
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);
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
) {
405 if (!g_custom_entries
)
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
,
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
)
438 if (!g_custom_entries
)
441 base::wcslcpy((*g_custom_entries
)[g_client_id_offset
].value
,
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
) {
454 DCHECK(index
< kMaxReportedActiveExtensions
);
456 if (!g_custom_entries
)
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
)
471 base::wcslcpy((*g_custom_entries
)[g_gpu_info_offset
].value
,
473 google_breakpad::CustomInfoEntry::kValueMaxLength
);
474 base::wcslcpy((*g_custom_entries
)[g_gpu_info_offset
+1].value
,
476 google_breakpad::CustomInfoEntry::kValueMaxLength
);
477 base::wcslcpy((*g_custom_entries
)[g_gpu_info_offset
+2].value
,
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
);
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.
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
);
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(),
519 DWORD len
= ::GetEnvironmentVariableW(
520 ASCIIToWide(env_vars::kRestartInfo
).c_str(), NULL
, 0);
524 wchar_t* restart_data
= new wchar_t[len
+ 1];
525 ::GetEnvironmentVariableW(ASCIIToWide(env_vars::kRestartInfo
).c_str(),
527 restart_data
[len
] = 0;
528 // The CHROME_RESTART var contains the dialog strings separated by '|'.
529 // See ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment()
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)
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(),
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
) {
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
);
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;
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;
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
,
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
,
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
;
631 scoped_array
<wchar_t> value(new wchar_t[value_length
]);
632 ::GetEnvironmentVariableW(var_name
, value
.get(), value_length
);
633 pipe_name
= value
.get();
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
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.
647 InitDefaultCrashCallback(default_filter
);
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
)) {
658 InitDefaultCrashCallback(default_filter
);
662 user_sid
= kSystemPrincipalSid
;
665 pipe_name
= kGoogleUpdatePipeName
;
666 pipe_name
+= user_sid
;
669 pipe_name
+= L
"-x64";
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
;
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
,
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
));
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);
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
);
736 if (QueueUserWorkItem(
737 &InitCrashReporterThread
,
739 WT_EXECUTELONGFUNCTION
) == 0) {
740 // We failed to queue to the worker pool, initialize in this thread.
741 InitCrashReporterThread(info
);