breakpad_win.cc 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright (c) 2012 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. // This module contains the necessary code to register the Breakpad exception
  5. // handler. This implementation is based on Chrome crash reporting code. See:
  6. // - src/components/crash/core/app/breakpad_win.cc
  7. // - src/chrome/installer/setup/setup_main.cc
  8. #include "remoting/base/breakpad.h"
  9. #include <crtdbg.h>
  10. #include <windows.h>
  11. #include <memory>
  12. #include <string>
  13. #include "base/atomicops.h"
  14. #include "base/check.h"
  15. #include "base/file_version_info.h"
  16. #include "base/lazy_instance.h"
  17. #include "base/notreached.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/strings/utf_string_conversions.h"
  20. #include "base/win/current_module.h"
  21. #include "base/win/wrapped_window_proc.h"
  22. #include "third_party/breakpad/breakpad/src/client/windows/handler/exception_handler.h"
  23. namespace remoting {
  24. void InitializeCrashReportingForTest(const wchar_t* pipe_name);
  25. } // namespace remoting
  26. namespace {
  27. const wchar_t kBreakpadProductName[] = L"Chromoting";
  28. const wchar_t kBreakpadVersionEntry[] = L"ver";
  29. const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0";
  30. const wchar_t kBreakpadProdEntry[] = L"prod";
  31. const wchar_t kBreakpadPlatformEntry[] = L"plat";
  32. const wchar_t kBreakpadPlatformWin32[] = L"Win32";
  33. // The protocol for connecting to the out-of-process Breakpad crash
  34. // reporter is different for x86-32 and x86-64: the message sizes
  35. // are different because the message struct contains a pointer. As
  36. // a result, there are two different named pipes to connect to. The
  37. // 64-bit one is distinguished with an "-x64" suffix.
  38. #if defined(_WIN64)
  39. const wchar_t kGoogleUpdatePipeName[] =
  40. L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
  41. #else
  42. const wchar_t kGoogleUpdatePipeName[] =
  43. L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
  44. #endif
  45. using base::subtle::AtomicWord;
  46. using base::subtle::NoBarrier_CompareAndSwap;
  47. class BreakpadWin {
  48. public:
  49. BreakpadWin();
  50. BreakpadWin(const BreakpadWin&) = delete;
  51. BreakpadWin& operator=(const BreakpadWin&) = delete;
  52. ~BreakpadWin();
  53. static BreakpadWin* GetInstance();
  54. private:
  55. // Returns the Custom information to be used for crash reporting.
  56. google_breakpad::CustomClientInfo* GetCustomInfo();
  57. // This callback is executed when the process has crashed and *before*
  58. // the crash dump is created. To prevent duplicate crash reports we
  59. // make every thread calling this method, except the very first one,
  60. // go to sleep.
  61. static bool OnExceptionCallback(void* context,
  62. EXCEPTION_POINTERS* exinfo,
  63. MDRawAssertionInfo* assertion);
  64. // Crashes the process after generating a dump for the provided exception.
  65. // Note that the crash reporter should be initialized before calling this
  66. // function for it to do anything.
  67. static int OnWindowProcedureException(EXCEPTION_POINTERS* exinfo);
  68. // Breakpad's exception handler.
  69. std::unique_ptr<google_breakpad::ExceptionHandler> breakpad_;
  70. // This flag is used to indicate that an exception is already being handled.
  71. volatile AtomicWord handling_exception_;
  72. // The testing hook below allows overriding the crash server pipe name.
  73. static const wchar_t* pipe_name_;
  74. friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*);
  75. };
  76. // |LazyInstance| is used to guarantee that the exception handler will be
  77. // initialized exactly once.
  78. // N.B. LazyInstance does not allow this to be a static member of the class.
  79. static base::LazyInstance<BreakpadWin>::Leaky g_instance =
  80. LAZY_INSTANCE_INITIALIZER;
  81. const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName;
  82. BreakpadWin::BreakpadWin() : handling_exception_(0) {
  83. // Disable the message box for assertions.
  84. _CrtSetReportMode(_CRT_ASSERT, 0);
  85. // Get the alternate dump directory. We use the temp path.
  86. // N.B. We don't use base::GetTempDir() here to avoid running more code then
  87. // necessary before crashes can be properly reported.
  88. wchar_t temp_directory[MAX_PATH + 1] = { 0 };
  89. DWORD length = GetTempPath(MAX_PATH, temp_directory);
  90. if (length == 0)
  91. return;
  92. // Minidump with stacks, PEB, TEBs and unloaded module list.
  93. MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
  94. MiniDumpWithProcessThreadData |
  95. MiniDumpWithUnloadedModules);
  96. breakpad_.reset(
  97. new google_breakpad::ExceptionHandler(
  98. temp_directory, &OnExceptionCallback, NULL, NULL,
  99. google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
  100. pipe_name_, GetCustomInfo()));
  101. if (breakpad_->IsOutOfProcess()) {
  102. // Tells breakpad to handle breakpoint and single step exceptions.
  103. breakpad_->set_handle_debug_exceptions(true);
  104. }
  105. // Catch exceptions thrown from a window procedure.
  106. base::win::WinProcExceptionFilter exception_filter =
  107. base::win::SetWinProcExceptionFilter(&OnWindowProcedureException);
  108. CHECK(!exception_filter);
  109. }
  110. BreakpadWin::~BreakpadWin() {
  111. // This object should be leaked so that crashes which occur during process
  112. // shutdown will be caught.
  113. NOTREACHED();
  114. }
  115. // static
  116. BreakpadWin* BreakpadWin::GetInstance() {
  117. return &g_instance.Get();
  118. }
  119. // Returns the Custom information to be used for crash reporting.
  120. google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() {
  121. std::unique_ptr<FileVersionInfo> version_info(
  122. FileVersionInfo::CreateFileVersionInfoForModule(CURRENT_MODULE()));
  123. static wchar_t version[64];
  124. if (version_info.get()) {
  125. wcscpy_s(version, base::as_wcstr(version_info->product_version()));
  126. } else {
  127. wcscpy_s(version, kBreakpadVersionDefault);
  128. }
  129. static google_breakpad::CustomInfoEntry ver_entry(
  130. kBreakpadVersionEntry, version);
  131. static google_breakpad::CustomInfoEntry prod_entry(
  132. kBreakpadProdEntry, kBreakpadProductName);
  133. static google_breakpad::CustomInfoEntry plat_entry(
  134. kBreakpadPlatformEntry, kBreakpadPlatformWin32);
  135. static google_breakpad::CustomInfoEntry entries[] = {
  136. ver_entry, prod_entry, plat_entry };
  137. static google_breakpad::CustomClientInfo custom_info = {entries,
  138. std::size(entries)};
  139. return &custom_info;
  140. }
  141. // static
  142. bool BreakpadWin::OnExceptionCallback(void* /* context */,
  143. EXCEPTION_POINTERS* /* exinfo */,
  144. MDRawAssertionInfo* /* assertion */) {
  145. BreakpadWin* self = BreakpadWin::GetInstance();
  146. if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) {
  147. // Capture every thread except the first one in the sleep. We don't
  148. // want multiple threads to concurrently report exceptions.
  149. ::Sleep(INFINITE);
  150. }
  151. return true;
  152. }
  153. // static
  154. int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* exinfo) {
  155. BreakpadWin* self = BreakpadWin::GetInstance();
  156. if (self->breakpad_.get() != NULL) {
  157. self->breakpad_->WriteMinidumpForException(exinfo);
  158. TerminateProcess(GetCurrentProcess(),
  159. exinfo->ExceptionRecord->ExceptionCode);
  160. }
  161. return EXCEPTION_CONTINUE_SEARCH;
  162. }
  163. } // namespace
  164. namespace remoting {
  165. void InitializeCrashReporting() {
  166. // Touch the object to make sure it is initialized.
  167. BreakpadWin::GetInstance();
  168. }
  169. void InitializeCrashReportingForTest(const wchar_t* pipe_name) {
  170. BreakpadWin::pipe_name_ = pipe_name;
  171. InitializeCrashReporting();
  172. }
  173. } // namespace remoting