1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1044 @@ 1.4 +// Copyright (c) 2006, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 +#include <ObjBase.h> 1.34 + 1.35 +#include <algorithm> 1.36 +#include <cassert> 1.37 +#include <cstdio> 1.38 + 1.39 +#include "common/windows/string_utils-inl.h" 1.40 + 1.41 +#include "client/windows/common/ipc_protocol.h" 1.42 +#include "client/windows/handler/exception_handler.h" 1.43 +#include "common/windows/guid_string.h" 1.44 + 1.45 +namespace google_breakpad { 1.46 + 1.47 +static const int kWaitForHandlerThreadMs = 60000; 1.48 +static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; 1.49 + 1.50 +// As documented on MSDN, on failure SuspendThread returns (DWORD) -1 1.51 +static const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1); 1.52 + 1.53 +// This is passed as the context to the MinidumpWriteDump callback. 1.54 +typedef struct { 1.55 + AppMemoryList::const_iterator iter; 1.56 + AppMemoryList::const_iterator end; 1.57 +} MinidumpCallbackContext; 1.58 + 1.59 +vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; 1.60 +LONG ExceptionHandler::handler_stack_index_ = 0; 1.61 +CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; 1.62 +volatile LONG ExceptionHandler::instance_count_ = 0; 1.63 + 1.64 +ExceptionHandler::ExceptionHandler(const wstring& dump_path, 1.65 + FilterCallback filter, 1.66 + MinidumpCallback callback, 1.67 + void* callback_context, 1.68 + int handler_types, 1.69 + MINIDUMP_TYPE dump_type, 1.70 + const wchar_t* pipe_name, 1.71 + const CustomClientInfo* custom_info) { 1.72 + Initialize(dump_path, 1.73 + filter, 1.74 + callback, 1.75 + callback_context, 1.76 + handler_types, 1.77 + dump_type, 1.78 + pipe_name, 1.79 + NULL, 1.80 + custom_info); 1.81 +} 1.82 + 1.83 +ExceptionHandler::ExceptionHandler(const wstring& dump_path, 1.84 + FilterCallback filter, 1.85 + MinidumpCallback callback, 1.86 + void* callback_context, 1.87 + int handler_types, 1.88 + MINIDUMP_TYPE dump_type, 1.89 + HANDLE pipe_handle, 1.90 + const CustomClientInfo* custom_info) { 1.91 + Initialize(dump_path, 1.92 + filter, 1.93 + callback, 1.94 + callback_context, 1.95 + handler_types, 1.96 + dump_type, 1.97 + NULL, 1.98 + pipe_handle, 1.99 + custom_info); 1.100 +} 1.101 + 1.102 +ExceptionHandler::ExceptionHandler(const wstring &dump_path, 1.103 + FilterCallback filter, 1.104 + MinidumpCallback callback, 1.105 + void* callback_context, 1.106 + int handler_types) { 1.107 + Initialize(dump_path, 1.108 + filter, 1.109 + callback, 1.110 + callback_context, 1.111 + handler_types, 1.112 + MiniDumpNormal, 1.113 + NULL, 1.114 + NULL, 1.115 + NULL); 1.116 +} 1.117 + 1.118 +void ExceptionHandler::Initialize(const wstring& dump_path, 1.119 + FilterCallback filter, 1.120 + MinidumpCallback callback, 1.121 + void* callback_context, 1.122 + int handler_types, 1.123 + MINIDUMP_TYPE dump_type, 1.124 + const wchar_t* pipe_name, 1.125 + HANDLE pipe_handle, 1.126 + const CustomClientInfo* custom_info) { 1.127 + LONG instance_count = InterlockedIncrement(&instance_count_); 1.128 + filter_ = filter; 1.129 + callback_ = callback; 1.130 + callback_context_ = callback_context; 1.131 + dump_path_c_ = NULL; 1.132 + next_minidump_id_c_ = NULL; 1.133 + next_minidump_path_c_ = NULL; 1.134 + dbghelp_module_ = NULL; 1.135 + minidump_write_dump_ = NULL; 1.136 + dump_type_ = dump_type; 1.137 + rpcrt4_module_ = NULL; 1.138 + uuid_create_ = NULL; 1.139 + handler_types_ = handler_types; 1.140 + previous_filter_ = NULL; 1.141 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.142 + previous_iph_ = NULL; 1.143 +#endif // _MSC_VER >= 1400 1.144 + previous_pch_ = NULL; 1.145 + handler_thread_ = NULL; 1.146 + is_shutdown_ = false; 1.147 + handler_start_semaphore_ = NULL; 1.148 + handler_finish_semaphore_ = NULL; 1.149 + requesting_thread_id_ = 0; 1.150 + exception_info_ = NULL; 1.151 + assertion_ = NULL; 1.152 + handler_return_value_ = false; 1.153 + handle_debug_exceptions_ = false; 1.154 + 1.155 + // Attempt to use out-of-process if user has specified a pipe. 1.156 + if (pipe_name != NULL || pipe_handle != NULL) { 1.157 + assert(!(pipe_name && pipe_handle)); 1.158 + 1.159 + scoped_ptr<CrashGenerationClient> client; 1.160 + if (pipe_name) { 1.161 + client.reset( 1.162 + new CrashGenerationClient(pipe_name, 1.163 + dump_type_, 1.164 + custom_info)); 1.165 + } else { 1.166 + client.reset( 1.167 + new CrashGenerationClient(pipe_handle, 1.168 + dump_type_, 1.169 + custom_info)); 1.170 + } 1.171 + 1.172 + // If successful in registering with the monitoring process, 1.173 + // there is no need to setup in-process crash generation. 1.174 + if (client->Register()) { 1.175 + crash_generation_client_.reset(client.release()); 1.176 + } 1.177 + } 1.178 + 1.179 + if (!IsOutOfProcess()) { 1.180 + // Either client did not ask for out-of-process crash generation 1.181 + // or registration with the server process failed. In either case, 1.182 + // setup to do in-process crash generation. 1.183 + 1.184 + // Set synchronization primitives and the handler thread. Each 1.185 + // ExceptionHandler object gets its own handler thread because that's the 1.186 + // only way to reliably guarantee sufficient stack space in an exception, 1.187 + // and it allows an easy way to get a snapshot of the requesting thread's 1.188 + // context outside of an exception. 1.189 + InitializeCriticalSection(&handler_critical_section_); 1.190 + handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); 1.191 + assert(handler_start_semaphore_ != NULL); 1.192 + 1.193 + handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); 1.194 + assert(handler_finish_semaphore_ != NULL); 1.195 + 1.196 + // Don't attempt to create the thread if we could not create the semaphores. 1.197 + if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) { 1.198 + DWORD thread_id; 1.199 + handler_thread_ = CreateThread(NULL, // lpThreadAttributes 1.200 + kExceptionHandlerThreadInitialStackSize, 1.201 + ExceptionHandlerThreadMain, 1.202 + this, // lpParameter 1.203 + 0, // dwCreationFlags 1.204 + &thread_id); 1.205 + assert(handler_thread_ != NULL); 1.206 + } 1.207 + 1.208 + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); 1.209 + if (dbghelp_module_) { 1.210 + minidump_write_dump_ = reinterpret_cast<MiniDumpWriteDump_type>( 1.211 + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); 1.212 + } 1.213 + 1.214 + // Load this library dynamically to not affect existing projects. Most 1.215 + // projects don't link against this directly, it's usually dynamically 1.216 + // loaded by dependent code. 1.217 + rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); 1.218 + if (rpcrt4_module_) { 1.219 + uuid_create_ = reinterpret_cast<UuidCreate_type>( 1.220 + GetProcAddress(rpcrt4_module_, "UuidCreate")); 1.221 + } 1.222 + 1.223 + // set_dump_path calls UpdateNextID. This sets up all of the path and id 1.224 + // strings, and their equivalent c_str pointers. 1.225 + set_dump_path(dump_path); 1.226 + } 1.227 + 1.228 + // Reserve one element for the instruction memory 1.229 + AppMemory instruction_memory; 1.230 + instruction_memory.ptr = NULL; 1.231 + instruction_memory.length = 0; 1.232 + app_memory_info_.push_back(instruction_memory); 1.233 + 1.234 + // There is a race condition here. If the first instance has not yet 1.235 + // initialized the critical section, the second (and later) instances may 1.236 + // try to use uninitialized critical section object. The feature of multiple 1.237 + // instances in one module is not used much, so leave it as is for now. 1.238 + // One way to solve this in the current design (that is, keeping the static 1.239 + // handler stack) is to use spin locks with volatile bools to synchronize 1.240 + // the handler stack. This works only if the compiler guarantees to generate 1.241 + // cache coherent code for volatile. 1.242 + // TODO(munjal): Fix this in a better way by changing the design if possible. 1.243 + 1.244 + // Lazy initialization of the handler_stack_critical_section_ 1.245 + if (instance_count == 1) { 1.246 + InitializeCriticalSection(&handler_stack_critical_section_); 1.247 + } 1.248 + 1.249 + if (handler_types != HANDLER_NONE) { 1.250 + EnterCriticalSection(&handler_stack_critical_section_); 1.251 + 1.252 + // The first time an ExceptionHandler that installs a handler is 1.253 + // created, set up the handler stack. 1.254 + if (!handler_stack_) { 1.255 + handler_stack_ = new vector<ExceptionHandler*>(); 1.256 + } 1.257 + handler_stack_->push_back(this); 1.258 + 1.259 + if (handler_types & HANDLER_EXCEPTION) 1.260 + previous_filter_ = SetUnhandledExceptionFilter(HandleException); 1.261 + 1.262 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.263 + if (handler_types & HANDLER_INVALID_PARAMETER) 1.264 + previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); 1.265 +#endif // _MSC_VER >= 1400 1.266 + 1.267 + if (handler_types & HANDLER_PURECALL) 1.268 + previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); 1.269 + 1.270 + LeaveCriticalSection(&handler_stack_critical_section_); 1.271 + } 1.272 +} 1.273 + 1.274 +ExceptionHandler::~ExceptionHandler() { 1.275 + if (dbghelp_module_) { 1.276 + FreeLibrary(dbghelp_module_); 1.277 + } 1.278 + 1.279 + if (rpcrt4_module_) { 1.280 + FreeLibrary(rpcrt4_module_); 1.281 + } 1.282 + 1.283 + if (handler_types_ != HANDLER_NONE) { 1.284 + EnterCriticalSection(&handler_stack_critical_section_); 1.285 + 1.286 + if (handler_types_ & HANDLER_EXCEPTION) 1.287 + SetUnhandledExceptionFilter(previous_filter_); 1.288 + 1.289 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.290 + if (handler_types_ & HANDLER_INVALID_PARAMETER) 1.291 + _set_invalid_parameter_handler(previous_iph_); 1.292 +#endif // _MSC_VER >= 1400 1.293 + 1.294 + if (handler_types_ & HANDLER_PURECALL) 1.295 + _set_purecall_handler(previous_pch_); 1.296 + 1.297 + if (handler_stack_->back() == this) { 1.298 + handler_stack_->pop_back(); 1.299 + } else { 1.300 + // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the 1.301 + // system's application event log. 1.302 + fprintf(stderr, "warning: removing Breakpad handler out of order\n"); 1.303 + vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin(); 1.304 + while (iterator != handler_stack_->end()) { 1.305 + if (*iterator == this) { 1.306 + iterator = handler_stack_->erase(iterator); 1.307 + } else { 1.308 + ++iterator; 1.309 + } 1.310 + } 1.311 + } 1.312 + 1.313 + if (handler_stack_->empty()) { 1.314 + // When destroying the last ExceptionHandler that installed a handler, 1.315 + // clean up the handler stack. 1.316 + delete handler_stack_; 1.317 + handler_stack_ = NULL; 1.318 + } 1.319 + 1.320 + LeaveCriticalSection(&handler_stack_critical_section_); 1.321 + } 1.322 + 1.323 + // Some of the objects were only initialized if out of process 1.324 + // registration was not done. 1.325 + if (!IsOutOfProcess()) { 1.326 +#ifdef BREAKPAD_NO_TERMINATE_THREAD 1.327 + // Clean up the handler thread and synchronization primitives. The handler 1.328 + // thread is either waiting on the semaphore to handle a crash or it is 1.329 + // handling a crash. Coming out of the wait is fast but wait more in the 1.330 + // eventuality a crash is handled. This compilation option results in a 1.331 + // deadlock if the exception handler is destroyed while executing code 1.332 + // inside DllMain. 1.333 + is_shutdown_ = true; 1.334 + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); 1.335 + WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs); 1.336 +#else 1.337 + TerminateThread(handler_thread_, 1); 1.338 +#endif // BREAKPAD_NO_TERMINATE_THREAD 1.339 + 1.340 + CloseHandle(handler_thread_); 1.341 + handler_thread_ = NULL; 1.342 + DeleteCriticalSection(&handler_critical_section_); 1.343 + CloseHandle(handler_start_semaphore_); 1.344 + CloseHandle(handler_finish_semaphore_); 1.345 + } 1.346 + 1.347 + // There is a race condition in the code below: if this instance is 1.348 + // deleting the static critical section and a new instance of the class 1.349 + // is created, then there is a possibility that the critical section be 1.350 + // initialized while the same critical section is being deleted. Given the 1.351 + // usage pattern for the code, this race condition is unlikely to hit, but it 1.352 + // is a race condition nonetheless. 1.353 + if (InterlockedDecrement(&instance_count_) == 0) { 1.354 + DeleteCriticalSection(&handler_stack_critical_section_); 1.355 + } 1.356 +} 1.357 + 1.358 +bool ExceptionHandler::RequestUpload(DWORD crash_id) { 1.359 + return crash_generation_client_->RequestUpload(crash_id); 1.360 +} 1.361 + 1.362 +// static 1.363 +DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { 1.364 + ExceptionHandler* self = reinterpret_cast<ExceptionHandler *>(lpParameter); 1.365 + assert(self); 1.366 + assert(self->handler_start_semaphore_ != NULL); 1.367 + assert(self->handler_finish_semaphore_ != NULL); 1.368 + 1.369 + while (true) { 1.370 + if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) == 1.371 + WAIT_OBJECT_0) { 1.372 + // Perform the requested action. 1.373 + if (self->is_shutdown_) { 1.374 + // The instance of the exception handler is being destroyed. 1.375 + break; 1.376 + } else { 1.377 + self->handler_return_value_ = 1.378 + self->WriteMinidumpWithException(self->requesting_thread_id_, 1.379 + self->exception_info_, 1.380 + self->assertion_); 1.381 + } 1.382 + 1.383 + // Allow the requesting thread to proceed. 1.384 + ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL); 1.385 + } 1.386 + } 1.387 + 1.388 + // This statement is not reached when the thread is unconditionally 1.389 + // terminated by the ExceptionHandler destructor. 1.390 + return 0; 1.391 +} 1.392 + 1.393 +// HandleException and HandleInvalidParameter must create an 1.394 +// AutoExceptionHandler object to maintain static state and to determine which 1.395 +// ExceptionHandler instance to use. The constructor locates the correct 1.396 +// instance, and makes it available through get_handler(). The destructor 1.397 +// restores the state in effect prior to allocating the AutoExceptionHandler. 1.398 +class AutoExceptionHandler { 1.399 + public: 1.400 + AutoExceptionHandler() { 1.401 + // Increment handler_stack_index_ so that if another Breakpad handler is 1.402 + // registered using this same HandleException function, and it needs to be 1.403 + // called while this handler is running (either because this handler 1.404 + // declines to handle the exception, or an exception occurs during 1.405 + // handling), HandleException will find the appropriate ExceptionHandler 1.406 + // object in handler_stack_ to deliver the exception to. 1.407 + // 1.408 + // Because handler_stack_ is addressed in reverse (as |size - index|), 1.409 + // preincrementing handler_stack_index_ avoids needing to subtract 1 from 1.410 + // the argument to |at|. 1.411 + // 1.412 + // The index is maintained instead of popping elements off of the handler 1.413 + // stack and pushing them at the end of this method. This avoids ruining 1.414 + // the order of elements in the stack in the event that some other thread 1.415 + // decides to manipulate the handler stack (such as creating a new 1.416 + // ExceptionHandler object) while an exception is being handled. 1.417 + EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_); 1.418 + handler_ = ExceptionHandler::handler_stack_->at( 1.419 + ExceptionHandler::handler_stack_->size() - 1.420 + ++ExceptionHandler::handler_stack_index_); 1.421 + 1.422 + // In case another exception occurs while this handler is doing its thing, 1.423 + // it should be delivered to the previous filter. 1.424 + SetUnhandledExceptionFilter(handler_->previous_filter_); 1.425 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.426 + _set_invalid_parameter_handler(handler_->previous_iph_); 1.427 +#endif // _MSC_VER >= 1400 1.428 + _set_purecall_handler(handler_->previous_pch_); 1.429 + } 1.430 + 1.431 + ~AutoExceptionHandler() { 1.432 + // Put things back the way they were before entering this handler. 1.433 + SetUnhandledExceptionFilter(ExceptionHandler::HandleException); 1.434 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.435 + _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter); 1.436 +#endif // _MSC_VER >= 1400 1.437 + _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); 1.438 + 1.439 + --ExceptionHandler::handler_stack_index_; 1.440 + LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); 1.441 + } 1.442 + 1.443 + ExceptionHandler* get_handler() const { return handler_; } 1.444 + 1.445 + private: 1.446 + ExceptionHandler* handler_; 1.447 +}; 1.448 + 1.449 +// static 1.450 +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) { 1.451 + AutoExceptionHandler auto_exception_handler; 1.452 + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); 1.453 + 1.454 + // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This 1.455 + // logic will short-circuit before calling WriteMinidumpOnHandlerThread, 1.456 + // allowing something else to handle the breakpoint without incurring the 1.457 + // overhead transitioning to and from the handler thread. This behavior 1.458 + // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions. 1.459 + DWORD code = exinfo->ExceptionRecord->ExceptionCode; 1.460 + LONG action; 1.461 + bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) || 1.462 + (code == EXCEPTION_SINGLE_STEP); 1.463 + 1.464 + bool success = false; 1.465 + 1.466 + if (!is_debug_exception || 1.467 + current_handler->get_handle_debug_exceptions()) { 1.468 + // If out-of-proc crash handler client is available, we have to use that 1.469 + // to generate dump and we cannot fall back on in-proc dump generation 1.470 + // because we never prepared for an in-proc dump generation 1.471 + 1.472 + // In case of out-of-process dump generation, directly call 1.473 + // WriteMinidumpWithException since there is no separate thread running. 1.474 + if (current_handler->IsOutOfProcess()) { 1.475 + success = current_handler->WriteMinidumpWithException( 1.476 + GetCurrentThreadId(), 1.477 + exinfo, 1.478 + NULL); 1.479 + } else { 1.480 + success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL); 1.481 + } 1.482 + } 1.483 + 1.484 + // The handler fully handled the exception. Returning 1.485 + // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually 1.486 + // results in the application being terminated. 1.487 + // 1.488 + // Note: If the application was launched from within the Cygwin 1.489 + // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the 1.490 + // application to be restarted. 1.491 + if (success) { 1.492 + action = EXCEPTION_EXECUTE_HANDLER; 1.493 + } else { 1.494 + // There was an exception, it was a breakpoint or something else ignored 1.495 + // above, or it was passed to the handler, which decided not to handle it. 1.496 + // This could be because the filter callback didn't want it, because 1.497 + // minidump writing failed for some reason, or because the post-minidump 1.498 + // callback function indicated failure. Give the previous handler a 1.499 + // chance to do something with the exception. If there is no previous 1.500 + // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger 1.501 + // or native "crashed" dialog to handle the exception. 1.502 + if (current_handler->previous_filter_) { 1.503 + action = current_handler->previous_filter_(exinfo); 1.504 + } else { 1.505 + action = EXCEPTION_CONTINUE_SEARCH; 1.506 + } 1.507 + } 1.508 + 1.509 + return action; 1.510 +} 1.511 + 1.512 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.513 +// static 1.514 +void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, 1.515 + const wchar_t* function, 1.516 + const wchar_t* file, 1.517 + unsigned int line, 1.518 + uintptr_t reserved) { 1.519 + // This is an invalid parameter, not an exception. It's safe to play with 1.520 + // sprintf here. 1.521 + AutoExceptionHandler auto_exception_handler; 1.522 + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); 1.523 + 1.524 + MDRawAssertionInfo assertion; 1.525 + memset(&assertion, 0, sizeof(assertion)); 1.526 + _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.expression), 1.527 + sizeof(assertion.expression) / sizeof(assertion.expression[0]), 1.528 + _TRUNCATE, L"%s", expression); 1.529 + _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.function), 1.530 + sizeof(assertion.function) / sizeof(assertion.function[0]), 1.531 + _TRUNCATE, L"%s", function); 1.532 + _snwprintf_s(reinterpret_cast<wchar_t*>(assertion.file), 1.533 + sizeof(assertion.file) / sizeof(assertion.file[0]), 1.534 + _TRUNCATE, L"%s", file); 1.535 + assertion.line = line; 1.536 + assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER; 1.537 + 1.538 + // Make up an exception record for the current thread and CPU context 1.539 + // to make it possible for the crash processor to classify these 1.540 + // as do regular crashes, and to make it humane for developers to 1.541 + // analyze them. 1.542 + EXCEPTION_RECORD exception_record = {}; 1.543 + CONTEXT exception_context = {}; 1.544 + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; 1.545 + 1.546 + ::RtlCaptureContext(&exception_context); 1.547 + 1.548 + exception_record.ExceptionCode = STATUS_INVALID_PARAMETER; 1.549 + 1.550 + // We store pointers to the the expression and function strings, 1.551 + // and the line as exception parameters to make them easy to 1.552 + // access by the developer on the far side. 1.553 + exception_record.NumberParameters = 3; 1.554 + exception_record.ExceptionInformation[0] = 1.555 + reinterpret_cast<ULONG_PTR>(&assertion.expression); 1.556 + exception_record.ExceptionInformation[1] = 1.557 + reinterpret_cast<ULONG_PTR>(&assertion.file); 1.558 + exception_record.ExceptionInformation[2] = assertion.line; 1.559 + 1.560 + bool success = false; 1.561 + // In case of out-of-process dump generation, directly call 1.562 + // WriteMinidumpWithException since there is no separate thread running. 1.563 + if (current_handler->IsOutOfProcess()) { 1.564 + success = current_handler->WriteMinidumpWithException( 1.565 + GetCurrentThreadId(), 1.566 + &exception_ptrs, 1.567 + &assertion); 1.568 + } else { 1.569 + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, 1.570 + &assertion); 1.571 + } 1.572 + 1.573 + if (!success) { 1.574 + if (current_handler->previous_iph_) { 1.575 + // The handler didn't fully handle the exception. Give it to the 1.576 + // previous invalid parameter handler. 1.577 + current_handler->previous_iph_(expression, 1.578 + function, 1.579 + file, 1.580 + line, 1.581 + reserved); 1.582 + } else { 1.583 + // If there's no previous handler, pass the exception back in to the 1.584 + // invalid parameter handler's core. That's the routine that called this 1.585 + // function, but now, since this function is no longer registered (and in 1.586 + // fact, no function at all is registered), this will result in the 1.587 + // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson. 1.588 + // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes 1.589 + // more information through. In non-debug builds, it is not available, 1.590 + // so fall back to using _invalid_parameter_noinfo. See invarg.c in the 1.591 + // CRT source. 1.592 +#ifdef _DEBUG 1.593 + _invalid_parameter(expression, function, file, line, reserved); 1.594 +#else // _DEBUG 1.595 + _invalid_parameter_noinfo(); 1.596 +#endif // _DEBUG 1.597 + } 1.598 + } 1.599 + 1.600 + // The handler either took care of the invalid parameter problem itself, 1.601 + // or passed it on to another handler. "Swallow" it by exiting, paralleling 1.602 + // the behavior of "swallowing" exceptions. 1.603 + exit(0); 1.604 +} 1.605 +#endif // _MSC_VER >= 1400 1.606 + 1.607 +// static 1.608 +void ExceptionHandler::HandlePureVirtualCall() { 1.609 + // This is an pure virtual function call, not an exception. It's safe to 1.610 + // play with sprintf here. 1.611 + AutoExceptionHandler auto_exception_handler; 1.612 + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); 1.613 + 1.614 + MDRawAssertionInfo assertion; 1.615 + memset(&assertion, 0, sizeof(assertion)); 1.616 + assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL; 1.617 + 1.618 + // Make up an exception record for the current thread and CPU context 1.619 + // to make it possible for the crash processor to classify these 1.620 + // as do regular crashes, and to make it humane for developers to 1.621 + // analyze them. 1.622 + EXCEPTION_RECORD exception_record = {}; 1.623 + CONTEXT exception_context = {}; 1.624 + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; 1.625 + 1.626 + ::RtlCaptureContext(&exception_context); 1.627 + 1.628 + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; 1.629 + 1.630 + // We store pointers to the the expression and function strings, 1.631 + // and the line as exception parameters to make them easy to 1.632 + // access by the developer on the far side. 1.633 + exception_record.NumberParameters = 3; 1.634 + exception_record.ExceptionInformation[0] = 1.635 + reinterpret_cast<ULONG_PTR>(&assertion.expression); 1.636 + exception_record.ExceptionInformation[1] = 1.637 + reinterpret_cast<ULONG_PTR>(&assertion.file); 1.638 + exception_record.ExceptionInformation[2] = assertion.line; 1.639 + 1.640 + bool success = false; 1.641 + // In case of out-of-process dump generation, directly call 1.642 + // WriteMinidumpWithException since there is no separate thread running. 1.643 + 1.644 + if (current_handler->IsOutOfProcess()) { 1.645 + success = current_handler->WriteMinidumpWithException( 1.646 + GetCurrentThreadId(), 1.647 + &exception_ptrs, 1.648 + &assertion); 1.649 + } else { 1.650 + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, 1.651 + &assertion); 1.652 + } 1.653 + 1.654 + if (!success) { 1.655 + if (current_handler->previous_pch_) { 1.656 + // The handler didn't fully handle the exception. Give it to the 1.657 + // previous purecall handler. 1.658 + current_handler->previous_pch_(); 1.659 + } else { 1.660 + // If there's no previous handler, return and let _purecall handle it. 1.661 + // This will just put up an assertion dialog. 1.662 + return; 1.663 + } 1.664 + } 1.665 + 1.666 + // The handler either took care of the invalid parameter problem itself, 1.667 + // or passed it on to another handler. "Swallow" it by exiting, paralleling 1.668 + // the behavior of "swallowing" exceptions. 1.669 + exit(0); 1.670 +} 1.671 + 1.672 +bool ExceptionHandler::WriteMinidumpOnHandlerThread( 1.673 + EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) { 1.674 + EnterCriticalSection(&handler_critical_section_); 1.675 + 1.676 + // There isn't much we can do if the handler thread 1.677 + // was not successfully created. 1.678 + if (handler_thread_ == NULL) { 1.679 + LeaveCriticalSection(&handler_critical_section_); 1.680 + return false; 1.681 + } 1.682 + 1.683 + // The handler thread should only be created when the semaphores are valid. 1.684 + assert(handler_start_semaphore_ != NULL); 1.685 + assert(handler_finish_semaphore_ != NULL); 1.686 + 1.687 + // Set up data to be passed in to the handler thread. 1.688 + requesting_thread_id_ = GetCurrentThreadId(); 1.689 + exception_info_ = exinfo; 1.690 + assertion_ = assertion; 1.691 + 1.692 + // This causes the handler thread to call WriteMinidumpWithException. 1.693 + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); 1.694 + 1.695 + // Wait until WriteMinidumpWithException is done and collect its return value. 1.696 + WaitForSingleObject(handler_finish_semaphore_, INFINITE); 1.697 + bool status = handler_return_value_; 1.698 + 1.699 + // Clean up. 1.700 + requesting_thread_id_ = 0; 1.701 + exception_info_ = NULL; 1.702 + assertion_ = NULL; 1.703 + 1.704 + LeaveCriticalSection(&handler_critical_section_); 1.705 + 1.706 + return status; 1.707 +} 1.708 + 1.709 +bool ExceptionHandler::WriteMinidump() { 1.710 + // Make up an exception record for the current thread and CPU context 1.711 + // to make it possible for the crash processor to classify these 1.712 + // as do regular crashes, and to make it humane for developers to 1.713 + // analyze them. 1.714 + EXCEPTION_RECORD exception_record = {}; 1.715 + CONTEXT exception_context = {}; 1.716 + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; 1.717 + 1.718 + ::RtlCaptureContext(&exception_context); 1.719 + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; 1.720 + 1.721 + return WriteMinidumpForException(&exception_ptrs); 1.722 +} 1.723 + 1.724 +bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { 1.725 + // In case of out-of-process dump generation, directly call 1.726 + // WriteMinidumpWithException since there is no separate thread running. 1.727 + if (IsOutOfProcess()) { 1.728 + return WriteMinidumpWithException(GetCurrentThreadId(), 1.729 + exinfo, 1.730 + NULL); 1.731 + } 1.732 + 1.733 + bool success = WriteMinidumpOnHandlerThread(exinfo, NULL); 1.734 + UpdateNextID(); 1.735 + return success; 1.736 +} 1.737 + 1.738 +// static 1.739 +bool ExceptionHandler::WriteMinidump(const wstring &dump_path, 1.740 + MinidumpCallback callback, 1.741 + void* callback_context) { 1.742 + ExceptionHandler handler(dump_path, NULL, callback, callback_context, 1.743 + HANDLER_NONE); 1.744 + return handler.WriteMinidump(); 1.745 +} 1.746 + 1.747 +// static 1.748 +bool ExceptionHandler::WriteMinidumpForChild(HANDLE child, 1.749 + DWORD child_blamed_thread, 1.750 + const wstring& dump_path, 1.751 + MinidumpCallback callback, 1.752 + void* callback_context) { 1.753 + EXCEPTION_RECORD ex; 1.754 + CONTEXT ctx; 1.755 + EXCEPTION_POINTERS exinfo = { NULL, NULL }; 1.756 + DWORD last_suspend_count = kFailedToSuspendThread; 1.757 + HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT | 1.758 + THREAD_QUERY_INFORMATION | 1.759 + THREAD_SUSPEND_RESUME, 1.760 + FALSE, 1.761 + child_blamed_thread); 1.762 + // This thread may have died already, so not opening the handle is a 1.763 + // non-fatal error. 1.764 + if (child_thread_handle != NULL) { 1.765 + last_suspend_count = SuspendThread(child_thread_handle); 1.766 + if (last_suspend_count != kFailedToSuspendThread) { 1.767 + ctx.ContextFlags = CONTEXT_ALL; 1.768 + if (GetThreadContext(child_thread_handle, &ctx)) { 1.769 + memset(&ex, 0, sizeof(ex)); 1.770 + ex.ExceptionCode = EXCEPTION_BREAKPOINT; 1.771 +#if defined(_M_IX86) 1.772 + ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Eip); 1.773 +#elif defined(_M_X64) 1.774 + ex.ExceptionAddress = reinterpret_cast<PVOID>(ctx.Rip); 1.775 +#endif 1.776 + exinfo.ExceptionRecord = &ex; 1.777 + exinfo.ContextRecord = &ctx; 1.778 + } 1.779 + } 1.780 + } 1.781 + 1.782 + ExceptionHandler handler(dump_path, NULL, callback, callback_context, 1.783 + HANDLER_NONE); 1.784 + bool success = handler.WriteMinidumpWithExceptionForProcess( 1.785 + child_blamed_thread, 1.786 + exinfo.ExceptionRecord ? &exinfo : NULL, 1.787 + NULL, child, false); 1.788 + 1.789 + if (last_suspend_count != kFailedToSuspendThread) { 1.790 + ResumeThread(child_thread_handle); 1.791 + } 1.792 + 1.793 + CloseHandle(child_thread_handle); 1.794 + 1.795 + if (callback) { 1.796 + success = callback(handler.dump_path_c_, handler.next_minidump_id_c_, 1.797 + callback_context, NULL, NULL, success); 1.798 + } 1.799 + 1.800 + return success; 1.801 +} 1.802 + 1.803 +bool ExceptionHandler::WriteMinidumpWithException( 1.804 + DWORD requesting_thread_id, 1.805 + EXCEPTION_POINTERS* exinfo, 1.806 + MDRawAssertionInfo* assertion) { 1.807 + // Give user code a chance to approve or prevent writing a minidump. If the 1.808 + // filter returns false, don't handle the exception at all. If this method 1.809 + // was called as a result of an exception, returning false will cause 1.810 + // HandleException to call any previous handler or return 1.811 + // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear 1.812 + // as though this handler were not present at all. 1.813 + if (filter_ && !filter_(callback_context_, exinfo, assertion)) { 1.814 + return false; 1.815 + } 1.816 + 1.817 + bool success = false; 1.818 + if (IsOutOfProcess()) { 1.819 + success = crash_generation_client_->RequestDump(exinfo, assertion); 1.820 + } else { 1.821 + success = WriteMinidumpWithExceptionForProcess(requesting_thread_id, 1.822 + exinfo, 1.823 + assertion, 1.824 + GetCurrentProcess(), 1.825 + true); 1.826 + } 1.827 + 1.828 + if (callback_) { 1.829 + // TODO(munjal): In case of out-of-process dump generation, both 1.830 + // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process 1.831 + // scenario, the server process ends up creating the dump path and dump 1.832 + // id so they are not known to the client. 1.833 + success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_, 1.834 + exinfo, assertion, success); 1.835 + } 1.836 + 1.837 + return success; 1.838 +} 1.839 + 1.840 +// static 1.841 +BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( 1.842 + PVOID context, 1.843 + const PMINIDUMP_CALLBACK_INPUT callback_input, 1.844 + PMINIDUMP_CALLBACK_OUTPUT callback_output) { 1.845 + switch (callback_input->CallbackType) { 1.846 + case MemoryCallback: { 1.847 + MinidumpCallbackContext* callback_context = 1.848 + reinterpret_cast<MinidumpCallbackContext*>(context); 1.849 + if (callback_context->iter == callback_context->end) 1.850 + return FALSE; 1.851 + 1.852 + // Include the specified memory region. 1.853 + callback_output->MemoryBase = callback_context->iter->ptr; 1.854 + callback_output->MemorySize = callback_context->iter->length; 1.855 + callback_context->iter++; 1.856 + return TRUE; 1.857 + } 1.858 + 1.859 + // Include all modules. 1.860 + case IncludeModuleCallback: 1.861 + case ModuleCallback: 1.862 + return TRUE; 1.863 + 1.864 + // Include all threads. 1.865 + case IncludeThreadCallback: 1.866 + case ThreadCallback: 1.867 + return TRUE; 1.868 + 1.869 + // Stop receiving cancel callbacks. 1.870 + case CancelCallback: 1.871 + callback_output->CheckCancel = FALSE; 1.872 + callback_output->Cancel = FALSE; 1.873 + return TRUE; 1.874 + } 1.875 + // Ignore other callback types. 1.876 + return FALSE; 1.877 +} 1.878 + 1.879 +bool ExceptionHandler::WriteMinidumpWithExceptionForProcess( 1.880 + DWORD requesting_thread_id, 1.881 + EXCEPTION_POINTERS* exinfo, 1.882 + MDRawAssertionInfo* assertion, 1.883 + HANDLE process, 1.884 + bool write_requester_stream) { 1.885 + bool success = false; 1.886 + if (minidump_write_dump_) { 1.887 + HANDLE dump_file = CreateFile(next_minidump_path_c_, 1.888 + GENERIC_WRITE, 1.889 + 0, // no sharing 1.890 + NULL, 1.891 + CREATE_NEW, // fail if exists 1.892 + FILE_ATTRIBUTE_NORMAL, 1.893 + NULL); 1.894 + if (dump_file != INVALID_HANDLE_VALUE) { 1.895 + MINIDUMP_EXCEPTION_INFORMATION except_info; 1.896 + except_info.ThreadId = requesting_thread_id; 1.897 + except_info.ExceptionPointers = exinfo; 1.898 + except_info.ClientPointers = FALSE; 1.899 + 1.900 + // Leave room in user_stream_array for possible breakpad and 1.901 + // assertion info streams. 1.902 + MINIDUMP_USER_STREAM user_stream_array[2]; 1.903 + MINIDUMP_USER_STREAM_INFORMATION user_streams; 1.904 + user_streams.UserStreamCount = 0; 1.905 + user_streams.UserStreamArray = user_stream_array; 1.906 + 1.907 + if (write_requester_stream) { 1.908 + // Add an MDRawBreakpadInfo stream to the minidump, to provide 1.909 + // additional information about the exception handler to the Breakpad 1.910 + // processor. The information will help the processor determine which 1.911 + // threads are relevant. The Breakpad processor does not require this 1.912 + // information but can function better with Breakpad-generated dumps 1.913 + // when it is present. The native debugger is not harmed by the 1.914 + // presence of this information. 1.915 + MDRawBreakpadInfo breakpad_info; 1.916 + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | 1.917 + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; 1.918 + breakpad_info.dump_thread_id = GetCurrentThreadId(); 1.919 + breakpad_info.requesting_thread_id = requesting_thread_id; 1.920 + 1.921 + int index = user_streams.UserStreamCount; 1.922 + user_stream_array[index].Type = MD_BREAKPAD_INFO_STREAM; 1.923 + user_stream_array[index].BufferSize = sizeof(breakpad_info); 1.924 + user_stream_array[index].Buffer = &breakpad_info; 1.925 + ++user_streams.UserStreamCount; 1.926 + } 1.927 + 1.928 + if (assertion) { 1.929 + int index = user_streams.UserStreamCount; 1.930 + user_stream_array[index].Type = MD_ASSERTION_INFO_STREAM; 1.931 + user_stream_array[index].BufferSize = sizeof(MDRawAssertionInfo); 1.932 + user_stream_array[index].Buffer = assertion; 1.933 + ++user_streams.UserStreamCount; 1.934 + } 1.935 + 1.936 + // Older versions of DbgHelp.dll don't correctly put the memory around 1.937 + // the faulting instruction pointer into the minidump. This 1.938 + // callback will ensure that it gets included. 1.939 + if (exinfo) { 1.940 + // Find a memory region of 256 bytes centered on the 1.941 + // faulting instruction pointer. 1.942 + const ULONG64 instruction_pointer = 1.943 +#if defined(_M_IX86) 1.944 + exinfo->ContextRecord->Eip; 1.945 +#elif defined(_M_AMD64) 1.946 + exinfo->ContextRecord->Rip; 1.947 +#else 1.948 +#error Unsupported platform 1.949 +#endif 1.950 + 1.951 + MEMORY_BASIC_INFORMATION info; 1.952 + if (VirtualQueryEx(process, 1.953 + reinterpret_cast<LPCVOID>(instruction_pointer), 1.954 + &info, 1.955 + sizeof(MEMORY_BASIC_INFORMATION)) != 0 && 1.956 + info.State == MEM_COMMIT) { 1.957 + // Attempt to get 128 bytes before and after the instruction 1.958 + // pointer, but settle for whatever's available up to the 1.959 + // boundaries of the memory region. 1.960 + const ULONG64 kIPMemorySize = 256; 1.961 + ULONG64 base = 1.962 + (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress), 1.963 + instruction_pointer - (kIPMemorySize / 2)); 1.964 + ULONG64 end_of_range = 1.965 + (std::min)(instruction_pointer + (kIPMemorySize / 2), 1.966 + reinterpret_cast<ULONG64>(info.BaseAddress) 1.967 + + info.RegionSize); 1.968 + ULONG size = static_cast<ULONG>(end_of_range - base); 1.969 + 1.970 + AppMemory& elt = app_memory_info_.front(); 1.971 + elt.ptr = base; 1.972 + elt.length = size; 1.973 + } 1.974 + } 1.975 + 1.976 + MinidumpCallbackContext context; 1.977 + context.iter = app_memory_info_.begin(); 1.978 + context.end = app_memory_info_.end(); 1.979 + 1.980 + // Skip the reserved element if there was no instruction memory 1.981 + if (context.iter->ptr == 0) { 1.982 + context.iter++; 1.983 + } 1.984 + 1.985 + MINIDUMP_CALLBACK_INFORMATION callback; 1.986 + callback.CallbackRoutine = MinidumpWriteDumpCallback; 1.987 + callback.CallbackParam = reinterpret_cast<void*>(&context); 1.988 + 1.989 + // The explicit comparison to TRUE avoids a warning (C4800). 1.990 + success = (minidump_write_dump_(process, 1.991 + GetProcessId(process), 1.992 + dump_file, 1.993 + dump_type_, 1.994 + exinfo ? &except_info : NULL, 1.995 + &user_streams, 1.996 + &callback) == TRUE); 1.997 + 1.998 + CloseHandle(dump_file); 1.999 + } 1.1000 + } 1.1001 + 1.1002 + return success; 1.1003 +} 1.1004 + 1.1005 +void ExceptionHandler::UpdateNextID() { 1.1006 + assert(uuid_create_); 1.1007 + UUID id = {0}; 1.1008 + if (uuid_create_) { 1.1009 + uuid_create_(&id); 1.1010 + } 1.1011 + next_minidump_id_ = GUIDString::GUIDToWString(&id); 1.1012 + next_minidump_id_c_ = next_minidump_id_.c_str(); 1.1013 + 1.1014 + wchar_t minidump_path[MAX_PATH]; 1.1015 + swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp", 1.1016 + dump_path_c_, next_minidump_id_c_); 1.1017 + 1.1018 + // remove when VC++7.1 is no longer supported 1.1019 + minidump_path[MAX_PATH - 1] = L'\0'; 1.1020 + 1.1021 + next_minidump_path_ = minidump_path; 1.1022 + next_minidump_path_c_ = next_minidump_path_.c_str(); 1.1023 +} 1.1024 + 1.1025 +void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { 1.1026 + AppMemoryList::iterator iter = 1.1027 + std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr); 1.1028 + if (iter != app_memory_info_.end()) { 1.1029 + // Don't allow registering the same pointer twice. 1.1030 + return; 1.1031 + } 1.1032 + 1.1033 + AppMemory app_memory; 1.1034 + app_memory.ptr = reinterpret_cast<ULONG64>(ptr); 1.1035 + app_memory.length = static_cast<ULONG>(length); 1.1036 + app_memory_info_.push_back(app_memory); 1.1037 +} 1.1038 + 1.1039 +void ExceptionHandler::UnregisterAppMemory(void* ptr) { 1.1040 + AppMemoryList::iterator iter = 1.1041 + std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr); 1.1042 + if (iter != app_memory_info_.end()) { 1.1043 + app_memory_info_.erase(iter); 1.1044 + } 1.1045 +} 1.1046 + 1.1047 +} // namespace google_breakpad