Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(107)

Side by Side Diff: third_party/breakpad/src/client/windows/handler/exception_handler.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 #include <ObjBase.h>
31
32 #include <algorithm>
33 #include <cassert>
34 #include <cstdio>
35
36 #include "common/windows/string_utils-inl.h"
37
38 #include "client/windows/common/ipc_protocol.h"
39 #include "client/windows/handler/exception_handler.h"
40 #include "common/windows/guid_string.h"
41
42 typedef VOID (WINAPI *RtlCaptureContextPtr) (PCONTEXT pContextRecord);
43
44 namespace google_breakpad {
45
46 static const int kWaitForHandlerThreadMs = 60000;
47 static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
48
49 // This is passed as the context to the MinidumpWriteDump callback.
50 typedef struct {
51 ULONG64 memory_base;
52 ULONG memory_size;
53 bool finished;
54 } MinidumpCallbackContext;
55
56 vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
57 LONG ExceptionHandler::handler_stack_index_ = 0;
58 CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
59 volatile LONG ExceptionHandler::instance_count_ = 0;
60
61 ExceptionHandler::ExceptionHandler(const wstring& dump_path,
62 FilterCallback filter,
63 MinidumpCallback callback,
64 void* callback_context,
65 int handler_types,
66 MINIDUMP_TYPE dump_type,
67 const wchar_t* pipe_name,
68 const CustomClientInfo* custom_info) {
69 Initialize(dump_path,
70 filter,
71 callback,
72 callback_context,
73 handler_types,
74 dump_type,
75 pipe_name,
76 custom_info);
77 }
78
79 ExceptionHandler::ExceptionHandler(const wstring &dump_path,
80 FilterCallback filter,
81 MinidumpCallback callback,
82 void* callback_context,
83 int handler_types) {
84 Initialize(dump_path,
85 filter,
86 callback,
87 callback_context,
88 handler_types,
89 MiniDumpNormal,
90 NULL,
91 NULL);
92 }
93
94 void ExceptionHandler::Initialize(const wstring& dump_path,
95 FilterCallback filter,
96 MinidumpCallback callback,
97 void* callback_context,
98 int handler_types,
99 MINIDUMP_TYPE dump_type,
100 const wchar_t* pipe_name,
101 const CustomClientInfo* custom_info) {
102 LONG instance_count = InterlockedIncrement(&instance_count_);
103 filter_ = filter;
104 callback_ = callback;
105 callback_context_ = callback_context;
106 dump_path_c_ = NULL;
107 next_minidump_id_c_ = NULL;
108 next_minidump_path_c_ = NULL;
109 dbghelp_module_ = NULL;
110 minidump_write_dump_ = NULL;
111 dump_type_ = dump_type;
112 rpcrt4_module_ = NULL;
113 uuid_create_ = NULL;
114 handler_types_ = handler_types;
115 previous_filter_ = NULL;
116 #if _MSC_VER >= 1400 // MSVC 2005/8
117 previous_iph_ = NULL;
118 #endif // _MSC_VER >= 1400
119 previous_pch_ = NULL;
120 handler_thread_ = NULL;
121 is_shutdown_ = false;
122 handler_start_semaphore_ = NULL;
123 handler_finish_semaphore_ = NULL;
124 requesting_thread_id_ = 0;
125 exception_info_ = NULL;
126 assertion_ = NULL;
127 handler_return_value_ = false;
128 handle_debug_exceptions_ = false;
129
130 // Attempt to use out-of-process if user has specified pipe name.
131 if (pipe_name != NULL) {
132 scoped_ptr<CrashGenerationClient> client(
133 new CrashGenerationClient(pipe_name,
134 dump_type_,
135 custom_info));
136
137 // If successful in registering with the monitoring process,
138 // there is no need to setup in-process crash generation.
139 if (client->Register()) {
140 crash_generation_client_.reset(client.release());
141 }
142 }
143
144 if (!IsOutOfProcess()) {
145 // Either client did not ask for out-of-process crash generation
146 // or registration with the server process failed. In either case,
147 // setup to do in-process crash generation.
148
149 // Set synchronization primitives and the handler thread. Each
150 // ExceptionHandler object gets its own handler thread because that's the
151 // only way to reliably guarantee sufficient stack space in an exception,
152 // and it allows an easy way to get a snapshot of the requesting thread's
153 // context outside of an exception.
154 InitializeCriticalSection(&handler_critical_section_);
155 handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
156 assert(handler_start_semaphore_ != NULL);
157
158 handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL);
159 assert(handler_finish_semaphore_ != NULL);
160
161 // Don't attempt to create the thread if we could not create the semaphores.
162 if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {
163 DWORD thread_id;
164 handler_thread_ = CreateThread(NULL, // lpThreadAttributes
165 kExceptionHandlerThreadInitialStackSize,
166 ExceptionHandlerThreadMain,
167 this, // lpParameter
168 0, // dwCreationFlags
169 &thread_id);
170 assert(handler_thread_ != NULL);
171 }
172
173 dbghelp_module_ = LoadLibrary(L"dbghelp.dll");
174 if (dbghelp_module_) {
175 minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>(
176 GetProcAddress(dbghelp_module_, "MiniDumpWriteDump"));
177 }
178
179 // Load this library dynamically to not affect existing projects. Most
180 // projects don't link against this directly, it's usually dynamically
181 // loaded by dependent code.
182 rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll");
183 if (rpcrt4_module_) {
184 uuid_create_ = reinterpret_cast<UuidCreate_type>(
185 GetProcAddress(rpcrt4_module_, "UuidCreate"));
186 }
187
188 // set_dump_path calls UpdateNextID. This sets up all of the path and id
189 // strings, and their equivalent c_str pointers.
190 set_dump_path(dump_path);
191 }
192
193 // There is a race condition here. If the first instance has not yet
194 // initialized the critical section, the second (and later) instances may
195 // try to use uninitialized critical section object. The feature of multiple
196 // instances in one module is not used much, so leave it as is for now.
197 // One way to solve this in the current design (that is, keeping the static
198 // handler stack) is to use spin locks with volatile bools to synchronize
199 // the handler stack. This works only if the compiler guarantees to generate
200 // cache coherent code for volatile.
201 // TODO(munjal): Fix this in a better way by changing the design if possible.
202
203 // Lazy initialization of the handler_stack_critical_section_
204 if (instance_count == 1) {
205 InitializeCriticalSection(&handler_stack_critical_section_);
206 }
207
208 if (handler_types != HANDLER_NONE) {
209 EnterCriticalSection(&handler_stack_critical_section_);
210
211 // The first time an ExceptionHandler that installs a handler is
212 // created, set up the handler stack.
213 if (!handler_stack_) {
214 handler_stack_ = new vector<ExceptionHandler*>();
215 }
216 handler_stack_->push_back(this);
217
218 if (handler_types & HANDLER_EXCEPTION)
219 previous_filter_ = SetUnhandledExceptionFilter(HandleException);
220
221 #if _MSC_VER >= 1400 // MSVC 2005/8
222 if (handler_types & HANDLER_INVALID_PARAMETER)
223 previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter);
224 #endif // _MSC_VER >= 1400
225
226 if (handler_types & HANDLER_PURECALL)
227 previous_pch_ = _set_purecall_handler(HandlePureVirtualCall);
228
229 LeaveCriticalSection(&handler_stack_critical_section_);
230 }
231 }
232
233 ExceptionHandler::~ExceptionHandler() {
234 if (dbghelp_module_) {
235 FreeLibrary(dbghelp_module_);
236 }
237
238 if (rpcrt4_module_) {
239 FreeLibrary(rpcrt4_module_);
240 }
241
242 if (handler_types_ != HANDLER_NONE) {
243 EnterCriticalSection(&handler_stack_critical_section_);
244
245 if (handler_types_ & HANDLER_EXCEPTION)
246 SetUnhandledExceptionFilter(previous_filter_);
247
248 #if _MSC_VER >= 1400 // MSVC 2005/8
249 if (handler_types_ & HANDLER_INVALID_PARAMETER)
250 _set_invalid_parameter_handler(previous_iph_);
251 #endif // _MSC_VER >= 1400
252
253 if (handler_types_ & HANDLER_PURECALL)
254 _set_purecall_handler(previous_pch_);
255
256 if (handler_stack_->back() == this) {
257 handler_stack_->pop_back();
258 } else {
259 // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the
260 // system's application event log.
261 fprintf(stderr, "warning: removing Breakpad handler out of order\n");
262 vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
263 while (iterator != handler_stack_->end()) {
264 if (*iterator == this) {
265 iterator = handler_stack_->erase(iterator);
266 } else {
267 ++iterator;
268 }
269 }
270 }
271
272 if (handler_stack_->empty()) {
273 // When destroying the last ExceptionHandler that installed a handler,
274 // clean up the handler stack.
275 delete handler_stack_;
276 handler_stack_ = NULL;
277 }
278
279 LeaveCriticalSection(&handler_stack_critical_section_);
280 }
281
282 // Some of the objects were only initialized if out of process
283 // registration was not done.
284 if (!IsOutOfProcess()) {
285 #ifdef BREAKPAD_NO_TERMINATE_THREAD
286 // Clean up the handler thread and synchronization primitives. The handler
287 // thread is either waiting on the semaphore to handle a crash or it is
288 // handling a crash. Coming out of the wait is fast but wait more in the
289 // eventuality a crash is handled. This compilation option results in a
290 // deadlock if the exception handler is destroyed while executing code
291 // inside DllMain.
292 is_shutdown_ = true;
293 ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
294 WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
295 #else
296 TerminateThread(handler_thread_, 1);
297 #endif // BREAKPAD_NO_TERMINATE_THREAD
298
299 CloseHandle(handler_thread_);
300 handler_thread_ = NULL;
301 DeleteCriticalSection(&handler_critical_section_);
302 CloseHandle(handler_start_semaphore_);
303 CloseHandle(handler_finish_semaphore_);
304 }
305
306 // There is a race condition in the code below: if this instance is
307 // deleting the static critical section and a new instance of the class
308 // is created, then there is a possibility that the critical section be
309 // initialized while the same critical section is being deleted. Given the
310 // usage pattern for the code, this race condition is unlikely to hit, but it
311 // is a race condition nonetheless.
312 if (InterlockedDecrement(&instance_count_) == 0) {
313 DeleteCriticalSection(&handler_stack_critical_section_);
314 }
315 }
316
317 // static
318 DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) {
319 ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(lpParameter);
320 assert(self);
321 assert(self->handler_start_semaphore_ != NULL);
322 assert(self->handler_finish_semaphore_ != NULL);
323
324 while (true) {
325 if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) ==
326 WAIT_OBJECT_0) {
327 // Perform the requested action.
328 if (self->is_shutdown_) {
329 // The instance of the exception handler is being destroyed.
330 break;
331 } else {
332 self->handler_return_value_ =
333 self->WriteMinidumpWithException(self->requesting_thread_id_,
334 self->exception_info_,
335 self->assertion_);
336 }
337
338 // Allow the requesting thread to proceed.
339 ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL);
340 }
341 }
342
343 // This statement is not reached when the thread is unconditionally
344 // terminated by the ExceptionHandler destructor.
345 return 0;
346 }
347
348 // HandleException and HandleInvalidParameter must create an
349 // AutoExceptionHandler object to maintain static state and to determine which
350 // ExceptionHandler instance to use. The constructor locates the correct
351 // instance, and makes it available through get_handler(). The destructor
352 // restores the state in effect prior to allocating the AutoExceptionHandler.
353 class AutoExceptionHandler {
354 public:
355 AutoExceptionHandler() {
356 // Increment handler_stack_index_ so that if another Breakpad handler is
357 // registered using this same HandleException function, and it needs to be
358 // called while this handler is running (either because this handler
359 // declines to handle the exception, or an exception occurs during
360 // handling), HandleException will find the appropriate ExceptionHandler
361 // object in handler_stack_ to deliver the exception to.
362 //
363 // Because handler_stack_ is addressed in reverse (as |size - index|),
364 // preincrementing handler_stack_index_ avoids needing to subtract 1 from
365 // the argument to |at|.
366 //
367 // The index is maintained instead of popping elements off of the handler
368 // stack and pushing them at the end of this method. This avoids ruining
369 // the order of elements in the stack in the event that some other thread
370 // decides to manipulate the handler stack (such as creating a new
371 // ExceptionHandler object) while an exception is being handled.
372 EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
373 handler_ = ExceptionHandler::handler_stack_->at(
374 ExceptionHandler::handler_stack_->size() -
375 ++ExceptionHandler::handler_stack_index_);
376
377 // In case another exception occurs while this handler is doing its thing,
378 // it should be delivered to the previous filter.
379 SetUnhandledExceptionFilter(handler_->previous_filter_);
380 #if _MSC_VER >= 1400 // MSVC 2005/8
381 _set_invalid_parameter_handler(handler_->previous_iph_);
382 #endif // _MSC_VER >= 1400
383 _set_purecall_handler(handler_->previous_pch_);
384 }
385
386 ~AutoExceptionHandler() {
387 // Put things back the way they were before entering this handler.
388 SetUnhandledExceptionFilter(ExceptionHandler::HandleException);
389 #if _MSC_VER >= 1400 // MSVC 2005/8
390 _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter);
391 #endif // _MSC_VER >= 1400
392 _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
393
394 --ExceptionHandler::handler_stack_index_;
395 LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
396 }
397
398 ExceptionHandler* get_handler() const { return handler_; }
399
400 private:
401 ExceptionHandler* handler_;
402 };
403
404 // static
405 LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
406 AutoExceptionHandler auto_exception_handler;
407 ExceptionHandler* current_handler = auto_exception_handler.get_handler();
408
409 // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This
410 // logic will short-circuit before calling WriteMinidumpOnHandlerThread,
411 // allowing something else to handle the breakpoint without incurring the
412 // overhead transitioning to and from the handler thread. This behavior
413 // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions.
414 DWORD code = exinfo->ExceptionRecord->ExceptionCode;
415 LONG action;
416 bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
417 (code == EXCEPTION_SINGLE_STEP);
418
419 bool success = false;
420
421 if (!is_debug_exception ||
422 current_handler->get_handle_debug_exceptions()) {
423 // If out-of-proc crash handler client is available, we have to use that
424 // to generate dump and we cannot fall back on in-proc dump generation
425 // because we never prepared for an in-proc dump generation
426
427 // In case of out-of-process dump generation, directly call
428 // WriteMinidumpWithException since there is no separate thread running.
429 if (current_handler->IsOutOfProcess()) {
430 success = current_handler->WriteMinidumpWithException(
431 GetCurrentThreadId(),
432 exinfo,
433 NULL);
434 } else {
435 success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL);
436 }
437 }
438
439 // The handler fully handled the exception. Returning
440 // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually
441 // results in the application being terminated.
442 //
443 // Note: If the application was launched from within the Cygwin
444 // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the
445 // application to be restarted.
446 if (success) {
447 action = EXCEPTION_EXECUTE_HANDLER;
448 } else {
449 // There was an exception, it was a breakpoint or something else ignored
450 // above, or it was passed to the handler, which decided not to handle it.
451 // This could be because the filter callback didn't want it, because
452 // minidump writing failed for some reason, or because the post-minidump
453 // callback function indicated failure. Give the previous handler a
454 // chance to do something with the exception. If there is no previous
455 // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger
456 // or native "crashed" dialog to handle the exception.
457 if (current_handler->previous_filter_) {
458 action = current_handler->previous_filter_(exinfo);
459 } else {
460 action = EXCEPTION_CONTINUE_SEARCH;
461 }
462 }
463
464 return action;
465 }
466
467 #if _MSC_VER >= 1400 // MSVC 2005/8
468 // static
469 void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression,
470 const wchar_t* function,
471 const wchar_t* file,
472 unsigned int line,
473 uintptr_t reserved) {
474 // This is an invalid parameter, not an exception. It's safe to play with
475 // sprintf here.
476 AutoExceptionHandler auto_exception_handler;
477 ExceptionHandler* current_handler = auto_exception_handler.get_handler();
478
479 MDRawAssertionInfo assertion;
480 memset(&assertion, 0, sizeof(assertion));
481 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression),
482 sizeof(assertion.expression) / sizeof(assertion.expression[0]),
483 _TRUNCATE, L"%s", expression);
484 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function),
485 sizeof(assertion.function) / sizeof(assertion.function[0]),
486 _TRUNCATE, L"%s", function);
487 _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.file),
488 sizeof(assertion.file) / sizeof(assertion.file[0]),
489 _TRUNCATE, L"%s", file);
490 assertion.line = line;
491 assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER;
492
493 // Make up an exception record for the current thread and CPU context
494 // to make it possible for the crash processor to classify these
495 // as do regular crashes, and to make it humane for developers to
496 // analyze them.
497 EXCEPTION_RECORD exception_record = {};
498 CONTEXT exception_context = {};
499 EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
500
501 EXCEPTION_POINTERS* exinfo = NULL;
502
503 RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr)
504 GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext");
505 if (fnRtlCaptureContext) {
506 fnRtlCaptureContext(&exception_context);
507
508 exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
509
510 // We store pointers to the the expression and function strings,
511 // and the line as exception parameters to make them easy to
512 // access by the developer on the far side.
513 exception_record.NumberParameters = 3;
514 exception_record.ExceptionInformation[0] =
515 reinterpret_cast<ULONG_PTR>(&assertion.expression);
516 exception_record.ExceptionInformation[1] =
517 reinterpret_cast<ULONG_PTR>(&assertion.file);
518 exception_record.ExceptionInformation[2] = assertion.line;
519
520 exinfo = &exception_ptrs;
521 }
522
523 bool success = false;
524 // In case of out-of-process dump generation, directly call
525 // WriteMinidumpWithException since there is no separate thread running.
526 if (current_handler->IsOutOfProcess()) {
527 success = current_handler->WriteMinidumpWithException(
528 GetCurrentThreadId(),
529 exinfo,
530 &assertion);
531 } else {
532 success = current_handler->WriteMinidumpOnHandlerThread(exinfo,
533 &assertion);
534 }
535
536 if (!success) {
537 if (current_handler->previous_iph_) {
538 // The handler didn't fully handle the exception. Give it to the
539 // previous invalid parameter handler.
540 current_handler->previous_iph_(expression,
541 function,
542 file,
543 line,
544 reserved);
545 } else {
546 // If there's no previous handler, pass the exception back in to the
547 // invalid parameter handler's core. That's the routine that called this
548 // function, but now, since this function is no longer registered (and in
549 // fact, no function at all is registered), this will result in the
550 // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson.
551 // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes
552 // more information through. In non-debug builds, it is not available,
553 // so fall back to using _invalid_parameter_noinfo. See invarg.c in the
554 // CRT source.
555 #ifdef _DEBUG
556 _invalid_parameter(expression, function, file, line, reserved);
557 #else // _DEBUG
558 _invalid_parameter_noinfo();
559 #endif // _DEBUG
560 }
561 }
562
563 // The handler either took care of the invalid parameter problem itself,
564 // or passed it on to another handler. "Swallow" it by exiting, paralleling
565 // the behavior of "swallowing" exceptions.
566 exit(0);
567 }
568 #endif // _MSC_VER >= 1400
569
570 // static
571 void ExceptionHandler::HandlePureVirtualCall() {
572 // This is an pure virtual function call, not an exception. It's safe to
573 // play with sprintf here.
574 AutoExceptionHandler auto_exception_handler;
575 ExceptionHandler* current_handler = auto_exception_handler.get_handler();
576
577 MDRawAssertionInfo assertion;
578 memset(&assertion, 0, sizeof(assertion));
579 assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
580
581 // Make up an exception record for the current thread and CPU context
582 // to make it possible for the crash processor to classify these
583 // as do regular crashes, and to make it humane for developers to
584 // analyze them.
585 EXCEPTION_RECORD exception_record = {};
586 CONTEXT exception_context = {};
587 EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
588
589 EXCEPTION_POINTERS* exinfo = NULL;
590
591 RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr)
592 GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext");
593 if (fnRtlCaptureContext) {
594 fnRtlCaptureContext(&exception_context);
595
596 exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
597
598 // We store pointers to the the expression and function strings,
599 // and the line as exception parameters to make them easy to
600 // access by the developer on the far side.
601 exception_record.NumberParameters = 3;
602 exception_record.ExceptionInformation[0] =
603 reinterpret_cast<ULONG_PTR>(&assertion.expression);
604 exception_record.ExceptionInformation[1] =
605 reinterpret_cast<ULONG_PTR>(&assertion.file);
606 exception_record.ExceptionInformation[2] = assertion.line;
607
608 exinfo = &exception_ptrs;
609 }
610
611 bool success = false;
612 // In case of out-of-process dump generation, directly call
613 // WriteMinidumpWithException since there is no separate thread running.
614
615 if (current_handler->IsOutOfProcess()) {
616 success = current_handler->WriteMinidumpWithException(
617 GetCurrentThreadId(),
618 exinfo,
619 &assertion);
620 } else {
621 success = current_handler->WriteMinidumpOnHandlerThread(exinfo,
622 &assertion);
623 }
624
625 if (!success) {
626 if (current_handler->previous_pch_) {
627 // The handler didn't fully handle the exception. Give it to the
628 // previous purecall handler.
629 current_handler->previous_pch_();
630 } else {
631 // If there's no previous handler, return and let _purecall handle it.
632 // This will just put up an assertion dialog.
633 return;
634 }
635 }
636
637 // The handler either took care of the invalid parameter problem itself,
638 // or passed it on to another handler. "Swallow" it by exiting, paralleling
639 // the behavior of "swallowing" exceptions.
640 exit(0);
641 }
642
643 bool ExceptionHandler::WriteMinidumpOnHandlerThread(
644 EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) {
645 EnterCriticalSection(&handler_critical_section_);
646
647 // There isn't much we can do if the handler thread
648 // was not successfully created.
649 if (handler_thread_ == NULL) {
650 LeaveCriticalSection(&handler_critical_section_);
651 return false;
652 }
653
654 // The handler thread should only be created when the semaphores are valid.
655 assert(handler_start_semaphore_ != NULL);
656 assert(handler_finish_semaphore_ != NULL);
657
658 // Set up data to be passed in to the handler thread.
659 requesting_thread_id_ = GetCurrentThreadId();
660 exception_info_ = exinfo;
661 assertion_ = assertion;
662
663 // This causes the handler thread to call WriteMinidumpWithException.
664 ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
665
666 // Wait until WriteMinidumpWithException is done and collect its return value.
667 WaitForSingleObject(handler_finish_semaphore_, INFINITE);
668 bool status = handler_return_value_;
669
670 // Clean up.
671 requesting_thread_id_ = 0;
672 exception_info_ = NULL;
673 assertion_ = NULL;
674
675 LeaveCriticalSection(&handler_critical_section_);
676
677 return status;
678 }
679
680 bool ExceptionHandler::WriteMinidump() {
681 return WriteMinidumpForException(NULL);
682 }
683
684 bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) {
685 // In case of out-of-process dump generation, directly call
686 // WriteMinidumpWithException since there is no separate thread running.
687 if (IsOutOfProcess()) {
688 return WriteMinidumpWithException(GetCurrentThreadId(),
689 exinfo,
690 NULL);
691 }
692
693 bool success = WriteMinidumpOnHandlerThread(exinfo, NULL);
694 UpdateNextID();
695 return success;
696 }
697
698 // static
699 bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
700 MinidumpCallback callback,
701 void* callback_context) {
702 ExceptionHandler handler(dump_path, NULL, callback, callback_context,
703 HANDLER_NONE);
704 return handler.WriteMinidump();
705 }
706
707 bool ExceptionHandler::WriteMinidumpWithException(
708 DWORD requesting_thread_id,
709 EXCEPTION_POINTERS* exinfo,
710 MDRawAssertionInfo* assertion) {
711 // Give user code a chance to approve or prevent writing a minidump. If the
712 // filter returns false, don't handle the exception at all. If this method
713 // was called as a result of an exception, returning false will cause
714 // HandleException to call any previous handler or return
715 // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear
716 // as though this handler were not present at all.
717 if (filter_ && !filter_(callback_context_, exinfo, assertion)) {
718 return false;
719 }
720
721 bool success = false;
722 if (IsOutOfProcess()) {
723 success = crash_generation_client_->RequestDump(exinfo, assertion);
724 } else {
725 if (minidump_write_dump_) {
726 HANDLE dump_file = CreateFile(next_minidump_path_c_,
727 GENERIC_WRITE,
728 0, // no sharing
729 NULL,
730 CREATE_NEW, // fail if exists
731 FILE_ATTRIBUTE_NORMAL,
732 NULL);
733 if (dump_file != INVALID_HANDLE_VALUE) {
734 MINIDUMP_EXCEPTION_INFORMATION except_info;
735 except_info.ThreadId = requesting_thread_id;
736 except_info.ExceptionPointers = exinfo;
737 except_info.ClientPointers = FALSE;
738
739 // Add an MDRawBreakpadInfo stream to the minidump, to provide
740 // additional information about the exception handler to the Breakpad
741 // processor. The information will help the processor determine which
742 // threads are relevant. The Breakpad processor does not require this
743 // information but can function better with Breakpad-generated dumps
744 // when it is present. The native debugger is not harmed by the
745 // presence of this information.
746 MDRawBreakpadInfo breakpad_info;
747 breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
748 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
749 breakpad_info.dump_thread_id = GetCurrentThreadId();
750 breakpad_info.requesting_thread_id = requesting_thread_id;
751
752 // Leave room in user_stream_array for a possible assertion info stream.
753 MINIDUMP_USER_STREAM user_stream_array[2];
754 user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
755 user_stream_array[0].BufferSize = sizeof(breakpad_info);
756 user_stream_array[0].Buffer = &breakpad_info;
757
758 MINIDUMP_USER_STREAM_INFORMATION user_streams;
759 user_streams.UserStreamCount = 1;
760 user_streams.UserStreamArray = user_stream_array;
761
762 if (assertion) {
763 user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
764 user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
765 user_stream_array[1].Buffer = assertion;
766 ++user_streams.UserStreamCount;
767 }
768
769 MINIDUMP_CALLBACK_INFORMATION callback;
770 MinidumpCallbackContext context;
771 MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
772 // Older versions of DbgHelp.dll don't correctly put the memory around
773 // the faulting instruction pointer into the minidump. This
774 // callback will ensure that it gets included.
775 if (exinfo) {
776 // Find a memory region of 256 bytes centered on the
777 // faulting instruction pointer.
778 const ULONG64 instruction_pointer =
779 #if defined(_M_IX86)
780 exinfo->ContextRecord->Eip;
781 #elif defined(_M_AMD64)
782 exinfo->ContextRecord->Rip;
783 #else
784 #error Unsupported platform
785 #endif
786
787 MEMORY_BASIC_INFORMATION info;
788 if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
789 &info,
790 sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
791 info.State == MEM_COMMIT) {
792 // Attempt to get 128 bytes before and after the instruction
793 // pointer, but settle for whatever's available up to the
794 // boundaries of the memory region.
795 const ULONG64 kIPMemorySize = 256;
796 context.memory_base =
797 (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
798 instruction_pointer - (kIPMemorySize / 2));
799 ULONG64 end_of_range =
800 (std::min)(instruction_pointer + (kIPMemorySize / 2),
801 reinterpret_cast<ULONG64>(info.BaseAddress)
802 + info.RegionSize);
803 context.memory_size =
804 static_cast<ULONG>(end_of_range - context.memory_base);
805
806 context.finished = false;
807 callback.CallbackRoutine = MinidumpWriteDumpCallback;
808 callback.CallbackParam = reinterpret_cast<void*>(&context);
809 callback_pointer = &callback;
810 }
811 }
812
813 // The explicit comparison to TRUE avoids a warning (C4800).
814 success = (minidump_write_dump_(GetCurrentProcess(),
815 GetCurrentProcessId(),
816 dump_file,
817 dump_type_,
818 exinfo ? &except_info : NULL,
819 &user_streams,
820 callback_pointer) == TRUE);
821
822 CloseHandle(dump_file);
823 }
824 }
825 }
826
827 if (callback_) {
828 // TODO(munjal): In case of out-of-process dump generation, both
829 // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process
830 // scenario, the server process ends up creating the dump path and dump
831 // id so they are not known to the client.
832 success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
833 exinfo, assertion, success);
834 }
835
836 return success;
837 }
838
839 // static
840 BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
841 PVOID context,
842 const PMINIDUMP_CALLBACK_INPUT callback_input,
843 PMINIDUMP_CALLBACK_OUTPUT callback_output) {
844 switch (callback_input->CallbackType) {
845 case MemoryCallback: {
846 MinidumpCallbackContext* callback_context =
847 reinterpret_cast<MinidumpCallbackContext*>(context);
848 if (callback_context->finished)
849 return FALSE;
850
851 // Include the specified memory region.
852 callback_output->MemoryBase = callback_context->memory_base;
853 callback_output->MemorySize = callback_context->memory_size;
854 callback_context->finished = true;
855 return TRUE;
856 }
857
858 // Include all modules.
859 case IncludeModuleCallback:
860 case ModuleCallback:
861 return TRUE;
862
863 // Include all threads.
864 case IncludeThreadCallback:
865 case ThreadCallback:
866 return TRUE;
867
868 // Stop receiving cancel callbacks.
869 case CancelCallback:
870 callback_output->CheckCancel = FALSE;
871 callback_output->Cancel = FALSE;
872 return TRUE;
873 }
874 // Ignore other callback types.
875 return FALSE;
876 }
877
878 void ExceptionHandler::UpdateNextID() {
879 assert(uuid_create_);
880 UUID id = {0};
881 if (uuid_create_) {
882 uuid_create_(&id);
883 }
884 next_minidump_id_ = GUIDString::GUIDToWString(&id);
885 next_minidump_id_c_ = next_minidump_id_.c_str();
886
887 wchar_t minidump_path[MAX_PATH];
888 swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp",
889 dump_path_c_, next_minidump_id_c_);
890
891 // remove when VC++7.1 is no longer supported
892 minidump_path[MAX_PATH - 1] = L'\0';
893
894 next_minidump_path_ = minidump_path;
895 next_minidump_path_c_ = next_minidump_path_.c_str();
896 }
897
898 } // namespace google_breakpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698