Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / remoting / base / breakpad_win.cc
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
5 // This module contains the necessary code to register the Breakpad exception
6 // handler. This implementation is based on Chrome crash reporting code. See:
7 //   - src/components/crash/app/breakpad_win.cc
8 //   - src/chrome/installer/setup/setup_main.cc
9
10 #include "remoting/base/breakpad.h"
11
12 #include <windows.h>
13 #include <string>
14
15 #include "base/atomicops.h"
16 #include "base/file_version_info.h"
17 #include "base/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/process/memory.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/win/wrapped_window_proc.h"
23 #include "breakpad/src/client/windows/handler/exception_handler.h"
24
25 namespace remoting {
26 void InitializeCrashReportingForTest(const wchar_t* pipe_name);
27 }  // namespace remoting
28
29 namespace {
30
31 const wchar_t kBreakpadProductName[] = L"Chromoting";
32 const wchar_t kBreakpadVersionEntry[] = L"ver";
33 const wchar_t kBreakpadVersionDefault[] = L"0.1.0.0";
34 const wchar_t kBreakpadProdEntry[] = L"prod";
35 const wchar_t kBreakpadPlatformEntry[] = L"plat";
36 const wchar_t kBreakpadPlatformWin32[] = L"Win32";
37
38 // The protocol for connecting to the out-of-process Breakpad crash
39 // reporter is different for x86-32 and x86-64: the message sizes
40 // are different because the message struct contains a pointer.  As
41 // a result, there are two different named pipes to connect to.  The
42 // 64-bit one is distinguished with an "-x64" suffix.
43 #if defined(_WIN64)
44 const wchar_t kGoogleUpdatePipeName[] =
45     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18-x64";
46 #else
47 const wchar_t kGoogleUpdatePipeName[] =
48     L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-18";
49 #endif
50
51 using base::subtle::AtomicWord;
52 using base::subtle::NoBarrier_CompareAndSwap;
53
54 class BreakpadWin {
55  public:
56   BreakpadWin();
57   ~BreakpadWin();
58
59   static BreakpadWin* GetInstance();
60
61  private:
62   // Returns the Custom information to be used for crash reporting.
63   google_breakpad::CustomClientInfo* GetCustomInfo();
64
65   // This callback is executed when the process has crashed and *before*
66   // the crash dump is created. To prevent duplicate crash reports we
67   // make every thread calling this method, except the very first one,
68   // go to sleep.
69   static bool OnExceptionCallback(void* context,
70                                   EXCEPTION_POINTERS* exinfo,
71                                   MDRawAssertionInfo* assertion);
72
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.
79   scoped_ptr<google_breakpad::ExceptionHandler> breakpad_;
80
81   // This flag is used to indicate that an exception is already being handled.
82   volatile AtomicWord handling_exception_;
83
84   // The testing hook below allows overriding the crash server pipe name.
85   static const wchar_t* pipe_name_;
86
87   friend void ::remoting::InitializeCrashReportingForTest(const wchar_t*);
88
89   DISALLOW_COPY_AND_ASSIGN(BreakpadWin);
90 };
91
92 // |LazyInstance| is used to guarantee that the exception handler will be
93 // initialized exactly once.
94 // N.B. LazyInstance does not allow this to be a static member of the class.
95 static base::LazyInstance<BreakpadWin>::Leaky g_instance =
96     LAZY_INSTANCE_INITIALIZER;
97
98 const wchar_t* BreakpadWin::pipe_name_ = kGoogleUpdatePipeName;
99
100 BreakpadWin::BreakpadWin() : handling_exception_(0) {
101   // Disable the message box for assertions.
102   _CrtSetReportMode(_CRT_ASSERT, 0);
103
104   // 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
106   //      necessary before crashes can be properly reported.
107   wchar_t temp_directory[MAX_PATH + 1] = { 0 };
108   DWORD length = GetTempPath(MAX_PATH, temp_directory);
109   if (length == 0)
110     return;
111
112   // Minidump with stacks, PEB, TEBs and unloaded module list.
113   MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
114       MiniDumpWithProcessThreadData |
115       MiniDumpWithUnloadedModules);
116   breakpad_.reset(
117       new google_breakpad::ExceptionHandler(
118           temp_directory, &OnExceptionCallback, NULL, NULL,
119           google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
120           pipe_name_, GetCustomInfo()));
121
122   if (breakpad_->IsOutOfProcess()) {
123     // Tells breakpad to handle breakpoint and single step exceptions.
124     breakpad_->set_handle_debug_exceptions(true);
125   }
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 }
132
133 BreakpadWin::~BreakpadWin() {
134   // This object should be leaked so that crashes occurred during the process
135   // shutdown will be caught.
136   NOTREACHED();
137 }
138
139 // static
140 BreakpadWin* BreakpadWin::GetInstance() {
141   return &g_instance.Get();
142 }
143
144 // Returns the Custom information to be used for crash reporting.
145 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, version_info->product_version().c_str());
154   } else {
155     wcscpy_s(version, kBreakpadVersionDefault);
156   }
157
158   static google_breakpad::CustomInfoEntry ver_entry(
159       kBreakpadVersionEntry, version);
160   static google_breakpad::CustomInfoEntry prod_entry(
161       kBreakpadProdEntry, kBreakpadProductName);
162   static google_breakpad::CustomInfoEntry plat_entry(
163       kBreakpadPlatformEntry, kBreakpadPlatformWin32);
164   static google_breakpad::CustomInfoEntry entries[] = {
165       ver_entry, prod_entry, plat_entry  };
166   static google_breakpad::CustomClientInfo custom_info = {
167       entries, arraysize(entries) };
168   return &custom_info;
169 }
170
171 // static
172 bool BreakpadWin::OnExceptionCallback(void* /* context */,
173                                       EXCEPTION_POINTERS* /* exinfo */,
174                                       MDRawAssertionInfo* /* assertion */) {
175   BreakpadWin* self = BreakpadWin::GetInstance();
176   if (NoBarrier_CompareAndSwap(&self->handling_exception_, 0, 1) != 0) {
177     // Capture every thread except the first one in the sleep. We don't
178     // want multiple threads to concurrently report exceptions.
179     ::Sleep(INFINITE);
180   }
181   return true;
182 }
183
184 // static
185 int BreakpadWin::OnWindowProcedureException(EXCEPTION_POINTERS* exinfo) {
186   BreakpadWin* self = BreakpadWin::GetInstance();
187   if (self->breakpad_.get() != NULL) {
188     self->breakpad_->WriteMinidumpForException(exinfo);
189     TerminateProcess(GetCurrentProcess(),
190                      exinfo->ExceptionRecord->ExceptionCode);
191   }
192   return EXCEPTION_CONTINUE_SEARCH;
193 }
194
195 }  // namespace
196
197 namespace remoting {
198
199 void InitializeCrashReporting() {
200   // Touch the object to make sure it is initialized.
201   BreakpadWin::GetInstance();
202 }
203
204 void InitializeCrashReportingForTest(const wchar_t* pipe_name) {
205   BreakpadWin::pipe_name_ = pipe_name;
206   InitializeCrashReporting();
207 }
208
209 }  // namespace remoting