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