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 "components/crash/app/breakpad_win.h" 6 7 #include <windows.h> 8 #include <shellapi.h> 9 #include <tchar.h> 10 #include <userenv.h> 11 #include <winnt.h> 12 13 #include <algorithm> 14 #include <map> 15 #include <vector> 16 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/strings/string16.h" 25 #include "base/strings/string_split.h" 26 #include "base/strings/string_util.h" 27 #include "base/strings/stringprintf.h" 28 #include "base/strings/utf_string_conversions.h" 29 #include "base/synchronization/lock.h" 30 #include "base/win/metro.h" 31 #include "base/win/pe_image.h" 32 #include "base/win/registry.h" 33 #include "base/win/win_util.h" 34 #include "breakpad/src/client/windows/handler/exception_handler.h" 35 #include "components/crash/app/crash_keys_win.h" 36 #include "components/crash/app/crash_reporter_client.h" 37 #include "components/crash/app/hard_error_handler_win.h" 38 #include "content/public/common/result_codes.h" 39 #include "sandbox/win/src/nt_internals.h" 40 #include "sandbox/win/src/sidestep/preamble_patcher.h" 41 42 // userenv.dll is required for GetProfileType(). 43 #pragma comment(lib, "userenv.lib") 44 45 #pragma intrinsic(_AddressOfReturnAddress) 46 #pragma intrinsic(_ReturnAddress) 47 48 namespace breakpad { 49 50 using crash_reporter::GetCrashReporterClient; 51 52 namespace { 53 54 // Minidump with stacks, PEB, TEB, and unloaded module list. 55 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>( 56 MiniDumpWithProcessThreadData | // Get PEB and TEB. 57 MiniDumpWithUnloadedModules); // Get unloaded modules when available. 58 59 // Minidump with all of the above, plus memory referenced from stack. 60 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>( 61 MiniDumpWithProcessThreadData | // Get PEB and TEB. 62 MiniDumpWithUnloadedModules | // Get unloaded modules when available. 63 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. 64 65 // Large dump with all process memory. 66 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>( 67 MiniDumpWithFullMemory | // Full memory from process. 68 MiniDumpWithProcessThreadData | // Get PEB and TEB. 69 MiniDumpWithHandleData | // Get all handle information. 70 MiniDumpWithUnloadedModules); // Get unloaded modules when available. 71 72 const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME"; 73 74 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; 75 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; 76 77 // This is the well known SID for the system principal. 78 const wchar_t kSystemPrincipalSid[] =L"S-1-5-18"; 79 80 google_breakpad::ExceptionHandler* g_breakpad = NULL; 81 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; 82 83 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; 84 EXCEPTION_RECORD g_surrogate_exception_record = {0}; 85 CONTEXT g_surrogate_context = {0}; 86 87 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, 88 NTSTATUS ExitStatus); 89 char* g_real_terminate_process_stub = NULL; 90 91 } // namespace 92 93 // Dumps the current process memory. 94 extern "C" void __declspec(dllexport) __cdecl DumpProcess() { 95 if (g_breakpad) { 96 g_breakpad->WriteMinidump(); 97 } 98 } 99 100 // Used for dumping a process state when there is no crash. 101 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { 102 if (g_dumphandler_no_crash) { 103 g_dumphandler_no_crash->WriteMinidump(); 104 } 105 } 106 107 namespace { 108 109 // We need to prevent ICF from folding DumpForHangDebuggingThread() and 110 // DumpProcessWithoutCrashThread() together, since that makes them 111 // indistinguishable in crash dumps. We do this by making the function 112 // bodies unique, and prevent optimization from shuffling things around. 113 MSVC_DISABLE_OPTIMIZE() 114 MSVC_PUSH_DISABLE_WARNING(4748) 115 116 DWORD WINAPI DumpProcessWithoutCrashThread(void*) { 117 DumpProcessWithoutCrash(); 118 return 0; 119 } 120 121 // The following two functions do exactly the same thing as the two above. But 122 // we want the signatures to be different so that we can easily track them in 123 // crash reports. 124 // TODO(yzshen): Remove when enough information is collected and the hang rate 125 // of pepper/renderer processes is reduced. 126 DWORD WINAPI DumpForHangDebuggingThread(void*) { 127 DumpProcessWithoutCrash(); 128 VLOG(1) << "dumped for hang debugging"; 129 return 0; 130 } 131 132 MSVC_POP_WARNING() 133 MSVC_ENABLE_OPTIMIZE() 134 135 } // namespace 136 137 // Injects a thread into a remote process to dump state when there is no crash. 138 extern "C" HANDLE __declspec(dllexport) __cdecl 139 InjectDumpProcessWithoutCrash(HANDLE process) { 140 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, 141 0, 0, NULL); 142 } 143 144 extern "C" HANDLE __declspec(dllexport) __cdecl 145 InjectDumpForHangDebugging(HANDLE process) { 146 return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread, 147 0, 0, NULL); 148 } 149 150 // Returns a string containing a list of all modifiers for the loaded profile. 151 std::wstring GetProfileType() { 152 std::wstring profile_type; 153 DWORD profile_bits = 0; 154 if (::GetProfileType(&profile_bits)) { 155 static const struct { 156 DWORD bit; 157 const wchar_t* name; 158 } kBitNames[] = { 159 { PT_MANDATORY, L"mandatory" }, 160 { PT_ROAMING, L"roaming" }, 161 { PT_TEMPORARY, L"temporary" }, 162 }; 163 for (size_t i = 0; i < arraysize(kBitNames); ++i) { 164 const DWORD this_bit = kBitNames[i].bit; 165 if ((profile_bits & this_bit) != 0) { 166 profile_type.append(kBitNames[i].name); 167 profile_bits &= ~this_bit; 168 if (profile_bits != 0) 169 profile_type.append(L", "); 170 } 171 } 172 } else { 173 DWORD last_error = ::GetLastError(); 174 base::SStringPrintf(&profile_type, L"error %u", last_error); 175 } 176 return profile_type; 177 } 178 179 namespace { 180 181 // This callback is used when we want to get a dump without crashing the 182 // process. 183 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*, 184 EXCEPTION_POINTERS* ex_info, 185 MDRawAssertionInfo*, bool) { 186 return true; 187 } 188 189 // This callback is executed when the browser process has crashed, after 190 // the crash dump has been created. We need to minimize the amount of work 191 // done here since we have potentially corrupted process. Our job is to 192 // spawn another instance of chrome which will show a 'chrome has crashed' 193 // dialog. This code needs to live in the exe and thus has no access to 194 // facilities such as the i18n helpers. 195 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*, 196 EXCEPTION_POINTERS* ex_info, 197 MDRawAssertionInfo*, bool) { 198 // Check if the exception is one of the kind which would not be solved 199 // by simply restarting chrome. In this case we show a message box with 200 // and exit silently. Remember that chrome is in a crashed state so we 201 // can't show our own UI from this process. 202 if (HardErrorHandler(ex_info)) 203 return true; 204 205 if (!GetCrashReporterClient()->AboutToRestart()) 206 return true; 207 208 // Now we just start chrome browser with the same command line. 209 STARTUPINFOW si = {sizeof(si)}; 210 PROCESS_INFORMATION pi; 211 if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE, 212 CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) { 213 ::CloseHandle(pi.hProcess); 214 ::CloseHandle(pi.hThread); 215 } 216 // After this return we will be terminated. The actual return value is 217 // not used at all. 218 return true; 219 } 220 221 // flag to indicate that we are already handling an exception. 222 volatile LONG handling_exception = 0; 223 224 // This callback is used when there is no crash. Note: Unlike the 225 // |FilterCallback| below this does not do dupe detection. It is upto the caller 226 // to implement it. 227 bool FilterCallbackWhenNoCrash( 228 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { 229 GetCrashReporterClient()->RecordCrashDumpAttempt(false); 230 return true; 231 } 232 233 // This callback is executed when the Chrome process has crashed and *before* 234 // the crash dump is created. To prevent duplicate crash reports we 235 // make every thread calling this method, except the very first one, 236 // go to sleep. 237 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) { 238 // Capture every thread except the first one in the sleep. We don't 239 // want multiple threads to concurrently report exceptions. 240 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) { 241 ::Sleep(INFINITE); 242 } 243 GetCrashReporterClient()->RecordCrashDumpAttempt(true); 244 return true; 245 } 246 247 // Previous unhandled filter. Will be called if not null when we 248 // intercept a crash. 249 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL; 250 251 // Exception filter used when breakpad is not enabled. We just display 252 // the "Do you want to restart" message and then we call the previous filter. 253 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) { 254 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); 255 256 if (previous_filter) 257 return previous_filter(info); 258 259 return EXCEPTION_EXECUTE_HANDLER; 260 } 261 262 // Exception filter for the service process used when breakpad is not enabled. 263 // We just display the "Do you want to restart" message and then die 264 // (without calling the previous filter). 265 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { 266 DumpDoneCallback(NULL, NULL, NULL, info, NULL, false); 267 return EXCEPTION_EXECUTE_HANDLER; 268 } 269 270 } // namespace 271 272 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you 273 // change the name or signature of this function you will break SyzyASAN 274 // instrumented releases of Chrome. Please contact syzygy-team (at) chromium.org 275 // before doing so! 276 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( 277 const wchar_t* key, const wchar_t* value) { 278 CrashKeysWin* keeper = CrashKeysWin::keeper(); 279 if (!keeper) 280 return; 281 282 // TODO(siggi): This doesn't look quite right - there's NULL deref potential 283 // here, and an implicit std::wstring conversion. Fixme. 284 keeper->SetCrashKeyValue(key, value); 285 } 286 287 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( 288 const wchar_t* key) { 289 CrashKeysWin* keeper = CrashKeysWin::keeper(); 290 if (!keeper) 291 return; 292 293 // TODO(siggi): This doesn't look quite right - there's NULL deref potential 294 // here, and an implicit std::wstring conversion. Fixme. 295 keeper->ClearCrashKeyValue(key); 296 } 297 298 static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption, 299 UINT flags, bool* exit_now) { 300 // We wrap the call to MessageBoxW with a SEH handler because it some 301 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes 302 // uncontrollably here. Being this a best effort deal we better go away. 303 __try { 304 *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags)); 305 } __except(EXCEPTION_EXECUTE_HANDLER) { 306 // Its not safe to continue executing, exit silently here. 307 ::TerminateProcess(::GetCurrentProcess(), 308 GetCrashReporterClient()->GetResultCodeRespawnFailed()); 309 } 310 311 return true; 312 } 313 314 // This function is executed by the child process that DumpDoneCallback() 315 // spawned and basically just shows the 'chrome has crashed' dialog if 316 // the CHROME_CRASHED environment variable is present. 317 bool ShowRestartDialogIfCrashed(bool* exit_now) { 318 // If we are being launched in metro mode don't try to show the dialog. 319 if (base::win::IsMetroProcess()) 320 return false; 321 322 base::string16 message; 323 base::string16 title; 324 bool is_rtl_locale; 325 if (!GetCrashReporterClient()->ShouldShowRestartDialog( 326 &title, &message, &is_rtl_locale)) { 327 return false; 328 } 329 330 // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX 331 // flags so that an RTL message box is displayed. 332 UINT flags = MB_OKCANCEL | MB_ICONWARNING; 333 if (is_rtl_locale) 334 flags |= MB_RIGHT | MB_RTLREADING; 335 336 return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now); 337 } 338 339 // Crashes the process after generating a dump for the provided exception. Note 340 // that the crash reporter should be initialized before calling this function 341 // for it to do anything. 342 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the 343 // the name or signature of this function you will break SyzyASAN instrumented 344 // releases of Chrome. Please contact syzygy-team (at) chromium.org before doing so! 345 extern "C" int __declspec(dllexport) CrashForException( 346 EXCEPTION_POINTERS* info) { 347 if (g_breakpad) { 348 g_breakpad->WriteMinidumpForException(info); 349 // Patched stub exists based on conditions (See InitCrashReporter). 350 // As a side note this function also gets called from 351 // WindowProcExceptionFilter. 352 if (g_real_terminate_process_stub == NULL) { 353 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED); 354 } else { 355 NtTerminateProcessPtr real_terminate_proc = 356 reinterpret_cast<NtTerminateProcessPtr>( 357 static_cast<char*>(g_real_terminate_process_stub)); 358 real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED); 359 } 360 } 361 return EXCEPTION_CONTINUE_SEARCH; 362 } 363 364 static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle, 365 NTSTATUS ExitStatus) { 366 if (g_breakpad && 367 (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) { 368 NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb()); 369 void* address_on_stack = _AddressOfReturnAddress(); 370 if (address_on_stack < tib->StackLimit || 371 address_on_stack > tib->StackBase) { 372 g_surrogate_exception_record.ExceptionAddress = _ReturnAddress(); 373 g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS; 374 g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; 375 CrashForException(&g_surrogate_exception_pointers); 376 } 377 } 378 379 NtTerminateProcessPtr real_proc = 380 reinterpret_cast<NtTerminateProcessPtr>( 381 static_cast<char*>(g_real_terminate_process_stub)); 382 return real_proc(ProcessHandle, ExitStatus); 383 } 384 385 static void InitTerminateProcessHooks() { 386 NtTerminateProcessPtr terminate_process_func_address = 387 reinterpret_cast<NtTerminateProcessPtr>(::GetProcAddress( 388 ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess")); 389 if (terminate_process_func_address == NULL) 390 return; 391 392 DWORD old_protect = 0; 393 if (!::VirtualProtect(terminate_process_func_address, 5, 394 PAGE_EXECUTE_READWRITE, &old_protect)) 395 return; 396 397 g_real_terminate_process_stub = reinterpret_cast<char*>(VirtualAllocEx( 398 ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize, 399 MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 400 if (g_real_terminate_process_stub == NULL) 401 return; 402 403 g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context; 404 g_surrogate_exception_pointers.ExceptionRecord = 405 &g_surrogate_exception_record; 406 407 sidestep::SideStepError patch_result = 408 sidestep::PreamblePatcher::Patch( 409 terminate_process_func_address, HookNtTerminateProcess, 410 g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize); 411 if (patch_result != sidestep::SIDESTEP_SUCCESS) { 412 CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub, 413 0, MEM_RELEASE)); 414 CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect, 415 &old_protect)); 416 return; 417 } 418 419 DWORD dummy = 0; 420 CHECK(::VirtualProtect(terminate_process_func_address, 421 5, 422 old_protect, 423 &dummy)); 424 CHECK(::VirtualProtect(g_real_terminate_process_stub, 425 sidestep::kMaxPreambleStubSize, 426 old_protect, 427 &old_protect)); 428 } 429 430 static void InitPipeNameEnvVar(bool is_per_user_install) { 431 scoped_ptr<base::Environment> env(base::Environment::Create()); 432 if (env->HasVar(kPipeNameVar)) { 433 // The Breakpad pipe name is already configured: nothing to do. 434 return; 435 } 436 437 // Check whether configuration management controls crash reporting. 438 bool crash_reporting_enabled = true; 439 bool controlled_by_policy = 440 GetCrashReporterClient()->ReportingIsEnforcedByPolicy( 441 &crash_reporting_enabled); 442 443 const CommandLine& command = *CommandLine::ForCurrentProcess(); 444 bool use_crash_service = !controlled_by_policy && 445 (command.HasSwitch(switches::kNoErrorDialogs) || 446 GetCrashReporterClient()->IsRunningUnattended()); 447 448 std::wstring pipe_name; 449 if (use_crash_service) { 450 // Crash reporting is done by crash_service.exe. 451 pipe_name = kChromePipeName; 452 } else { 453 // We want to use the Google Update crash reporting. We need to check if the 454 // user allows it first (in case the administrator didn't already decide 455 // via policy). 456 if (!controlled_by_policy) 457 crash_reporting_enabled = 458 GetCrashReporterClient()->GetCollectStatsConsent(); 459 460 if (!crash_reporting_enabled) { 461 // Crash reporting is disabled, don't set the environment variable. 462 return; 463 } 464 465 // Build the pipe name. It can be either: 466 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" 467 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" 468 std::wstring user_sid; 469 if (is_per_user_install) { 470 if (!base::win::GetUserSidString(&user_sid)) { 471 return; 472 } 473 } else { 474 user_sid = kSystemPrincipalSid; 475 } 476 477 pipe_name = kGoogleUpdatePipeName; 478 pipe_name += user_sid; 479 } 480 env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name)); 481 } 482 483 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { 484 previous_filter = SetUnhandledExceptionFilter(filter); 485 } 486 487 void InitCrashReporter(const std::string& process_type_switch) { 488 const CommandLine& command = *CommandLine::ForCurrentProcess(); 489 if (command.HasSwitch(switches::kDisableBreakpad)) 490 return; 491 492 // Disable the message box for assertions. 493 _CrtSetReportMode(_CRT_ASSERT, 0); 494 495 std::wstring process_type = base::ASCIIToWide(process_type_switch); 496 if (process_type.empty()) 497 process_type = L"browser"; 498 499 wchar_t exe_path[MAX_PATH]; 500 exe_path[0] = 0; 501 GetModuleFileNameW(NULL, exe_path, MAX_PATH); 502 503 bool is_per_user_install = 504 GetCrashReporterClient()->GetIsPerUserInstall(base::FilePath(exe_path)); 505 506 // This is intentionally leaked. 507 CrashKeysWin* keeper = new CrashKeysWin(); 508 509 google_breakpad::CustomClientInfo* custom_info = 510 keeper->GetCustomInfo(exe_path, process_type, 511 GetProfileType(), CommandLine::ForCurrentProcess(), 512 GetCrashReporterClient()); 513 514 google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; 515 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; 516 // We install the post-dump callback only for the browser and service 517 // processes. It spawns a new browser/service process. 518 if (process_type == L"browser") { 519 callback = &DumpDoneCallback; 520 default_filter = &ChromeExceptionFilter; 521 } else if (process_type == L"service") { 522 callback = &DumpDoneCallback; 523 default_filter = &ServiceExceptionFilter; 524 } 525 526 if (process_type == L"browser") { 527 InitPipeNameEnvVar(is_per_user_install); 528 GetCrashReporterClient()->InitBrowserCrashDumpsRegKey(); 529 } 530 531 scoped_ptr<base::Environment> env(base::Environment::Create()); 532 std::string pipe_name_ascii; 533 if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) { 534 // Breakpad is not enabled. Configuration is managed or the user 535 // did not allow Google Update to send crashes. We need to use 536 // our default crash handler instead, but only for the 537 // browser/service processes. 538 if (default_filter) 539 InitDefaultCrashCallback(default_filter); 540 return; 541 } 542 std::wstring pipe_name = base::ASCIIToWide(pipe_name_ascii); 543 544 #ifdef _WIN64 545 // The protocol for connecting to the out-of-process Breakpad crash 546 // reporter is different for x86-32 and x86-64: the message sizes 547 // are different because the message struct contains a pointer. As 548 // a result, there are two different named pipes to connect to. The 549 // 64-bit one is distinguished with an "-x64" suffix. 550 pipe_name += L"-x64"; 551 #endif 552 553 // Get the alternate dump directory. We use the temp path. 554 wchar_t temp_dir[MAX_PATH] = {0}; 555 ::GetTempPathW(MAX_PATH, temp_dir); 556 557 MINIDUMP_TYPE dump_type = kSmallDumpType; 558 // Capture full memory if explicitly instructed to. 559 if (command.HasSwitch(switches::kFullMemoryCrashReport)) 560 dump_type = kFullDumpType; 561 else if (GetCrashReporterClient()->GetShouldDumpLargerDumps( 562 is_per_user_install)) 563 dump_type = kLargerDumpType; 564 565 g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback, 566 callback, NULL, 567 google_breakpad::ExceptionHandler::HANDLER_ALL, 568 dump_type, pipe_name.c_str(), custom_info); 569 570 // Now initialize the non crash dump handler. 571 g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir, 572 &FilterCallbackWhenNoCrash, 573 &DumpDoneCallbackWhenNoCrash, 574 NULL, 575 // Set the handler to none so this handler would not be added to 576 // |handler_stack_| in |ExceptionHandler| which is a list of exception 577 // handlers. 578 google_breakpad::ExceptionHandler::HANDLER_NONE, 579 dump_type, pipe_name.c_str(), custom_info); 580 581 // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other 582 // executable images linked with base should set this again for 583 // DumpWithoutCrashing to function correctly. 584 // See chrome_main.cc for example. 585 base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash); 586 587 if (g_breakpad->IsOutOfProcess()) { 588 // Tells breakpad to handle breakpoint and single step exceptions. 589 // This might break JIT debuggers, but at least it will always 590 // generate a crashdump for these exceptions. 591 g_breakpad->set_handle_debug_exceptions(true); 592 593 #ifndef _WIN64 594 if (process_type != L"browser" && 595 !GetCrashReporterClient()->IsRunningUnattended()) { 596 // Initialize the hook TerminateProcess to catch unexpected exits. 597 InitTerminateProcessHooks(); 598 } 599 #endif 600 } 601 } 602 603 // If the user has disabled crash reporting uploads and restarted Chrome, the 604 // restarted instance will still contain the pipe environment variable, which 605 // will allow the restarted process to still upload crash reports. This function 606 // clears the environment variable, so that the restarted Chrome, which inherits 607 // its environment from the current Chrome, will no longer contain the variable. 608 extern "C" void __declspec(dllexport) __cdecl 609 ClearBreakpadPipeEnvironmentVariable() { 610 scoped_ptr<base::Environment> env(base::Environment::Create()); 611 env->UnSetVar(kPipeNameVar); 612 } 613 614 } // namespace breakpad 615