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.
5 #include "components/crash/content/app/breakpad_win.h"
17 #include "base/base_switches.h"
18 #include "base/basictypes.h"
19 #include "base/command_line.h"
20 #include "base/debug/crash_logging.h"
21 #include "base/debug/dump_without_crashing.h"
22 #include "base/environment.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "base/numerics/safe_conversions.h"
25 #include "base/strings/string16.h"
26 #include "base/strings/string_split.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/strings/utf_string_conversions.h"
30 #include "base/synchronization/lock.h"
31 #include "base/win/metro.h"
32 #include "base/win/pe_image.h"
33 #include "base/win/registry.h"
34 #include "base/win/win_util.h"
35 #include "breakpad/src/client/windows/handler/exception_handler.h"
36 #include "components/crash/content/app/crash_keys_win.h"
37 #include "components/crash/content/app/crash_reporter_client.h"
38 #include "components/crash/content/app/hard_error_handler_win.h"
39 #include "content/public/common/result_codes.h"
40 #include "sandbox/win/src/nt_internals.h"
41 #include "sandbox/win/src/sidestep/preamble_patcher.h"
43 // userenv.dll is required for GetProfileType().
44 #pragma comment(lib, "userenv.lib")
46 #pragma intrinsic(_AddressOfReturnAddress)
47 #pragma intrinsic(_ReturnAddress)
50 // See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
51 typedef struct _UNWIND_INFO
{
52 unsigned char Version
: 3;
53 unsigned char Flags
: 5;
54 unsigned char SizeOfProlog
;
55 unsigned char CountOfCodes
;
56 unsigned char FrameRegister
: 4;
57 unsigned char FrameOffset
: 4;
58 ULONG ExceptionHandler
;
59 } UNWIND_INFO
, *PUNWIND_INFO
;
64 using crash_reporter::GetCrashReporterClient
;
68 // Minidump with stacks, PEB, TEB, and unloaded module list.
69 const MINIDUMP_TYPE kSmallDumpType
= static_cast<MINIDUMP_TYPE
>(
70 MiniDumpWithProcessThreadData
| // Get PEB and TEB.
71 MiniDumpWithUnloadedModules
); // Get unloaded modules when available.
73 // Minidump with all of the above, plus memory referenced from stack.
74 const MINIDUMP_TYPE kLargerDumpType
= static_cast<MINIDUMP_TYPE
>(
75 MiniDumpWithProcessThreadData
| // Get PEB and TEB.
76 MiniDumpWithUnloadedModules
| // Get unloaded modules when available.
77 MiniDumpWithIndirectlyReferencedMemory
); // Get memory referenced by stack.
79 // Large dump with all process memory.
80 const MINIDUMP_TYPE kFullDumpType
= static_cast<MINIDUMP_TYPE
>(
81 MiniDumpWithFullMemory
| // Full memory from process.
82 MiniDumpWithProcessThreadData
| // Get PEB and TEB.
83 MiniDumpWithHandleData
| // Get all handle information.
84 MiniDumpWithUnloadedModules
); // Get unloaded modules when available.
86 const char kPipeNameVar
[] = "CHROME_BREAKPAD_PIPE_NAME";
88 const wchar_t kGoogleUpdatePipeName
[] = L
"\\\\.\\pipe\\GoogleCrashServices\\";
89 const wchar_t kChromePipeName
[] = L
"\\\\.\\pipe\\ChromeCrashServices";
91 // This is the well known SID for the system principal.
92 const wchar_t kSystemPrincipalSid
[] =L
"S-1-5-18";
94 google_breakpad::ExceptionHandler
* g_breakpad
= NULL
;
95 google_breakpad::ExceptionHandler
* g_dumphandler_no_crash
= NULL
;
98 EXCEPTION_POINTERS g_surrogate_exception_pointers
= {0};
99 EXCEPTION_RECORD g_surrogate_exception_record
= {0};
100 CONTEXT g_surrogate_context
= {0};
101 #endif // !defined(_WIN64)
103 typedef NTSTATUS (WINAPI
* NtTerminateProcessPtr
)(HANDLE ProcessHandle
,
104 NTSTATUS ExitStatus
);
105 char* g_real_terminate_process_stub
= NULL
;
109 // Dumps the current process memory.
110 extern "C" void __declspec(dllexport
) __cdecl
DumpProcess() {
112 g_breakpad
->WriteMinidump();
116 // Used for dumping a process state when there is no crash.
117 extern "C" void __declspec(dllexport
) __cdecl
DumpProcessWithoutCrash() {
118 if (g_dumphandler_no_crash
) {
119 g_dumphandler_no_crash
->WriteMinidump();
125 // We need to prevent ICF from folding DumpForHangDebuggingThread() and
126 // DumpProcessWithoutCrashThread() together, since that makes them
127 // indistinguishable in crash dumps. We do this by making the function
128 // bodies unique, and prevent optimization from shuffling things around.
129 MSVC_DISABLE_OPTIMIZE()
130 MSVC_PUSH_DISABLE_WARNING(4748)
132 DWORD WINAPI
DumpProcessWithoutCrashThread(void*) {
133 DumpProcessWithoutCrash();
137 // The following two functions do exactly the same thing as the two above. But
138 // we want the signatures to be different so that we can easily track them in
140 // TODO(yzshen): Remove when enough information is collected and the hang rate
141 // of pepper/renderer processes is reduced.
142 DWORD WINAPI
DumpForHangDebuggingThread(void*) {
143 DumpProcessWithoutCrash();
144 VLOG(1) << "dumped for hang debugging";
149 MSVC_ENABLE_OPTIMIZE()
153 // Injects a thread into a remote process to dump state when there is no crash.
154 extern "C" HANDLE
__declspec(dllexport
) __cdecl
155 InjectDumpProcessWithoutCrash(HANDLE process
) {
156 return CreateRemoteThread(process
, NULL
, 0, DumpProcessWithoutCrashThread
,
160 extern "C" HANDLE
__declspec(dllexport
) __cdecl
161 InjectDumpForHangDebugging(HANDLE process
) {
162 return CreateRemoteThread(process
, NULL
, 0, DumpForHangDebuggingThread
,
166 // Returns a string containing a list of all modifiers for the loaded profile.
167 std::wstring
GetProfileType() {
168 std::wstring profile_type
;
169 DWORD profile_bits
= 0;
170 if (::GetProfileType(&profile_bits
)) {
171 static const struct {
175 { PT_MANDATORY
, L
"mandatory" },
176 { PT_ROAMING
, L
"roaming" },
177 { PT_TEMPORARY
, L
"temporary" },
179 for (size_t i
= 0; i
< arraysize(kBitNames
); ++i
) {
180 const DWORD this_bit
= kBitNames
[i
].bit
;
181 if ((profile_bits
& this_bit
) != 0) {
182 profile_type
.append(kBitNames
[i
].name
);
183 profile_bits
&= ~this_bit
;
184 if (profile_bits
!= 0)
185 profile_type
.append(L
", ");
189 DWORD last_error
= ::GetLastError();
190 base::SStringPrintf(&profile_type
, L
"error %u", last_error
);
197 // This callback is used when we want to get a dump without crashing the
199 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
200 EXCEPTION_POINTERS
* ex_info
,
201 MDRawAssertionInfo
*, bool succeeded
) {
202 GetCrashReporterClient()->RecordCrashDumpAttemptResult(
203 false /* is_real_crash */, succeeded
);
207 // This callback is executed when the browser process has crashed, after
208 // the crash dump has been created. We need to minimize the amount of work
209 // done here since we have potentially corrupted process. Our job is to
210 // spawn another instance of chrome which will show a 'chrome has crashed'
211 // dialog. This code needs to live in the exe and thus has no access to
212 // facilities such as the i18n helpers.
213 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
214 EXCEPTION_POINTERS
* ex_info
,
215 MDRawAssertionInfo
*, bool succeeded
) {
216 GetCrashReporterClient()->RecordCrashDumpAttemptResult(
217 true /* is_real_crash */, succeeded
);
218 // Check if the exception is one of the kind which would not be solved
219 // by simply restarting chrome. In this case we show a message box with
220 // and exit silently. Remember that chrome is in a crashed state so we
221 // can't show our own UI from this process.
222 if (HardErrorHandler(ex_info
))
225 if (!GetCrashReporterClient()->AboutToRestart())
228 // Now we just start chrome browser with the same command line.
229 STARTUPINFOW si
= {sizeof(si
)};
230 PROCESS_INFORMATION pi
;
231 if (::CreateProcessW(NULL
, ::GetCommandLineW(), NULL
, NULL
, FALSE
,
232 CREATE_UNICODE_ENVIRONMENT
, NULL
, NULL
, &si
, &pi
)) {
233 ::CloseHandle(pi
.hProcess
);
234 ::CloseHandle(pi
.hThread
);
236 // After this return we will be terminated. The actual return value is
241 // flag to indicate that we are already handling an exception.
242 volatile LONG handling_exception
= 0;
244 // This callback is used when there is no crash. Note: Unlike the
245 // |FilterCallback| below this does not do dupe detection. It is upto the caller
247 bool FilterCallbackWhenNoCrash(
248 void*, EXCEPTION_POINTERS
*, MDRawAssertionInfo
*) {
249 GetCrashReporterClient()->RecordCrashDumpAttempt(false);
253 // This callback is executed when the Chrome process has crashed and *before*
254 // the crash dump is created. To prevent duplicate crash reports we
255 // make every thread calling this method, except the very first one,
257 bool FilterCallback(void*, EXCEPTION_POINTERS
*, MDRawAssertionInfo
*) {
258 // Capture every thread except the first one in the sleep. We don't
259 // want multiple threads to concurrently report exceptions.
260 if (::InterlockedCompareExchange(&handling_exception
, 1, 0) == 1) {
263 GetCrashReporterClient()->RecordCrashDumpAttempt(true);
267 // Previous unhandled filter. Will be called if not null when we
268 // intercept a crash.
269 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter
= NULL
;
271 // Exception filter used when breakpad is not enabled. We just display
272 // the "Do you want to restart" message and then we call the previous filter.
273 long WINAPI
ChromeExceptionFilter(EXCEPTION_POINTERS
* info
) {
274 DumpDoneCallback(NULL
, NULL
, NULL
, info
, NULL
, false);
277 return previous_filter(info
);
279 return EXCEPTION_EXECUTE_HANDLER
;
282 // Exception filter for the service process used when breakpad is not enabled.
283 // We just display the "Do you want to restart" message and then die
284 // (without calling the previous filter).
285 long WINAPI
ServiceExceptionFilter(EXCEPTION_POINTERS
* info
) {
286 DumpDoneCallback(NULL
, NULL
, NULL
, info
, NULL
, false);
287 return EXCEPTION_EXECUTE_HANDLER
;
290 #if !defined(COMPONENT_BUILD)
291 // Installed via base::debug::SetCrashKeyReportingFunctions.
292 void SetCrashKeyValueForBaseDebug(const base::StringPiece
& key
,
293 const base::StringPiece
& value
) {
294 DCHECK(CrashKeysWin::keeper());
295 CrashKeysWin::keeper()->SetCrashKeyValue(base::UTF8ToUTF16(key
),
296 base::UTF8ToUTF16(value
));
299 // Installed via base::debug::SetCrashKeyReportingFunctions.
300 void ClearCrashKeyForBaseDebug(const base::StringPiece
& key
) {
301 DCHECK(CrashKeysWin::keeper());
302 CrashKeysWin::keeper()->ClearCrashKeyValue(base::UTF8ToUTF16(key
));
304 #endif // !defined(COMPONENT_BUILD)
308 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you
309 // change the name or signature of this function you will break SyzyASAN
310 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org
312 extern "C" void __declspec(dllexport
) __cdecl
SetCrashKeyValueImpl(
313 const wchar_t* key
, const wchar_t* value
) {
314 CrashKeysWin
* keeper
= CrashKeysWin::keeper();
318 // TODO(siggi): This doesn't look quite right - there's NULL deref potential
319 // here, and an implicit std::wstring conversion. Fixme.
320 keeper
->SetCrashKeyValue(key
, value
);
323 extern "C" void __declspec(dllexport
) __cdecl
ClearCrashKeyValueImpl(
324 const wchar_t* key
) {
325 CrashKeysWin
* keeper
= CrashKeysWin::keeper();
329 // TODO(siggi): This doesn't look quite right - there's NULL deref potential
330 // here, and an implicit std::wstring conversion. Fixme.
331 keeper
->ClearCrashKeyValue(key
);
334 static bool WrapMessageBoxWithSEH(const wchar_t* text
, const wchar_t* caption
,
335 UINT flags
, bool* exit_now
) {
336 // We wrap the call to MessageBoxW with a SEH handler because it some
337 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
338 // uncontrollably here. Being this a best effort deal we better go away.
340 *exit_now
= (IDOK
!= ::MessageBoxW(NULL
, text
, caption
, flags
));
341 } __except(EXCEPTION_EXECUTE_HANDLER
) {
342 // Its not safe to continue executing, exit silently here.
343 ::TerminateProcess(::GetCurrentProcess(),
344 GetCrashReporterClient()->GetResultCodeRespawnFailed());
350 // This function is executed by the child process that DumpDoneCallback()
351 // spawned and basically just shows the 'chrome has crashed' dialog if
352 // the CHROME_CRASHED environment variable is present.
353 bool ShowRestartDialogIfCrashed(bool* exit_now
) {
354 // If we are being launched in metro mode don't try to show the dialog.
355 if (base::win::IsMetroProcess())
358 base::string16 message
;
359 base::string16 title
;
361 if (!GetCrashReporterClient()->ShouldShowRestartDialog(
362 &title
, &message
, &is_rtl_locale
)) {
366 // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
367 // flags so that an RTL message box is displayed.
368 UINT flags
= MB_OKCANCEL
| MB_ICONWARNING
;
370 flags
|= MB_RIGHT
| MB_RTLREADING
;
372 return WrapMessageBoxWithSEH(message
.c_str(), title
.c_str(), flags
, exit_now
);
375 extern "C" void __declspec(dllexport
) TerminateProcessWithoutDump() {
376 // Patched stub exists based on conditions (See InitCrashReporter).
377 // As a side note this function also gets called from
378 // WindowProcExceptionFilter.
379 if (g_real_terminate_process_stub
== NULL
) {
380 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED
);
382 NtTerminateProcessPtr real_terminate_proc
=
383 reinterpret_cast<NtTerminateProcessPtr
>(
384 static_cast<char*>(g_real_terminate_process_stub
));
385 real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED
);
389 // Crashes the process after generating a dump for the provided exception. Note
390 // that the crash reporter should be initialized before calling this function
391 // for it to do anything.
392 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the
393 // the name or signature of this function you will break SyzyASAN instrumented
394 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so!
395 extern "C" int __declspec(dllexport
) CrashForException(
396 EXCEPTION_POINTERS
* info
) {
398 g_breakpad
->WriteMinidumpForException(info
);
399 TerminateProcessWithoutDump();
401 return EXCEPTION_CONTINUE_SEARCH
;
405 static NTSTATUS WINAPI
HookNtTerminateProcess(HANDLE ProcessHandle
,
406 NTSTATUS ExitStatus
) {
408 (ProcessHandle
== ::GetCurrentProcess() || ProcessHandle
== NULL
)) {
409 NT_TIB
* tib
= reinterpret_cast<NT_TIB
*>(NtCurrentTeb());
410 void* address_on_stack
= _AddressOfReturnAddress();
411 if (address_on_stack
< tib
->StackLimit
||
412 address_on_stack
> tib
->StackBase
) {
413 g_surrogate_exception_record
.ExceptionAddress
= _ReturnAddress();
414 g_surrogate_exception_record
.ExceptionCode
= DBG_TERMINATE_PROCESS
;
415 g_surrogate_exception_record
.ExceptionFlags
= EXCEPTION_NONCONTINUABLE
;
416 CrashForException(&g_surrogate_exception_pointers
);
420 NtTerminateProcessPtr real_proc
=
421 reinterpret_cast<NtTerminateProcessPtr
>(
422 static_cast<char*>(g_real_terminate_process_stub
));
423 return real_proc(ProcessHandle
, ExitStatus
);
426 static void InitTerminateProcessHooks() {
427 NtTerminateProcessPtr terminate_process_func_address
=
428 reinterpret_cast<NtTerminateProcessPtr
>(::GetProcAddress(
429 ::GetModuleHandle(L
"ntdll.dll"), "NtTerminateProcess"));
430 if (terminate_process_func_address
== NULL
)
433 DWORD old_protect
= 0;
434 if (!::VirtualProtect(terminate_process_func_address
, 5,
435 PAGE_EXECUTE_READWRITE
, &old_protect
))
438 g_real_terminate_process_stub
= reinterpret_cast<char*>(VirtualAllocEx(
439 ::GetCurrentProcess(), NULL
, sidestep::kMaxPreambleStubSize
,
440 MEM_COMMIT
, PAGE_EXECUTE_READWRITE
));
441 if (g_real_terminate_process_stub
== NULL
)
444 g_surrogate_exception_pointers
.ContextRecord
= &g_surrogate_context
;
445 g_surrogate_exception_pointers
.ExceptionRecord
=
446 &g_surrogate_exception_record
;
448 sidestep::SideStepError patch_result
=
449 sidestep::PreamblePatcher::Patch(
450 terminate_process_func_address
, HookNtTerminateProcess
,
451 g_real_terminate_process_stub
, sidestep::kMaxPreambleStubSize
);
452 if (patch_result
!= sidestep::SIDESTEP_SUCCESS
) {
453 CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub
,
455 CHECK(::VirtualProtect(terminate_process_func_address
, 5, old_protect
,
461 CHECK(::VirtualProtect(terminate_process_func_address
,
465 CHECK(::VirtualProtect(g_real_terminate_process_stub
,
466 sidestep::kMaxPreambleStubSize
,
472 static void InitPipeNameEnvVar(bool is_per_user_install
) {
473 scoped_ptr
<base::Environment
> env(base::Environment::Create());
474 if (env
->HasVar(kPipeNameVar
)) {
475 // The Breakpad pipe name is already configured: nothing to do.
479 // Check whether configuration management controls crash reporting.
480 bool crash_reporting_enabled
= true;
481 bool controlled_by_policy
=
482 GetCrashReporterClient()->ReportingIsEnforcedByPolicy(
483 &crash_reporting_enabled
);
485 const base::CommandLine
& command
= *base::CommandLine::ForCurrentProcess();
486 bool use_crash_service
= !controlled_by_policy
&&
487 (command
.HasSwitch(switches::kNoErrorDialogs
) ||
488 GetCrashReporterClient()->IsRunningUnattended());
490 std::wstring pipe_name
;
491 if (use_crash_service
) {
492 // Crash reporting is done by crash_service.exe.
493 pipe_name
= kChromePipeName
;
495 // We want to use the Google Update crash reporting. We need to check if the
496 // user allows it first (in case the administrator didn't already decide
498 if (!controlled_by_policy
)
499 crash_reporting_enabled
=
500 GetCrashReporterClient()->GetCollectStatsConsent();
502 if (!crash_reporting_enabled
) {
503 // Crash reporting is disabled, don't set the environment variable.
507 // Build the pipe name. It can be either:
508 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
509 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
510 std::wstring user_sid
;
511 if (is_per_user_install
) {
512 if (!base::win::GetUserSidString(&user_sid
)) {
516 user_sid
= kSystemPrincipalSid
;
519 pipe_name
= kGoogleUpdatePipeName
;
520 pipe_name
+= user_sid
;
522 env
->SetVar(kPipeNameVar
, base::UTF16ToASCII(pipe_name
));
525 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter
) {
526 previous_filter
= SetUnhandledExceptionFilter(filter
);
529 void InitCrashReporter(const std::string
& process_type_switch
) {
530 const base::CommandLine
& command
= *base::CommandLine::ForCurrentProcess();
531 if (command
.HasSwitch(switches::kDisableBreakpad
))
534 // Disable the message box for assertions.
535 _CrtSetReportMode(_CRT_ASSERT
, 0);
537 base::string16 process_type
= base::ASCIIToUTF16(process_type_switch
);
538 if (process_type
.empty())
539 process_type
= L
"browser";
541 wchar_t exe_path
[MAX_PATH
];
543 GetModuleFileNameW(NULL
, exe_path
, MAX_PATH
);
545 bool is_per_user_install
=
546 GetCrashReporterClient()->GetIsPerUserInstall(base::FilePath(exe_path
));
548 // This is intentionally leaked.
549 CrashKeysWin
* keeper
= new CrashKeysWin();
551 google_breakpad::CustomClientInfo
* custom_info
=
552 keeper
->GetCustomInfo(exe_path
, process_type
, GetProfileType(),
553 base::CommandLine::ForCurrentProcess(),
554 GetCrashReporterClient());
556 #if !defined(COMPONENT_BUILD)
557 // chrome/common/child_process_logging_win.cc registers crash keys for
558 // chrome.dll. In a component build, that is sufficient as chrome.dll and
559 // chrome.exe share a copy of base (in base.dll).
560 // In a static build, the EXE must separately initialize the crash keys
561 // configuration as it has its own statically linked copy of base.
562 base::debug::SetCrashKeyReportingFunctions(&SetCrashKeyValueForBaseDebug
,
563 &ClearCrashKeyForBaseDebug
);
564 GetCrashReporterClient()->RegisterCrashKeys();
567 google_breakpad::ExceptionHandler::MinidumpCallback callback
= NULL
;
568 LPTOP_LEVEL_EXCEPTION_FILTER default_filter
= NULL
;
569 // We install the post-dump callback only for the browser and service
570 // processes. It spawns a new browser/service process.
571 if (process_type
== L
"browser") {
572 callback
= &DumpDoneCallback
;
573 default_filter
= &ChromeExceptionFilter
;
574 } else if (process_type
== L
"service") {
575 callback
= &DumpDoneCallback
;
576 default_filter
= &ServiceExceptionFilter
;
579 if (process_type
== L
"browser") {
580 InitPipeNameEnvVar(is_per_user_install
);
581 GetCrashReporterClient()->InitBrowserCrashDumpsRegKey();
584 scoped_ptr
<base::Environment
> env(base::Environment::Create());
585 std::string pipe_name_ascii
;
586 if (!env
->GetVar(kPipeNameVar
, &pipe_name_ascii
)) {
587 // Breakpad is not enabled. Configuration is managed or the user
588 // did not allow Google Update to send crashes. We need to use
589 // our default crash handler instead, but only for the
590 // browser/service processes.
592 InitDefaultCrashCallback(default_filter
);
595 base::string16 pipe_name
= base::ASCIIToUTF16(pipe_name_ascii
);
598 // The protocol for connecting to the out-of-process Breakpad crash
599 // reporter is different for x86-32 and x86-64: the message sizes
600 // are different because the message struct contains a pointer. As
601 // a result, there are two different named pipes to connect to. The
602 // 64-bit one is distinguished with an "-x64" suffix.
603 pipe_name
+= L
"-x64";
606 // Get the alternate dump directory. We use the temp path.
607 wchar_t temp_dir
[MAX_PATH
] = {0};
608 ::GetTempPathW(MAX_PATH
, temp_dir
);
610 MINIDUMP_TYPE dump_type
= kSmallDumpType
;
611 // Capture full memory if explicitly instructed to.
612 if (command
.HasSwitch(switches::kFullMemoryCrashReport
))
613 dump_type
= kFullDumpType
;
614 else if (GetCrashReporterClient()->GetShouldDumpLargerDumps(
615 is_per_user_install
))
616 dump_type
= kLargerDumpType
;
618 g_breakpad
= new google_breakpad::ExceptionHandler(temp_dir
, &FilterCallback
,
620 google_breakpad::ExceptionHandler::HANDLER_ALL
,
621 dump_type
, pipe_name
.c_str(), custom_info
);
623 // Now initialize the non crash dump handler.
624 g_dumphandler_no_crash
= new google_breakpad::ExceptionHandler(temp_dir
,
625 &FilterCallbackWhenNoCrash
,
626 &DumpDoneCallbackWhenNoCrash
,
628 // Set the handler to none so this handler would not be added to
629 // |handler_stack_| in |ExceptionHandler| which is a list of exception
631 google_breakpad::ExceptionHandler::HANDLER_NONE
,
632 dump_type
, pipe_name
.c_str(), custom_info
);
634 // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other
635 // executable images linked with base should set this again for
636 // DumpWithoutCrashing to function correctly.
637 // See chrome_main.cc for example.
638 base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash
);
640 if (g_breakpad
->IsOutOfProcess()) {
641 // Tells breakpad to handle breakpoint and single step exceptions.
642 // This might break JIT debuggers, but at least it will always
643 // generate a crashdump for these exceptions.
644 g_breakpad
->set_handle_debug_exceptions(true);
647 if (process_type
!= L
"browser" &&
648 !GetCrashReporterClient()->IsRunningUnattended()) {
649 // Initialize the hook TerminateProcess to catch unexpected exits.
650 InitTerminateProcessHooks();
656 void ConsumeInvalidHandleExceptions() {
658 g_breakpad
->set_consume_invalid_handle_exceptions(true);
660 if (g_dumphandler_no_crash
) {
661 g_dumphandler_no_crash
->set_consume_invalid_handle_exceptions(true);
665 // If the user has disabled crash reporting uploads and restarted Chrome, the
666 // restarted instance will still contain the pipe environment variable, which
667 // will allow the restarted process to still upload crash reports. This function
668 // clears the environment variable, so that the restarted Chrome, which inherits
669 // its environment from the current Chrome, will no longer contain the variable.
670 extern "C" void __declspec(dllexport
) __cdecl
671 ClearBreakpadPipeEnvironmentVariable() {
672 scoped_ptr
<base::Environment
> env(base::Environment::Create());
673 env
->UnSetVar(kPipeNameVar
);
677 int CrashForExceptionInNonABICompliantCodeRange(
678 PEXCEPTION_RECORD ExceptionRecord
,
679 ULONG64 EstablisherFrame
,
680 PCONTEXT ContextRecord
,
681 PDISPATCHER_CONTEXT DispatcherContext
) {
682 EXCEPTION_POINTERS info
= { ExceptionRecord
, ContextRecord
};
683 return CrashForException(&info
);
686 struct ExceptionHandlerRecord
{
687 RUNTIME_FUNCTION runtime_function
;
688 UNWIND_INFO unwind_info
;
689 unsigned char thunk
[12];
692 extern "C" void __declspec(dllexport
) __cdecl
693 RegisterNonABICompliantCodeRange(void* start
, size_t size_in_bytes
) {
694 ExceptionHandlerRecord
* record
=
695 reinterpret_cast<ExceptionHandlerRecord
*>(start
);
697 // We assume that the first page of the code range is executable and
698 // committed and reserved for breakpad. What could possibly go wrong?
700 // All addresses are 32bit relative offsets to start.
701 record
->runtime_function
.BeginAddress
= 0;
702 record
->runtime_function
.EndAddress
=
703 base::checked_cast
<DWORD
>(size_in_bytes
);
704 record
->runtime_function
.UnwindData
=
705 offsetof(ExceptionHandlerRecord
, unwind_info
);
707 // Create unwind info that only specifies an exception handler.
708 record
->unwind_info
.Version
= 1;
709 record
->unwind_info
.Flags
= UNW_FLAG_EHANDLER
;
710 record
->unwind_info
.SizeOfProlog
= 0;
711 record
->unwind_info
.CountOfCodes
= 0;
712 record
->unwind_info
.FrameRegister
= 0;
713 record
->unwind_info
.FrameOffset
= 0;
714 record
->unwind_info
.ExceptionHandler
=
715 offsetof(ExceptionHandlerRecord
, thunk
);
719 record
->thunk
[0] = 0x48;
720 record
->thunk
[1] = 0xb8;
721 void* handler
= &CrashForExceptionInNonABICompliantCodeRange
;
722 memcpy(&record
->thunk
[2], &handler
, 8);
725 record
->thunk
[10] = 0xff;
726 record
->thunk
[11] = 0xe0;
728 // Protect reserved page against modifications.
730 CHECK(VirtualProtect(
731 start
, sizeof(ExceptionHandlerRecord
), PAGE_EXECUTE_READ
, &old_protect
));
732 CHECK(RtlAddFunctionTable(
733 &record
->runtime_function
, 1, reinterpret_cast<DWORD64
>(start
)));
736 extern "C" void __declspec(dllexport
) __cdecl
737 UnregisterNonABICompliantCodeRange(void* start
) {
738 ExceptionHandlerRecord
* record
=
739 reinterpret_cast<ExceptionHandlerRecord
*>(start
);
741 CHECK(RtlDeleteFunctionTable(&record
->runtime_function
));
745 } // namespace breakpad