|
1 // Copyright (c) 2006, Google Inc. |
|
2 // All rights reserved. |
|
3 // |
|
4 // Redistribution and use in source and binary forms, with or without |
|
5 // modification, are permitted provided that the following conditions are |
|
6 // met: |
|
7 // |
|
8 // * Redistributions of source code must retain the above copyright |
|
9 // notice, this list of conditions and the following disclaimer. |
|
10 // * Redistributions in binary form must reproduce the above |
|
11 // copyright notice, this list of conditions and the following disclaimer |
|
12 // in the documentation and/or other materials provided with the |
|
13 // distribution. |
|
14 // * Neither the name of Google Inc. nor the names of its |
|
15 // contributors may be used to endorse or promote products derived from |
|
16 // this software without specific prior written permission. |
|
17 // |
|
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
29 |
|
30 // ExceptionHandler can write a minidump file when an exception occurs, |
|
31 // or when WriteMinidump() is called explicitly by your program. |
|
32 // |
|
33 // To have the exception handler write minidumps when an uncaught exception |
|
34 // (crash) occurs, you should create an instance early in the execution |
|
35 // of your program, and keep it around for the entire time you want to |
|
36 // have crash handling active (typically, until shutdown). |
|
37 // |
|
38 // If you want to write minidumps without installing the exception handler, |
|
39 // you can create an ExceptionHandler with install_handler set to false, |
|
40 // then call WriteMinidump. You can also use this technique if you want to |
|
41 // use different minidump callbacks for different call sites. |
|
42 // |
|
43 // In either case, a callback function is called when a minidump is written, |
|
44 // which receives the unqiue id of the minidump. The caller can use this |
|
45 // id to collect and write additional application state, and to launch an |
|
46 // external crash-reporting application. |
|
47 // |
|
48 // It is important that creation and destruction of ExceptionHandler objects |
|
49 // be nested cleanly, when using install_handler = true. |
|
50 // Avoid the following pattern: |
|
51 // ExceptionHandler *e = new ExceptionHandler(...); |
|
52 // ExceptionHandler *f = new ExceptionHandler(...); |
|
53 // delete e; |
|
54 // This will put the exception filter stack into an inconsistent state. |
|
55 |
|
56 #ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ |
|
57 #define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ |
|
58 |
|
59 #include <stdlib.h> |
|
60 #include <Windows.h> |
|
61 #include <DbgHelp.h> |
|
62 #include <rpc.h> |
|
63 |
|
64 #pragma warning( push ) |
|
65 // Disable exception handler warnings. |
|
66 #pragma warning( disable : 4530 ) |
|
67 |
|
68 #include <list> |
|
69 #include <string> |
|
70 #include <vector> |
|
71 |
|
72 #include "client/windows/common/ipc_protocol.h" |
|
73 #include "client/windows/crash_generation/crash_generation_client.h" |
|
74 #include "common/scoped_ptr.h" |
|
75 #include "google_breakpad/common/minidump_format.h" |
|
76 |
|
77 namespace google_breakpad { |
|
78 |
|
79 using std::vector; |
|
80 using std::wstring; |
|
81 |
|
82 // These entries store a list of memory regions that the client wants included |
|
83 // in the minidump. |
|
84 struct AppMemory { |
|
85 ULONG64 ptr; |
|
86 ULONG length; |
|
87 |
|
88 bool operator==(const struct AppMemory& other) const { |
|
89 return ptr == other.ptr; |
|
90 } |
|
91 |
|
92 bool operator==(const void* other) const { |
|
93 return ptr == reinterpret_cast<ULONG64>(other); |
|
94 } |
|
95 }; |
|
96 typedef std::list<AppMemory> AppMemoryList; |
|
97 |
|
98 class ExceptionHandler { |
|
99 public: |
|
100 // A callback function to run before Breakpad performs any substantial |
|
101 // processing of an exception. A FilterCallback is called before writing |
|
102 // a minidump. context is the parameter supplied by the user as |
|
103 // callback_context when the handler was created. exinfo points to the |
|
104 // exception record, if any; assertion points to assertion information, |
|
105 // if any. |
|
106 // |
|
107 // If a FilterCallback returns true, Breakpad will continue processing, |
|
108 // attempting to write a minidump. If a FilterCallback returns false, |
|
109 // Breakpad will immediately report the exception as unhandled without |
|
110 // writing a minidump, allowing another handler the opportunity to handle it. |
|
111 typedef bool (*FilterCallback)(void* context, EXCEPTION_POINTERS* exinfo, |
|
112 MDRawAssertionInfo* assertion); |
|
113 |
|
114 // A callback function to run after the minidump has been written. |
|
115 // minidump_id is a unique id for the dump, so the minidump |
|
116 // file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied |
|
117 // by the user as callback_context when the handler was created. exinfo |
|
118 // points to the exception record, or NULL if no exception occurred. |
|
119 // succeeded indicates whether a minidump file was successfully written. |
|
120 // assertion points to information about an assertion if the handler was |
|
121 // invoked by an assertion. |
|
122 // |
|
123 // If an exception occurred and the callback returns true, Breakpad will treat |
|
124 // the exception as fully-handled, suppressing any other handlers from being |
|
125 // notified of the exception. If the callback returns false, Breakpad will |
|
126 // treat the exception as unhandled, and allow another handler to handle it. |
|
127 // If there are no other handlers, Breakpad will report the exception to the |
|
128 // system as unhandled, allowing a debugger or native crash dialog the |
|
129 // opportunity to handle the exception. Most callback implementations |
|
130 // should normally return the value of |succeeded|, or when they wish to |
|
131 // not report an exception of handled, false. Callbacks will rarely want to |
|
132 // return true directly (unless |succeeded| is true). |
|
133 // |
|
134 // For out-of-process dump generation, dump path and minidump ID will always |
|
135 // be NULL. In case of out-of-process dump generation, the dump path and |
|
136 // minidump id are controlled by the server process and are not communicated |
|
137 // back to the crashing process. |
|
138 typedef bool (*MinidumpCallback)(const wchar_t* dump_path, |
|
139 const wchar_t* minidump_id, |
|
140 void* context, |
|
141 EXCEPTION_POINTERS* exinfo, |
|
142 MDRawAssertionInfo* assertion, |
|
143 bool succeeded); |
|
144 |
|
145 // HandlerType specifies which types of handlers should be installed, if |
|
146 // any. Use HANDLER_NONE for an ExceptionHandler that remains idle, |
|
147 // without catching any failures on its own. This type of handler may |
|
148 // still be triggered by calling WriteMinidump. Otherwise, use a |
|
149 // combination of the other HANDLER_ values, or HANDLER_ALL to install |
|
150 // all handlers. |
|
151 enum HandlerType { |
|
152 HANDLER_NONE = 0, |
|
153 HANDLER_EXCEPTION = 1 << 0, // SetUnhandledExceptionFilter |
|
154 HANDLER_INVALID_PARAMETER = 1 << 1, // _set_invalid_parameter_handler |
|
155 HANDLER_PURECALL = 1 << 2, // _set_purecall_handler |
|
156 HANDLER_ALL = HANDLER_EXCEPTION | |
|
157 HANDLER_INVALID_PARAMETER | |
|
158 HANDLER_PURECALL |
|
159 }; |
|
160 |
|
161 // Creates a new ExceptionHandler instance to handle writing minidumps. |
|
162 // Before writing a minidump, the optional filter callback will be called. |
|
163 // Its return value determines whether or not Breakpad should write a |
|
164 // minidump. Minidump files will be written to dump_path, and the optional |
|
165 // callback is called after writing the dump file, as described above. |
|
166 // handler_types specifies the types of handlers that should be installed. |
|
167 ExceptionHandler(const wstring& dump_path, |
|
168 FilterCallback filter, |
|
169 MinidumpCallback callback, |
|
170 void* callback_context, |
|
171 int handler_types); |
|
172 |
|
173 // Creates a new ExceptionHandler instance that can attempt to perform |
|
174 // out-of-process dump generation if pipe_name is not NULL. If pipe_name is |
|
175 // NULL, or if out-of-process dump generation registration step fails, |
|
176 // in-process dump generation will be used. This also allows specifying |
|
177 // the dump type to generate. |
|
178 ExceptionHandler(const wstring& dump_path, |
|
179 FilterCallback filter, |
|
180 MinidumpCallback callback, |
|
181 void* callback_context, |
|
182 int handler_types, |
|
183 MINIDUMP_TYPE dump_type, |
|
184 const wchar_t* pipe_name, |
|
185 const CustomClientInfo* custom_info); |
|
186 |
|
187 // As above, creates a new ExceptionHandler instance to perform |
|
188 // out-of-process dump generation if the given pipe_handle is not NULL. |
|
189 ExceptionHandler(const wstring& dump_path, |
|
190 FilterCallback filter, |
|
191 MinidumpCallback callback, |
|
192 void* callback_context, |
|
193 int handler_types, |
|
194 MINIDUMP_TYPE dump_type, |
|
195 HANDLE pipe_handle, |
|
196 const CustomClientInfo* custom_info); |
|
197 |
|
198 ~ExceptionHandler(); |
|
199 |
|
200 // Get and set the minidump path. |
|
201 wstring dump_path() const { return dump_path_; } |
|
202 void set_dump_path(const wstring &dump_path) { |
|
203 dump_path_ = dump_path; |
|
204 dump_path_c_ = dump_path_.c_str(); |
|
205 UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. |
|
206 } |
|
207 |
|
208 // Requests that a previously reported crash be uploaded. |
|
209 bool RequestUpload(DWORD crash_id); |
|
210 |
|
211 // Writes a minidump immediately. This can be used to capture the |
|
212 // execution state independently of a crash. Returns true on success. |
|
213 bool WriteMinidump(); |
|
214 |
|
215 // Writes a minidump immediately, with the user-supplied exception |
|
216 // information. |
|
217 bool WriteMinidumpForException(EXCEPTION_POINTERS* exinfo); |
|
218 |
|
219 // Convenience form of WriteMinidump which does not require an |
|
220 // ExceptionHandler instance. |
|
221 static bool WriteMinidump(const wstring &dump_path, |
|
222 MinidumpCallback callback, void* callback_context); |
|
223 |
|
224 // Write a minidump of |child| immediately. This can be used to |
|
225 // capture the execution state of |child| independently of a crash. |
|
226 // Pass a meaningful |child_blamed_thread| to make that thread in |
|
227 // the child process the one from which a crash signature is |
|
228 // extracted. |
|
229 static bool WriteMinidumpForChild(HANDLE child, |
|
230 DWORD child_blamed_thread, |
|
231 const wstring& dump_path, |
|
232 MinidumpCallback callback, |
|
233 void* callback_context); |
|
234 |
|
235 // Get the thread ID of the thread requesting the dump (either the exception |
|
236 // thread or any other thread that called WriteMinidump directly). This |
|
237 // may be useful if you want to include additional thread state in your |
|
238 // dumps. |
|
239 DWORD get_requesting_thread_id() const { return requesting_thread_id_; } |
|
240 |
|
241 // Controls behavior of EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP. |
|
242 bool get_handle_debug_exceptions() const { return handle_debug_exceptions_; } |
|
243 void set_handle_debug_exceptions(bool handle_debug_exceptions) { |
|
244 handle_debug_exceptions_ = handle_debug_exceptions; |
|
245 } |
|
246 |
|
247 // Returns whether out-of-process dump generation is used or not. |
|
248 bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; } |
|
249 |
|
250 // Calling RegisterAppMemory(p, len) causes len bytes starting |
|
251 // at address p to be copied to the minidump when a crash happens. |
|
252 void RegisterAppMemory(void* ptr, size_t length); |
|
253 void UnregisterAppMemory(void* ptr); |
|
254 |
|
255 private: |
|
256 friend class AutoExceptionHandler; |
|
257 |
|
258 // Initializes the instance with given values. |
|
259 void Initialize(const wstring& dump_path, |
|
260 FilterCallback filter, |
|
261 MinidumpCallback callback, |
|
262 void* callback_context, |
|
263 int handler_types, |
|
264 MINIDUMP_TYPE dump_type, |
|
265 const wchar_t* pipe_name, |
|
266 HANDLE pipe_handle, |
|
267 const CustomClientInfo* custom_info); |
|
268 |
|
269 // Function pointer type for MiniDumpWriteDump, which is looked up |
|
270 // dynamically. |
|
271 typedef BOOL (WINAPI *MiniDumpWriteDump_type)( |
|
272 HANDLE hProcess, |
|
273 DWORD dwPid, |
|
274 HANDLE hFile, |
|
275 MINIDUMP_TYPE DumpType, |
|
276 CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, |
|
277 CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, |
|
278 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); |
|
279 |
|
280 // Function pointer type for UuidCreate, which is looked up dynamically. |
|
281 typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID* Uuid); |
|
282 |
|
283 // Runs the main loop for the exception handler thread. |
|
284 static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter); |
|
285 |
|
286 // Called on the exception thread when an unhandled exception occurs. |
|
287 // Signals the exception handler thread to handle the exception. |
|
288 static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo); |
|
289 |
|
290 #if _MSC_VER >= 1400 // MSVC 2005/8 |
|
291 // This function will be called by some CRT functions when they detect |
|
292 // that they were passed an invalid parameter. Note that in _DEBUG builds, |
|
293 // the CRT may display an assertion dialog before calling this function, |
|
294 // and the function will not be called unless the assertion dialog is |
|
295 // dismissed by clicking "Ignore." |
|
296 static void HandleInvalidParameter(const wchar_t* expression, |
|
297 const wchar_t* function, |
|
298 const wchar_t* file, |
|
299 unsigned int line, |
|
300 uintptr_t reserved); |
|
301 #endif // _MSC_VER >= 1400 |
|
302 |
|
303 // This function will be called by the CRT when a pure virtual |
|
304 // function is called. |
|
305 static void HandlePureVirtualCall(); |
|
306 |
|
307 // This is called on the exception thread or on another thread that |
|
308 // the user wishes to produce a dump from. It calls |
|
309 // WriteMinidumpWithException on the handler thread, avoiding stack |
|
310 // overflows and inconsistent dumps due to writing the dump from |
|
311 // the exception thread. If the dump is requested as a result of an |
|
312 // exception, exinfo contains exception information, otherwise, it |
|
313 // is NULL. If the dump is requested as a result of an assertion |
|
314 // (such as an invalid parameter being passed to a CRT function), |
|
315 // assertion contains data about the assertion, otherwise, it is NULL. |
|
316 bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo, |
|
317 MDRawAssertionInfo* assertion); |
|
318 |
|
319 // This function is called on the handler thread. It calls into |
|
320 // WriteMinidumpWithExceptionForProcess() with a handle to the |
|
321 // current process. requesting_thread_id is the ID of the thread |
|
322 // that requested the dump. If the dump is requested as a result of |
|
323 // an exception, exinfo contains exception information, otherwise, |
|
324 // it is NULL. |
|
325 bool WriteMinidumpWithException(DWORD requesting_thread_id, |
|
326 EXCEPTION_POINTERS* exinfo, |
|
327 MDRawAssertionInfo* assertion); |
|
328 |
|
329 // This function is used as a callback when calling MinidumpWriteDump, |
|
330 // in order to add additional memory regions to the dump. |
|
331 static BOOL CALLBACK MinidumpWriteDumpCallback( |
|
332 PVOID context, |
|
333 const PMINIDUMP_CALLBACK_INPUT callback_input, |
|
334 PMINIDUMP_CALLBACK_OUTPUT callback_output); |
|
335 |
|
336 // This function does the actual writing of a minidump. It is |
|
337 // called on the handler thread. requesting_thread_id is the ID of |
|
338 // the thread that requested the dump, if that information is |
|
339 // meaningful. If the dump is requested as a result of an |
|
340 // exception, exinfo contains exception information, otherwise, it |
|
341 // is NULL. process is the one that will be dumped. If |
|
342 // requesting_thread_id is meaningful and should be added to the |
|
343 // minidump, write_requester_stream is |true|. |
|
344 bool WriteMinidumpWithExceptionForProcess(DWORD requesting_thread_id, |
|
345 EXCEPTION_POINTERS* exinfo, |
|
346 MDRawAssertionInfo* assertion, |
|
347 HANDLE process, |
|
348 bool write_requester_stream); |
|
349 |
|
350 // Generates a new ID and stores it in next_minidump_id_, and stores the |
|
351 // path of the next minidump to be written in next_minidump_path_. |
|
352 void UpdateNextID(); |
|
353 |
|
354 FilterCallback filter_; |
|
355 MinidumpCallback callback_; |
|
356 void* callback_context_; |
|
357 |
|
358 scoped_ptr<CrashGenerationClient> crash_generation_client_; |
|
359 |
|
360 // The directory in which a minidump will be written, set by the dump_path |
|
361 // argument to the constructor, or set_dump_path. |
|
362 wstring dump_path_; |
|
363 |
|
364 // The basename of the next minidump to be written, without the extension. |
|
365 wstring next_minidump_id_; |
|
366 |
|
367 // The full pathname of the next minidump to be written, including the file |
|
368 // extension. |
|
369 wstring next_minidump_path_; |
|
370 |
|
371 // Pointers to C-string representations of the above. These are set when |
|
372 // the above wstring versions are set in order to avoid calling c_str during |
|
373 // an exception, as c_str may attempt to allocate heap memory. These |
|
374 // pointers are not owned by the ExceptionHandler object, but their lifetimes |
|
375 // should be equivalent to the lifetimes of the associated wstring, provided |
|
376 // that the wstrings are not altered. |
|
377 const wchar_t* dump_path_c_; |
|
378 const wchar_t* next_minidump_id_c_; |
|
379 const wchar_t* next_minidump_path_c_; |
|
380 |
|
381 HMODULE dbghelp_module_; |
|
382 MiniDumpWriteDump_type minidump_write_dump_; |
|
383 MINIDUMP_TYPE dump_type_; |
|
384 |
|
385 HMODULE rpcrt4_module_; |
|
386 UuidCreate_type uuid_create_; |
|
387 |
|
388 // Tracks the handler types that were installed according to the |
|
389 // handler_types constructor argument. |
|
390 int handler_types_; |
|
391 |
|
392 // When installed_handler_ is true, previous_filter_ is the unhandled |
|
393 // exception filter that was set prior to installing ExceptionHandler as |
|
394 // the unhandled exception filter and pointing it to |this|. NULL indicates |
|
395 // that there is no previous unhandled exception filter. |
|
396 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_; |
|
397 |
|
398 #if _MSC_VER >= 1400 // MSVC 2005/8 |
|
399 // Beginning in VC 8, the CRT provides an invalid parameter handler that will |
|
400 // be called when some CRT functions are passed invalid parameters. In |
|
401 // earlier CRTs, the same conditions would cause unexpected behavior or |
|
402 // crashes. |
|
403 _invalid_parameter_handler previous_iph_; |
|
404 #endif // _MSC_VER >= 1400 |
|
405 |
|
406 // The CRT allows you to override the default handler for pure |
|
407 // virtual function calls. |
|
408 _purecall_handler previous_pch_; |
|
409 |
|
410 // The exception handler thread. |
|
411 HANDLE handler_thread_; |
|
412 |
|
413 // True if the exception handler is being destroyed. |
|
414 // Starting with MSVC 2005, Visual C has stronger guarantees on volatile vars. |
|
415 // It has release semantics on write and acquire semantics on reads. |
|
416 // See the msdn documentation. |
|
417 volatile bool is_shutdown_; |
|
418 |
|
419 // The critical section enforcing the requirement that only one exception be |
|
420 // handled by a handler at a time. |
|
421 CRITICAL_SECTION handler_critical_section_; |
|
422 |
|
423 // Semaphores used to move exception handling between the exception thread |
|
424 // and the handler thread. handler_start_semaphore_ is signalled by the |
|
425 // exception thread to wake up the handler thread when an exception occurs. |
|
426 // handler_finish_semaphore_ is signalled by the handler thread to wake up |
|
427 // the exception thread when handling is complete. |
|
428 HANDLE handler_start_semaphore_; |
|
429 HANDLE handler_finish_semaphore_; |
|
430 |
|
431 // The next 2 fields contain data passed from the requesting thread to |
|
432 // the handler thread. |
|
433 |
|
434 // The thread ID of the thread requesting the dump (either the exception |
|
435 // thread or any other thread that called WriteMinidump directly). |
|
436 DWORD requesting_thread_id_; |
|
437 |
|
438 // The exception info passed to the exception handler on the exception |
|
439 // thread, if an exception occurred. NULL for user-requested dumps. |
|
440 EXCEPTION_POINTERS* exception_info_; |
|
441 |
|
442 // If the handler is invoked due to an assertion, this will contain a |
|
443 // pointer to the assertion information. It is NULL at other times. |
|
444 MDRawAssertionInfo* assertion_; |
|
445 |
|
446 // The return value of the handler, passed from the handler thread back to |
|
447 // the requesting thread. |
|
448 bool handler_return_value_; |
|
449 |
|
450 // If true, the handler will intercept EXCEPTION_BREAKPOINT and |
|
451 // EXCEPTION_SINGLE_STEP exceptions. Leave this false (the default) |
|
452 // to not interfere with debuggers. |
|
453 bool handle_debug_exceptions_; |
|
454 |
|
455 // Callers can request additional memory regions to be included in |
|
456 // the dump. |
|
457 AppMemoryList app_memory_info_; |
|
458 |
|
459 // A stack of ExceptionHandler objects that have installed unhandled |
|
460 // exception filters. This vector is used by HandleException to determine |
|
461 // which ExceptionHandler object to route an exception to. When an |
|
462 // ExceptionHandler is created with install_handler true, it will append |
|
463 // itself to this list. |
|
464 static vector<ExceptionHandler*>* handler_stack_; |
|
465 |
|
466 // The index of the ExceptionHandler in handler_stack_ that will handle the |
|
467 // next exception. Note that 0 means the last entry in handler_stack_, 1 |
|
468 // means the next-to-last entry, and so on. This is used by HandleException |
|
469 // to support multiple stacked Breakpad handlers. |
|
470 static LONG handler_stack_index_; |
|
471 |
|
472 // handler_stack_critical_section_ guards operations on handler_stack_ and |
|
473 // handler_stack_index_. The critical section is initialized by the |
|
474 // first instance of the class and destroyed by the last instance of it. |
|
475 static CRITICAL_SECTION handler_stack_critical_section_; |
|
476 |
|
477 // The number of instances of this class. |
|
478 volatile static LONG instance_count_; |
|
479 |
|
480 // disallow copy ctor and operator= |
|
481 explicit ExceptionHandler(const ExceptionHandler &); |
|
482 void operator=(const ExceptionHandler &); |
|
483 }; |
|
484 |
|
485 } // namespace google_breakpad |
|
486 |
|
487 #pragma warning( pop ) |
|
488 |
|
489 #endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ |