breakpad_win.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 48namespace breakpad { 49 50using crash_reporter::GetCrashReporterClient; 51 52namespace { 53 54// Minidump with stacks, PEB, TEB, and unloaded module list. 55const 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. 60const 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. 66const 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 72const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME"; 73 74const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; 75const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; 76 77// This is the well known SID for the system principal. 78const wchar_t kSystemPrincipalSid[] =L"S-1-5-18"; 79 80google_breakpad::ExceptionHandler* g_breakpad = NULL; 81google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL; 82 83EXCEPTION_POINTERS g_surrogate_exception_pointers = {0}; 84EXCEPTION_RECORD g_surrogate_exception_record = {0}; 85CONTEXT g_surrogate_context = {0}; 86 87typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, 88 NTSTATUS ExitStatus); 89char* g_real_terminate_process_stub = NULL; 90 91} // namespace 92 93// Dumps the current process memory. 94extern "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. 101extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() { 102 if (g_dumphandler_no_crash) { 103 g_dumphandler_no_crash->WriteMinidump(); 104 } 105} 106 107namespace { 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. 113MSVC_DISABLE_OPTIMIZE() 114MSVC_PUSH_DISABLE_WARNING(4748) 115 116DWORD 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. 126DWORD WINAPI DumpForHangDebuggingThread(void*) { 127 DumpProcessWithoutCrash(); 128 VLOG(1) << "dumped for hang debugging"; 129 return 0; 130} 131 132MSVC_POP_WARNING() 133MSVC_ENABLE_OPTIMIZE() 134 135} // namespace 136 137// Injects a thread into a remote process to dump state when there is no crash. 138extern "C" HANDLE __declspec(dllexport) __cdecl 139InjectDumpProcessWithoutCrash(HANDLE process) { 140 return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread, 141 0, 0, NULL); 142} 143 144extern "C" HANDLE __declspec(dllexport) __cdecl 145InjectDumpForHangDebugging(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. 151std::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 179namespace { 180 181// This callback is used when we want to get a dump without crashing the 182// process. 183bool 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. 195bool 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. 222volatile 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. 227bool 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. 237bool 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. 249LPTOP_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. 253long 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). 265long 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@chromium.org 275// before doing so! 276extern "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 287extern "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 298static 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. 317bool 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@chromium.org before doing so! 345extern "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 364static 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 385static 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 430static 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 483void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) { 484 previous_filter = SetUnhandledExceptionFilter(filter); 485} 486 487void 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. 608extern "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