Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This module contains the necessary code to register the Breakpad exception | 5 // This module contains the necessary code to register the Breakpad exception |
| 6 // handler. This implementation is based on Chrome crash reporting code. See: | 6 // handler. This implementation is based on Chrome crash reporting code. |
| 7 // - src/components/breakpad/app/breakpad_win.cc | |
| 8 // - src/chrome/installer/setup/setup_main.cc | |
| 9 | 7 |
| 10 #include "remoting/base/breakpad.h" | 8 #include "chrome_elf/breakpad.h" |
| 11 | 9 |
| 12 #include <windows.h> | |
| 13 #include <string> | 10 #include <string> |
| 14 | 11 |
| 15 #include "base/atomicops.h" | 12 #include "base/atomicops.h" |
| 16 #include "base/file_version_info.h" | |
| 17 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
| 18 #include "base/logging.h" | |
| 19 #include "base/memory/scoped_ptr.h" | 14 #include "base/memory/scoped_ptr.h" |
| 20 #include "base/process/memory.h" | |
| 21 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 22 #include "base/win/wrapped_window_proc.h" | 16 #include "breakpad/src/client/windows/crash_generation/crash_generation_client.h " |
| 23 #include "breakpad/src/client/windows/handler/exception_handler.h" | 17 #include "breakpad/src/client/windows/handler/exception_handler.h" |
| 24 | 18 #include "version.h" // NOLINT |
| 25 namespace remoting { | |
| 26 void InitializeCrashReportingForTest(const wchar_t* pipe_name); | |
| 27 } // namespace remoting | |
| 28 | 19 |
| 29 namespace { | 20 namespace { |
| 30 | 21 |
| 31 const wchar_t kBreakpadProductName[] = L"Chromoting"; | 22 const wchar_t kBreakpadProductName[] = L"ChromeElf"; |
| 32 const wchar_t kBreakpadVersionEntry[] = L"ver"; | 23 const wchar_t kBreakpadVersionEntry[] = L"ver"; |
| 33 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0"; | 24 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0"; |
| 34 const wchar_t kBreakpadProdEntry[] = L"prod"; | 25 const wchar_t kBreakpadProdEntry[] = L"prod"; |
| 35 const wchar_t kBreakpadPlatformEntry[] = L"plat"; | 26 const wchar_t kBreakpadPlatformEntry[] = L"plat"; |
| 36 const wchar_t kBreakpadPlatformWin32[] = L"Win32"; | 27 const wchar_t kBreakpadPlatformWin32[] = L"Win32"; |
| 37 | 28 |
| 29 // TODO(caitkp): figure out what these should be. Current pipe works with local | |
| 30 // instance of crash_service.exe. | |
| 38 // The protocol for connecting to the out-of-process Breakpad crash | 31 // The protocol for connecting to the out-of-process Breakpad crash |
| 39 // reporter is different for x86-32 and x86-64: the message sizes | 32 // reporter is different for x86-32 and x86-64: the message sizes |
| 40 // are different because the message struct contains a pointer. As | 33 // are different because the message struct contains a pointer. As |
| 41 // a result, there are two different named pipes to connect to. The | 34 // a result, there are two different named pipes to connect to. The |
| 42 // 64-bit one is distinguished with an "-x64" suffix. | 35 // 64-bit one is distinguished with an "-x64" suffix. |
| 43 #if defined(_WIN64) | 36 #if defined(_WIN64) |
| 44 const wchar_t kGoogleUpdatePipeName[] = | 37 const wchar_t kGoogleUpdatePipeName[] = |
| 45 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64"; | 38 L"\\\\.\\pipe\\ChromeCrashServices"; |
| 39 //L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64"; | |
| 46 #else | 40 #else |
| 47 const wchar_t kGoogleUpdatePipeName[] = | 41 const wchar_t kGoogleUpdatePipeName[] = |
| 48 L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18"; | 42 L"\\\\.\\pipe\\ChromeCrashServices"; |
| 43 //L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18"; | |
| 49 #endif | 44 #endif |
| 50 | 45 |
| 51 using base::subtle::AtomicWord; | 46 using base::subtle::AtomicWord; |
| 52 using base::subtle::NoBarrier_CompareAndSwap; | 47 using base::subtle::NoBarrier_CompareAndSwap; |
| 53 | 48 |
| 54 class BreakpadWin { | 49 class BreakpadWin { |
| 55 public: | 50 public: |
| 56 BreakpadWin(); | 51 BreakpadWin(); |
| 57 ~BreakpadWin(); | 52 ~BreakpadWin(); |
| 58 | 53 |
| 59 static BreakpadWin* GetInstance(); | 54 static BreakpadWin* GetInstance(); |
| 60 | 55 |
| 56 // Generates dump on request for given |exinfo|. | |
| 57 static int OnDumpRequested(EXCEPTION_POINTERS* exinfo); | |
| 58 | |
| 61 private: | 59 private: |
| 62 // Returns the Custom information to be used for crash reporting. | 60 // Returns the Custom information to be used for crash reporting. |
| 63 google_breakpad::CustomClientInfo* GetCustomInfo(); | 61 google_breakpad::CustomClientInfo* GetCustomInfo(); |
| 64 | 62 |
| 65 // This callback is executed when the process has crashed and *before* | 63 // This callback is executed when the process has crashed and *before* |
| 66 // the crash dump is created. To prevent duplicate crash reports we | 64 // the crash dump is created. To prevent duplicate crash reports we |
| 67 // make every thread calling this method, except the very first one, | 65 // make every thread calling this method, except the very first one, |
| 68 // go to sleep. | 66 // go to sleep. |
| 69 static bool OnExceptionCallback(void* context, | 67 static bool OnExceptionCallback(void* context, |
| 70 EXCEPTION_POINTERS* exinfo, | 68 EXCEPTION_POINTERS* exinfo, |
| 71 MDRawAssertionInfo* assertion); | 69 MDRawAssertionInfo* assertion); |
| 72 | 70 |
| 73 // Crashes the process after generating a dump for the provided exception. | |
| 74 // Note that the crash reporter should be initialized before calling this | |
| 75 // function for it to do anything. | |
| 76 static int OnWindowProcedureException(EXCEPTION_POINTERS* exinfo); | |
| 77 | |
| 78 // Breakpad's exception handler. | 71 // Breakpad's exception handler. |
| 79 scoped_ptr<google_breakpad::ExceptionHandler> breakpad_; | 72 scoped_ptr<google_breakpad::ExceptionHandler> breakpad_; |
| 80 | 73 |
| 81 // This flag is used to indicate that an exception is already being handled. | 74 // This flag is used to indicate that an exception is already being handled. |
| 82 volatile AtomicWord handling_exception_; | 75 volatile AtomicWord handling_exception_; |
| 83 | 76 |
| 84 // The testing hook below allows overriding the crash server pipe name. | 77 // The testing hook below allows overriding the crash server pipe name. |
| 85 static const wchar_t* pipe_name_; | 78 static const wchar_t* pipe_name_; |
| 86 | 79 |
| 87 friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*); | |
| 88 | |
| 89 DISALLOW_COPY_AND_ASSIGN(BreakpadWin); | 80 DISALLOW_COPY_AND_ASSIGN(BreakpadWin); |
| 90 }; | 81 }; |
| 91 | 82 |
| 92 // |LazyInstance| is used to guarantee that the exception handler will be | 83 // |LazyInstance| is used to guarantee that the exception handler will be |
| 93 // initialized exactly once. | 84 // initialized exactly once. |
| 94 // N.B. LazyInstance does not allow this to be a static member of the class. | 85 // N.B. LazyInstance does not allow this to be a static member of the class. |
| 95 static base::LazyInstance<BreakpadWin>::Leaky g_instance = | 86 static base::LazyInstance<BreakpadWin>::Leaky g_instance = |
| 96 LAZY_INSTANCE_INITIALIZER; | 87 LAZY_INSTANCE_INITIALIZER; |
| 97 | 88 |
| 98 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName; | 89 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName; |
| 99 | 90 |
| 100 BreakpadWin::BreakpadWin() : handling_exception_(0) { | 91 BreakpadWin::BreakpadWin() : handling_exception_(0) { |
| 101 // Disable the message box for assertions. | 92 // Disable the message box for assertions. |
| 102 _CrtSetReportMode(_CRT_ASSERT, 0); | 93 _CrtSetReportMode(_CRT_ASSERT, 0); |
| 103 | 94 |
| 104 // Get the alternate dump directory. We use the temp path. | 95 // Get the alternate dump directory. We use the temp path. |
| 105 // N.B. We don't use base::GetTempDir() here to avoid running more code then | 96 // N.B. We don't use base::GetTempDir() here to avoid running more code then |
| 106 // necessary before crashes can be properly reported. | 97 // necessary before crashes can be properly reported. |
| 107 wchar_t temp_directory[MAX_PATH + 1] = { 0 }; | 98 wchar_t temp_directory[MAX_PATH + 1] = { 0 }; |
| 108 DWORD length = GetTempPath(MAX_PATH, temp_directory); | 99 DWORD length = GetTempPath(MAX_PATH, temp_directory); |
| 109 if (length == 0) | 100 if (length == 0) |
| 110 return; | 101 return; |
| 111 | 102 |
| 112 // Minidump with stacks, PEB, TEBs and unloaded module list. | 103 // Minidump with stacks, PEB, TEBs and unloaded module list. |
| 113 MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( | 104 MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>( |
| 114 MiniDumpWithProcessThreadData | | 105 MiniDumpWithProcessThreadData | // Get PEB and TEB. |
| 115 MiniDumpWithUnloadedModules); | 106 MiniDumpWithUnloadedModules | // Get unloaded modules when available. |
| 116 breakpad_.reset( | 107 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. |
| 117 new google_breakpad::ExceptionHandler( | 108 |
| 118 temp_directory, &OnExceptionCallback, NULL, NULL, | 109 breakpad_.reset(new google_breakpad::ExceptionHandler( |
| 119 google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type, | 110 temp_directory, |
| 120 pipe_name_, GetCustomInfo())); | 111 &OnExceptionCallback, |
| 112 NULL, | |
| 113 NULL, | |
| 114 google_breakpad::ExceptionHandler::HANDLER_ALL, | |
| 115 dump_type, | |
| 116 pipe_name_, | |
| 117 GetCustomInfo())); | |
| 121 | 118 |
| 122 if (breakpad_->IsOutOfProcess()) { | 119 if (breakpad_->IsOutOfProcess()) { |
| 123 // Tells breakpad to handle breakpoint and single step exceptions. | 120 // Tells breakpad to handle breakpoint and single step exceptions. |
| 124 breakpad_->set_handle_debug_exceptions(true); | 121 breakpad_->set_handle_debug_exceptions(true); |
| 125 } | 122 } |
| 126 | |
| 127 // Catch exceptions thrown from a window procedure. | |
| 128 base::win::WinProcExceptionFilter exception_filter = | |
| 129 base::win::SetWinProcExceptionFilter(&OnWindowProcedureException); | |
| 130 CHECK(!exception_filter); | |
| 131 } | 123 } |
| 132 | 124 |
| 133 BreakpadWin::~BreakpadWin() { | 125 BreakpadWin::~BreakpadWin() { |
| 134 // This object should be leaked so that crashes occurred during the process | 126 // This object should be leaked so that crashes occurred during the process |
| 135 // shutdown will be caught. | 127 // shutdown will be caught. |
| 136 NOTREACHED(); | 128 NOTREACHED(); |
| 137 } | 129 } |
| 138 | 130 |
| 139 // static | 131 // static |
| 140 BreakpadWin* BreakpadWin::GetInstance() { | 132 BreakpadWin* BreakpadWin::GetInstance() { |
| 141 return &g_instance.Get(); | 133 return &g_instance.Get(); |
| 142 } | 134 } |
| 143 | 135 |
| 136 // static | |
| 137 int BreakpadWin::OnDumpRequested(EXCEPTION_POINTERS* exinfo) { | |
| 138 BreakpadWin* self = BreakpadWin::GetInstance(); | |
| 139 if (self->breakpad_.get() != NULL) { | |
| 140 self->breakpad_->WriteMinidumpForException(exinfo); | |
| 141 // TODO(caitkp): Is terminating the right thing to do here? | |
|
Sigurður Ásgeirsson
2014/02/05 13:58:33
It's six of on, half a dozen of the other. If you
Cait (Slow)
2014/02/07 15:32:02
Done -- I'll grab the dump and let the loader take
| |
| 142 TerminateProcess(GetCurrentProcess(), | |
| 143 exinfo->ExceptionRecord->ExceptionCode); | |
| 144 } | |
| 145 return EXCEPTION_CONTINUE_SEARCH; | |
| 146 } | |
| 147 | |
| 144 // Returns the Custom information to be used for crash reporting. | 148 // Returns the Custom information to be used for crash reporting. |
| 145 google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() { | 149 google_breakpad::CustomClientInfo* BreakpadWin::GetCustomInfo() { |
| 146 HMODULE binary = base::GetModuleFromAddress( | |
| 147 reinterpret_cast<void*>(&remoting::InitializeCrashReporting)); | |
| 148 scoped_ptr<FileVersionInfo> version_info( | |
| 149 FileVersionInfo::CreateFileVersionInfoForModule(binary)); | |
| 150 | |
| 151 static wchar_t version[64]; | |
| 152 if (version_info.get()) { | |
| 153 wcscpy_s(version, | |
| 154 base::UTF16ToWide(version_info->product_version()).c_str()); | |
| 155 } else { | |
| 156 wcscpy_s(version, kBreakpadVersionDefault); | |
| 157 } | |
| 158 | |
| 159 static google_breakpad::CustomInfoEntry ver_entry( | 150 static google_breakpad::CustomInfoEntry ver_entry( |
| 160 kBreakpadVersionEntry, version); | 151 kBreakpadVersionEntry, TEXT(CHROME_VERSION_STRING)); |
| 161 static google_breakpad::CustomInfoEntry prod_entry( | 152 static google_breakpad::CustomInfoEntry prod_entry( |
| 162 kBreakpadProdEntry, kBreakpadProductName); | 153 kBreakpadProdEntry, kBreakpadProductName); |
| 163 static google_breakpad::CustomInfoEntry plat_entry( | 154 static google_breakpad::CustomInfoEntry plat_entry( |
| 164 kBreakpadPlatformEntry, kBreakpadPlatformWin32); | 155 kBreakpadPlatformEntry, kBreakpadPlatformWin32); |
| 165 static google_breakpad::CustomInfoEntry entries[] = { | 156 static google_breakpad::CustomInfoEntry entries[] = { |
| 166 ver_entry, prod_entry, plat_entry }; | 157 ver_entry, prod_entry, plat_entry }; |
| 167 static google_breakpad::CustomClientInfo custom_info = { | 158 static google_breakpad::CustomClientInfo custom_info = { |
| 168 entries, arraysize(entries) }; | 159 entries, arraysize(entries) }; |
| 169 return &custom_info; | 160 return &custom_info; |
| 170 } | 161 } |
| 171 | 162 |
| 172 // static | 163 // static |
| 173 bool BreakpadWin::OnExceptionCallback(void* /* context */, | 164 bool BreakpadWin::OnExceptionCallback(void* /* context */, |
| 174 EXCEPTION_POINTERS* /* exinfo */, | 165 EXCEPTION_POINTERS* /* exinfo */, |
| 175 MDRawAssertionInfo* /* assertion */) { | 166 MDRawAssertionInfo* /* assertion */) { |
| 176 BreakpadWin* self = BreakpadWin::GetInstance(); | 167 BreakpadWin* self = BreakpadWin::GetInstance(); |
| 177 if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) { | 168 if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) { |
| 178 // Capture every thread except the first one in the sleep. We don't | 169 // Capture every thread except the first one in the sleep. We don't |
| 179 // want multiple threads to concurrently report exceptions. | 170 // want multiple threads to concurrently report exceptions. |
| 180 ::Sleep(INFINITE); | 171 ::Sleep(INFINITE); |
| 181 } | 172 } |
| 182 return true; | 173 return true; |
| 183 } | 174 } |
| 184 | 175 |
| 185 // static | |
| 186 int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* exinfo) { | |
| 187 BreakpadWin* self = BreakpadWin::GetInstance(); | |
| 188 if (self->breakpad_.get() != NULL) { | |
| 189 self->breakpad_->WriteMinidumpForException(exinfo); | |
| 190 TerminateProcess(GetCurrentProcess(), | |
| 191 exinfo->ExceptionRecord->ExceptionCode); | |
| 192 } | |
| 193 return EXCEPTION_CONTINUE_SEARCH; | |
| 194 } | |
| 195 | |
| 196 } // namespace | 176 } // namespace |
| 197 | 177 |
| 198 namespace remoting { | 178 int GenerateCrashDump(EXCEPTION_POINTERS* exinfo) { |
| 179 return BreakpadWin::OnDumpRequested(exinfo); | |
|
Sigurður Ásgeirsson
2014/02/05 13:58:33
As we discussed, you may want to filter out debug
Cait (Slow)
2014/02/07 15:32:02
I tested under windbg and it seemed fine -- no har
| |
| 180 } | |
| 199 | 181 |
| 200 void InitializeCrashReporting() { | 182 void InitializeCrashReporting() { |
| 201 // Touch the object to make sure it is initialized. | 183 // Touch the object to make sure it is initialized. |
| 202 BreakpadWin::GetInstance(); | 184 BreakpadWin::GetInstance(); |
| 203 } | 185 } |
| 204 | |
| 205 void InitializeCrashReportingForTest(const wchar_t* pipe_name) { | |
| 206 BreakpadWin::pipe_name_ = pipe_name; | |
| 207 InitializeCrashReporting(); | |
| 208 } | |
| 209 | |
| 210 } // namespace remoting | |
| OLD | NEW |