diff options
Diffstat (limited to 'toolkit/crashreporter/breakpad-client/windows/common')
5 files changed, 673 insertions, 0 deletions
diff --git a/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h b/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h new file mode 100644 index 0000000000..3fd4b9b7e6 --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/auto_critical_section.h @@ -0,0 +1,81 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ +#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ + +#include <windows.h> + +namespace google_breakpad { + +// Automatically enters the critical section in the constructor and leaves +// the critical section in the destructor. +class AutoCriticalSection { + public: + // Creates a new instance with the given critical section object + // and enters the critical section immediately. + explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs), taken_(false) { + assert(cs_); + Acquire(); + } + + // Destructor: leaves the critical section. + ~AutoCriticalSection() { + if (taken_) { + Release(); + } + } + + // Enters the critical section. Recursive Acquire() calls are not allowed. + void Acquire() { + assert(!taken_); + EnterCriticalSection(cs_); + taken_ = true; + } + + // Leaves the critical section. The caller should not call Release() unless + // the critical seciton has been entered already. + void Release() { + assert(taken_); + taken_ = false; + LeaveCriticalSection(cs_); + } + + private: + // Disable copy ctor and operator=. + AutoCriticalSection(const AutoCriticalSection&); + AutoCriticalSection& operator=(const AutoCriticalSection&); + + CRITICAL_SECTION* cs_; + bool taken_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ diff --git a/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h b/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h new file mode 100644 index 0000000000..c74868198c --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/ipc_protocol.h @@ -0,0 +1,181 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ +#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ + +#include <windows.h> +#include <dbghelp.h> +#include <string> +#include <utility> +#include "common/windows/string_utils-inl.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Name/value pair for custom client information. +struct CustomInfoEntry { + // Maximum length for name and value for client custom info. + static const int kNameMaxLength = 64; + static const int kValueMaxLength = 64; + + CustomInfoEntry() { + // Putting name and value in initializer list makes VC++ show warning 4351. + set_name(NULL); + set_value(NULL); + } + + CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + void set_name(const wchar_t* name_arg) { + if (!name_arg) { + name[0] = L'\0'; + return; + } + WindowsStringUtils::safe_wcscpy(name, kNameMaxLength, name_arg); + } + + void set_value(const wchar_t* value_arg) { + if (!value_arg) { + value[0] = L'\0'; + return; + } + + WindowsStringUtils::safe_wcscpy(value, kValueMaxLength, value_arg); + } + + void set(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + wchar_t name[kNameMaxLength]; + wchar_t value[kValueMaxLength]; +}; + +// Constants for the protocol between client and the server. + +// Tags sent with each message indicating the purpose of +// the message. +enum MessageTag { + MESSAGE_TAG_NONE = 0, + MESSAGE_TAG_REGISTRATION_REQUEST = 1, + MESSAGE_TAG_REGISTRATION_RESPONSE = 2, + MESSAGE_TAG_REGISTRATION_ACK = 3, + MESSAGE_TAG_UPLOAD_REQUEST = 4 +}; + +struct CustomClientInfo { + const CustomInfoEntry* entries; + size_t count; +}; + +// Message structure for IPC between crash client and crash server. +struct ProtocolMessage { + ProtocolMessage() + : tag(MESSAGE_TAG_NONE), + id(0), + dump_type(MiniDumpNormal), + thread_id(0), + exception_pointers(NULL), + assert_info(NULL), + custom_client_info(), + dump_request_handle(NULL), + dump_generated_handle(NULL), + server_alive_handle(NULL) { + } + + // Creates an instance with the given parameters. + ProtocolMessage(MessageTag arg_tag, + DWORD arg_id, + MINIDUMP_TYPE arg_dump_type, + DWORD* arg_thread_id, + EXCEPTION_POINTERS** arg_exception_pointers, + MDRawAssertionInfo* arg_assert_info, + const CustomClientInfo& custom_info, + HANDLE arg_dump_request_handle, + HANDLE arg_dump_generated_handle, + HANDLE arg_server_alive) + : tag(arg_tag), + id(arg_id), + dump_type(arg_dump_type), + thread_id(arg_thread_id), + exception_pointers(arg_exception_pointers), + assert_info(arg_assert_info), + custom_client_info(custom_info), + dump_request_handle(arg_dump_request_handle), + dump_generated_handle(arg_dump_generated_handle), + server_alive_handle(arg_server_alive) { + } + + // Tag in the message. + MessageTag tag; + + // The id for this message. This may be either a process id or a crash id + // depending on the type of message. + DWORD id; + + // Dump type requested. + MINIDUMP_TYPE dump_type; + + // Client thread id pointer. + DWORD* thread_id; + + // Exception information. + EXCEPTION_POINTERS** exception_pointers; + + // Assert information in case of an invalid parameter or + // pure call failure. + MDRawAssertionInfo* assert_info; + + // Custom client information. + CustomClientInfo custom_client_info; + + // Handle to signal the crash event. + HANDLE dump_request_handle; + + // Handle to check if server is done generating crash. + HANDLE dump_generated_handle; + + // Handle to a mutex that becomes signaled (WAIT_ABANDONED) + // if server process goes down. + HANDLE server_alive_handle; + + private: + // Disable copy ctor and operator=. + ProtocolMessage(const ProtocolMessage& msg); + ProtocolMessage& operator=(const ProtocolMessage& msg); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ diff --git a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc new file mode 100644 index 0000000000..54d8e25274 --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.cc @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "minidump_callback.h" + +#include <winternl.h> + +#include <algorithm> +#include <cassert> + +namespace google_breakpad { + +static const DWORD sHeapRegionSize= 1024; +static DWORD sPageSize = 0; + +using NtQueryInformationThreadFunc = decltype(::NtQueryInformationThread); +static NtQueryInformationThreadFunc* sNtQueryInformationThread = nullptr; + + +namespace { +enum { + ThreadBasicInformation, +}; + +struct CLIENT_ID { + PVOID UniqueProcess; + PVOID UniqueThread; +}; + +struct THREAD_BASIC_INFORMATION { + NTSTATUS ExitStatus; + PVOID TebBaseAddress; + CLIENT_ID ClientId; + KAFFINITY AffMask; + DWORD Priority; + DWORD BasePriority; +}; +} + +void InitAppMemoryInternal() +{ + if (!sPageSize) { + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + sPageSize = systemInfo.dwPageSize; + } + + if (!sNtQueryInformationThread) { + sNtQueryInformationThread = (NtQueryInformationThreadFunc*) + (::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), + "NtQueryInformationThread")); + } +} + +bool GetAppMemoryFromRegister(HANDLE aProcess, + const NT_TIB* aTib, + RegisterValueType aRegister, + AppMemory* aResult) +{ + static_assert(sizeof(RegisterValueType) == sizeof(void*), + "Size mismatch between DWORD/DWORD64 and void*"); + + if (!sPageSize) { + // GetSystemInfo() should not fail, but bail out just in case it fails. + return false; + } + + RegisterValueType addr = aRegister; + addr &= ~(static_cast<RegisterValueType>(sPageSize) - 1); + + if (aTib) { + if (aRegister >= (RegisterValueType)aTib->StackLimit && + aRegister <= (RegisterValueType)aTib->StackBase) { + // aRegister points to the stack. + return false; + } + } + + MEMORY_BASIC_INFORMATION memInfo; + memset(&memInfo, 0, sizeof(memInfo)); + SIZE_T rv = ::VirtualQueryEx(aProcess, + reinterpret_cast<void*>(addr), + &memInfo, + sizeof(memInfo)); + if (!rv) { + // VirtualQuery fails: aAddr is not on heap. + return false; + } + + // Check protection and type of the memory region. Include the region if it's + // 1. read-write: heap, or + // 2. read-executable and private: likely to be JIT code. + if (memInfo.Protect != PAGE_READWRITE && + memInfo.Protect != PAGE_EXECUTE_READ) { + return false; + } + + // Try to include a region of size sHeapRegionSize around aRegister, bounded + // by the [BaseAddress, BaseAddress + RegionSize]. + RegisterValueType lower = + std::max(aRegister - sHeapRegionSize / 2, + reinterpret_cast<RegisterValueType>(memInfo.BaseAddress)); + + RegisterValueType upper = + std::min(lower + sHeapRegionSize, + reinterpret_cast<RegisterValueType>(memInfo.BaseAddress) + + memInfo.RegionSize); + + aResult->ptr = lower; + aResult->length = upper - lower; + + return true; +} + +static AppMemoryList::iterator +FindNextPreallocated(AppMemoryList& aList, AppMemoryList::iterator aBegin) { + auto it = aBegin; + for (auto it = aBegin; it != aList.end(); it++) { + if (it->preallocated) { + return it; + } + } + + assert(it == aList.end()); + return it; +} + +static bool +GetThreadTib(HANDLE aProcess, DWORD aThreadId, NT_TIB* aTib) { + HANDLE threadHandle = ::OpenThread(THREAD_QUERY_INFORMATION, + FALSE, + aThreadId); + if (!threadHandle) { + return false; + } + + if (!sNtQueryInformationThread) { + return false; + } + + THREAD_BASIC_INFORMATION threadInfo; + auto status = (*sNtQueryInformationThread)(threadHandle, + (THREADINFOCLASS)ThreadBasicInformation, + &threadInfo, + sizeof(threadInfo), + NULL); + if (!NT_SUCCESS(status)) { + return false; + } + + auto readSuccess = ::ReadProcessMemory(aProcess, + threadInfo.TebBaseAddress, + aTib, + sizeof(*aTib), + NULL); + if (!readSuccess) { + return false; + } + + ::CloseHandle(threadHandle); + return true; +} + +void IncludeAppMemoryFromExceptionContext(HANDLE aProcess, + DWORD aThreadId, + AppMemoryList& aList, + PCONTEXT aExceptionContext, + bool aInstructionPointerOnly) { + RegisterValueType heapAddrCandidates[kExceptionAppMemoryRegions]; + size_t numElements = 0; + + NT_TIB tib; + memset(&tib, 0, sizeof(tib)); + if (!GetThreadTib(aProcess, aThreadId, &tib)) { + // Fail to query thread stack range: only safe to include the region around + // the instruction pointer. + aInstructionPointerOnly = true; + } + + // Add registers that might have a heap address to heapAddrCandidates. + // Note that older versions of DbgHelp.dll don't correctly put the memory + // around the faulting instruction pointer into the minidump. Include Rip/Eip + // unconditionally ensures it gets included. +#if defined(_M_IX86) + if (!aInstructionPointerOnly) { + heapAddrCandidates[numElements++] = aExceptionContext->Eax; + heapAddrCandidates[numElements++] = aExceptionContext->Ebx; + heapAddrCandidates[numElements++] = aExceptionContext->Ecx; + heapAddrCandidates[numElements++] = aExceptionContext->Edx; + heapAddrCandidates[numElements++] = aExceptionContext->Esi; + heapAddrCandidates[numElements++] = aExceptionContext->Edi; + } + heapAddrCandidates[numElements++] = aExceptionContext->Eip; +#elif defined(_M_AMD64) + if (!aInstructionPointerOnly) { + heapAddrCandidates[numElements++] = aExceptionContext->Rax; + heapAddrCandidates[numElements++] = aExceptionContext->Rbx; + heapAddrCandidates[numElements++] = aExceptionContext->Rcx; + heapAddrCandidates[numElements++] = aExceptionContext->Rdx; + heapAddrCandidates[numElements++] = aExceptionContext->Rsi; + heapAddrCandidates[numElements++] = aExceptionContext->Rdi; + heapAddrCandidates[numElements++] = aExceptionContext->R8; + heapAddrCandidates[numElements++] = aExceptionContext->R9; + heapAddrCandidates[numElements++] = aExceptionContext->R10; + heapAddrCandidates[numElements++] = aExceptionContext->R11; + heapAddrCandidates[numElements++] = aExceptionContext->R12; + heapAddrCandidates[numElements++] = aExceptionContext->R13; + heapAddrCandidates[numElements++] = aExceptionContext->R14; + heapAddrCandidates[numElements++] = aExceptionContext->R15; + } + heapAddrCandidates[numElements++] = aExceptionContext->Rip; +#elif defined(_M_ARM64) + if (!aInstructionPointerOnly) { + for (auto reg : aExceptionContext->X) { + heapAddrCandidates[numElements++] = reg; + } + heapAddrCandidates[numElements++] = aExceptionContext->Sp; + } + heapAddrCandidates[numElements++] = aExceptionContext->Pc; +#endif + + // Inplace sort the candidates for excluding or merging memory regions. + auto begin = &heapAddrCandidates[0], end = &heapAddrCandidates[numElements]; + std::make_heap(begin, end); + std::sort_heap(begin, end); + + auto appMemory = FindNextPreallocated(aList, aList.begin()); + for (size_t i = 0; i < numElements; i++) { + if (appMemory == aList.end()) { + break; + } + + AppMemory tmp{}; + if (!GetAppMemoryFromRegister(aProcess, + aInstructionPointerOnly ? nullptr : &tib, + heapAddrCandidates[i], + &tmp)) { + continue; + } + + if (!(tmp.ptr && tmp.length)) { + // Something unexpected happens. Skip this candidate. + continue; + } + + if (!appMemory->ptr) { + *appMemory = tmp; + continue; + } + + if (appMemory->ptr + appMemory->length > tmp.ptr) { + // The beginning of the next region fall within the range of the previous + // region: merge into one. Note that we don't merge adjacent regions like + // [0, 99] and [100, 199] in case we cross the border of memory allocation + // regions. + appMemory->length = tmp.ptr + tmp.length - appMemory->ptr; + continue; + } + + appMemory = FindNextPreallocated(aList, ++appMemory); + if (appMemory == aList.end()) { + break; + } + + *appMemory = tmp; + } +} + +BOOL CALLBACK MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output) { + switch (callback_input->CallbackType) { + case MemoryCallback: { + MinidumpCallbackContext* callback_context = + reinterpret_cast<MinidumpCallbackContext*>(context); + + // Skip unused preallocated AppMemory elements. + while (callback_context->iter != callback_context->end && + callback_context->iter->preallocated && + !callback_context->iter->ptr) { + callback_context->iter++; + } + + if (callback_context->iter == callback_context->end) + return FALSE; + + // Include the specified memory region. + callback_output->MemoryBase = callback_context->iter->ptr; + callback_output->MemorySize = callback_context->iter->length; + callback_context->iter++; + return TRUE; + } + + // Include all modules. + case IncludeModuleCallback: + case ModuleCallback: + return TRUE; + + // Include all threads. + case IncludeThreadCallback: + case ThreadCallback: + return TRUE; + + // Stop receiving cancel callbacks. + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; + } + // Ignore other callback types. + return FALSE; +} + +} // namespace google_breakpad + diff --git a/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h new file mode 100644 index 0000000000..1306235cfd --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/minidump_callback.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MINIDUMP_CALLBACK_H__ +#define MINIDUMP_CALLBACK_H__ + +#include <windows.h> +#include <dbghelp.h> + +#include <list> + +namespace google_breakpad { + +// These entries store a list of memory regions that the client wants included +// in the minidump. +struct AppMemory { + ULONG64 ptr; + ULONG length; + bool preallocated; + + bool operator==(const struct AppMemory& other) const { + return ptr == other.ptr; + } + + bool operator==(const void* other) const { + return ptr == reinterpret_cast<ULONG64>(other); + } + + AppMemory() + : ptr(0) + , length(0) + , preallocated(false) + {} + + AppMemory& operator=(const AppMemory& other) = default; +}; +typedef std::list<AppMemory> AppMemoryList; + +// This is passed as the context to the MinidumpWriteDump callback. +typedef struct { + AppMemoryList::const_iterator iter; + AppMemoryList::const_iterator end; +} MinidumpCallbackContext; + +static const size_t kExceptionAppMemoryRegions = 33; + +#if defined(_M_IX86) +using RegisterValueType = DWORD; +#elif defined(_M_AMD64) || defined(_M_ARM64) +using RegisterValueType = DWORD64; +#else +#error Unsupported platform +#endif + +void IncludeAppMemoryFromExceptionContext(HANDLE aProcess, + DWORD aThreadId, + AppMemoryList& aList, + PCONTEXT aExceptionContext, + bool aInsttructionPointerOnly); + +// This function is used as a callback when calling MinidumpWriteDump, +// in order to add additional memory regions to the dump. +BOOL CALLBACK MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output); + +// Called during startup to initialize system information used by +// IncludeAppMemoryFromExceptionContext(). +void InitAppMemoryInternal(); + +} // namespace google_breakpad + +#endif + diff --git a/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild b/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild new file mode 100644 index 0000000000..ddc1d58d56 --- /dev/null +++ b/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +lobjs_client_common = [ + 'minidump_callback.cc', +] + +subdir = 'toolkit/crashreporter/breakpad-client/windows/common' +objs_client_common = [ + '/%s/%s' % (subdir, s) for s in lobjs_client_common +] |