Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e718065aeb | ||
|
d30a46c7dc | ||
|
22de2f34d4 | ||
|
796f803028 | ||
|
8fae3bfa9c | ||
|
c64358406e | ||
|
93199644cc | ||
|
64bae947cc | ||
|
e1323e9cf4 | ||
|
c15059e161 | ||
|
8803ee297c | ||
|
c2da4b7b28 | ||
|
39b88e1577 | ||
|
273d3260a0 | ||
|
2ae7008236 | ||
|
dbeae392b9 |
121
3rdparty/google-breakpad/CMakeLists.txt
vendored
121
3rdparty/google-breakpad/CMakeLists.txt
vendored
@ -2,35 +2,104 @@ cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (LINUX)
|
||||
set(SOURCES
|
||||
client/linux/crash_generation/crash_generation_client.cc
|
||||
client/linux/handler/exception_handler.cc
|
||||
client/linux/minidump_writer/linux_dumper.cc
|
||||
client/linux/minidump_writer/minidump_writer.cc
|
||||
client/minidump_file_writer.cc
|
||||
common/convert_UTF.c
|
||||
common/md5.c
|
||||
common/string_conversion.cc
|
||||
common/linux/file_id.cc
|
||||
common/linux/guid_creator.cc
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=gnu++0x")
|
||||
|
||||
set(COMMON_SOURCES
|
||||
common/convert_UTF.c
|
||||
common/dwarf/bytereader.cc
|
||||
common/dwarf/dwarf2diehandler.cc
|
||||
common/dwarf/dwarf2reader.cc
|
||||
common/dwarf_cfi_to_module.cc
|
||||
common/dwarf_cu_to_module.cc
|
||||
common/dwarf_line_to_module.cc
|
||||
common/language.cc
|
||||
common/md5.cc
|
||||
common/module.cc
|
||||
common/stabs_reader.cc
|
||||
common/stabs_to_module.cc
|
||||
common/string_conversion.cc
|
||||
)
|
||||
|
||||
set(LIBRARY_SOURCES
|
||||
client/minidump_file_writer.cc
|
||||
)
|
||||
|
||||
set(DUMP_SYMS_SOURCES
|
||||
)
|
||||
|
||||
if(LINUX)
|
||||
add_definitions(-DHAVE_A_OUT_H)
|
||||
|
||||
list(APPEND COMMON_SOURCES
|
||||
common/linux/dump_symbols.cc
|
||||
common/linux/elf_symbols_to_module.cc
|
||||
common/linux/elfutils.cc
|
||||
common/linux/file_id.cc
|
||||
common/linux/guid_creator.cc
|
||||
common/linux/linux_libc_support.cc
|
||||
common/linux/memory_mapped_file.cc
|
||||
common/linux/safe_readlink.cc
|
||||
)
|
||||
|
||||
ADD_LIBRARY(breakpad STATIC
|
||||
${SOURCES}
|
||||
list(APPEND LIBRARY_SOURCES
|
||||
client/linux/crash_generation/crash_generation_client.cc
|
||||
client/linux/handler/exception_handler.cc
|
||||
client/linux/handler/minidump_descriptor.cc
|
||||
client/linux/log/log.cc
|
||||
client/linux/minidump_writer/linux_dumper.cc
|
||||
client/linux/minidump_writer/linux_ptrace_dumper.cc
|
||||
client/linux/minidump_writer/minidump_writer.cc
|
||||
client/minidump_file_writer.cc
|
||||
)
|
||||
endif (LINUX)
|
||||
|
||||
if (APPLE)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms/build/Release/dump_syms
|
||||
COMMAND xcodebuild -target dump_syms -configuration Release -sdk macosx10.5 ARCHS=i386
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms)
|
||||
list(APPEND DUMP_SYMS_SOURCES
|
||||
tools/linux/dump_syms/dump_syms.cc
|
||||
)
|
||||
elseif(APPLE)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/client/apple/Framework)
|
||||
add_definitions(-DHAVE_MACH_O_NLIST_H)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/client/mac/build/Release/Breakpad.Framework
|
||||
COMMAND xcodebuild -target Breakpad -configuration Release -sdk macosx10.5 ARCHS=i386
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/client/mac)
|
||||
list(APPEND COMMON_SOURCES
|
||||
common/mac/arch_utilities.cc
|
||||
common/mac/bootstrap_compat.cc
|
||||
common/mac/dump_syms.mm
|
||||
common/mac/file_id.cc
|
||||
common/mac/MachIPC.mm
|
||||
common/mac/macho_id.cc
|
||||
common/mac/macho_reader.cc
|
||||
common/mac/macho_utilities.cc
|
||||
common/mac/macho_walker.cc
|
||||
common/mac/SimpleStringDictionary.mm
|
||||
common/mac/string_utilities.cc
|
||||
)
|
||||
|
||||
add_custom_target(breakpad
|
||||
DEPENDS tools/mac/dump_syms/build/Release/dump_syms
|
||||
client/mac/build/Release/Breakpad.Framework)
|
||||
endif (APPLE)
|
||||
list(APPEND LIBRARY_SOURCES
|
||||
client/mac/crash_generation/crash_generation_client.cc
|
||||
client/mac/Framework/Breakpad.mm
|
||||
client/mac/Framework/OnDemandServer.mm
|
||||
client/mac/handler/breakpad_nlist_64.cc
|
||||
client/mac/handler/dynamic_images.cc
|
||||
client/mac/handler/exception_handler.cc
|
||||
client/mac/handler/minidump_generator.cc
|
||||
client/mac/handler/protected_memory_allocator.cc
|
||||
)
|
||||
|
||||
list(APPEND DUMP_SYMS_SOURCES
|
||||
tools/mac/dump_syms/dump_syms_tool.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(libbreakpad_common STATIC ${COMMON_SOURCES})
|
||||
add_library(breakpad STATIC ${LIBRARY_SOURCES})
|
||||
|
||||
# This is used by cmake/DumpSymbols.cmake
|
||||
add_executable(dump_syms ${DUMP_SYMS_SOURCES})
|
||||
|
||||
target_link_libraries(breakpad libbreakpad_common)
|
||||
target_link_libraries(dump_syms libbreakpad_common)
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(dump_syms
|
||||
/System/Library/Frameworks/Foundation.framework
|
||||
)
|
||||
endif()
|
||||
|
22
3rdparty/google-breakpad/c++0x.patch
vendored
Normal file
22
3rdparty/google-breakpad/c++0x.patch
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
diff --git a/3rdparty/google-breakpad/client/linux/minidump_writer/linux_dumper.h b/3rdparty/google-breakpad/client/linux/minidump_writer/linux_dumper.h
|
||||
index a4a3ab5..a46cbfc 100644
|
||||
--- a/3rdparty/google-breakpad/client/linux/minidump_writer/linux_dumper.h
|
||||
+++ b/3rdparty/google-breakpad/client/linux/minidump_writer/linux_dumper.h
|
||||
@@ -50,7 +50,7 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
-typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
+typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
#endif
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
@@ -60,7 +60,7 @@ typedef Elf32_auxv_t elf_aux_entry;
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
#endif
|
||||
|
||||
-typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
+typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
|
||||
// When we find the VDSO mapping in the process's address space, this
|
||||
// is the name we use for it when writing it to the minidump.
|
72
3rdparty/google-breakpad/client/apple/Framework/BreakpadDefines.h
vendored
Normal file
72
3rdparty/google-breakpad/client/apple/Framework/BreakpadDefines.h
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// Keys for configuration file
|
||||
#define kReporterMinidumpDirectoryKey "MinidumpDir"
|
||||
#define kReporterMinidumpIDKey "MinidumpID"
|
||||
|
||||
// Filename for recording uploaded IDs
|
||||
#define kReporterLogFilename "uploads.log"
|
||||
|
||||
// The default subdirectory of the Library to put crash dumps in
|
||||
// The subdirectory is
|
||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
||||
#define kDefaultLibrarySubdirectory "Breakpad"
|
||||
|
||||
// Specify some special keys to be used in the configuration file that is
|
||||
// generated by Breakpad and consumed by the crash_sender.
|
||||
#define BREAKPAD_PRODUCT "BreakpadProduct"
|
||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
|
||||
#define BREAKPAD_VERSION "BreakpadVersion"
|
||||
#define BREAKPAD_VENDOR "BreakpadVendor"
|
||||
#define BREAKPAD_URL "BreakpadURL"
|
||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
|
||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
|
||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
|
||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
|
||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
|
||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
|
||||
#define BREAKPAD_REPORTER_EXE_LOCATION \
|
||||
"BreakpadReporterExeLocation"
|
||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
|
||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
|
||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
|
||||
#define BREAKPAD_COMMENTS "BreakpadComments"
|
||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
|
||||
#define BREAKPAD_EMAIL "BreakpadEmail"
|
||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
|
||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
|
||||
|
||||
// The keys below are NOT user supplied, and are used internally.
|
||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
|
||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
|
||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
|
||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
|
||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
|
||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
|
@ -35,6 +35,7 @@
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
@ -67,12 +68,14 @@ CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
|
||||
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
|
||||
*p = fds[1];
|
||||
|
||||
HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||
sys_close(fds[1]);
|
||||
if (ret <= 0)
|
||||
return false;
|
||||
|
||||
// wait for an ACK from the server
|
||||
char b;
|
||||
HANDLE_EINTR(sys_read(fds[0], &b, 1));
|
||||
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -73,19 +73,14 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/signal.h>
|
||||
#endif
|
||||
#include <sys/syscall.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/signal.h>
|
||||
#include <sys/ucontext.h>
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
#include <sys/wait.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
@ -93,12 +88,14 @@
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#include "linux/sched.h"
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
@ -111,144 +108,177 @@ static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
// The list of signals which we consider to be crashes. The default action for
|
||||
// all these signals must be Core (see man 7 signal) because we rethrow the
|
||||
// signal after handling it and expect that it'll be fatal.
|
||||
static const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
|
||||
const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
|
||||
};
|
||||
const int kNumHandledSignals =
|
||||
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
|
||||
struct sigaction old_handlers[kNumHandledSignals];
|
||||
bool handlers_installed = false;
|
||||
|
||||
// InstallAlternateStackLocked will store the newly installed stack in new_stack
|
||||
// and (if it exists) the previously installed stack in old_stack.
|
||||
stack_t old_stack;
|
||||
stack_t new_stack;
|
||||
bool stack_installed = false;
|
||||
|
||||
// Create an alternative stack to run the signal handlers on. This is done since
|
||||
// the signal might have been caused by a stack overflow.
|
||||
// Runs before crashing: normal context.
|
||||
void InstallAlternateStackLocked() {
|
||||
if (stack_installed)
|
||||
return;
|
||||
|
||||
memset(&old_stack, 0, sizeof(old_stack));
|
||||
memset(&new_stack, 0, sizeof(new_stack));
|
||||
|
||||
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
|
||||
// the alternative stack. Ensure that the size of the alternative stack is
|
||||
// large enough.
|
||||
static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ);
|
||||
|
||||
// Only set an alternative stack if there isn't already one, or if the current
|
||||
// one is too small.
|
||||
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
|
||||
old_stack.ss_size < kSigStackSize) {
|
||||
new_stack.ss_sp = malloc(kSigStackSize);
|
||||
new_stack.ss_size = kSigStackSize;
|
||||
|
||||
if (sys_sigaltstack(&new_stack, NULL) == -1) {
|
||||
free(new_stack.ss_sp);
|
||||
return;
|
||||
}
|
||||
stack_installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void RestoreAlternateStackLocked() {
|
||||
if (!stack_installed)
|
||||
return;
|
||||
|
||||
stack_t current_stack;
|
||||
if (sys_sigaltstack(NULL, ¤t_stack) == -1)
|
||||
return;
|
||||
|
||||
// Only restore the old_stack if the current alternative stack is the one
|
||||
// installed by the call to InstallAlternateStackLocked.
|
||||
if (current_stack.ss_sp == new_stack.ss_sp) {
|
||||
if (old_stack.ss_sp) {
|
||||
if (sys_sigaltstack(&old_stack, NULL) == -1)
|
||||
return;
|
||||
} else {
|
||||
stack_t disable_stack;
|
||||
disable_stack.ss_flags = SS_DISABLE;
|
||||
if (sys_sigaltstack(&disable_stack, NULL) == -1)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
free(new_stack.ss_sp);
|
||||
stack_installed = false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// We can stack multiple exception handlers. In that case, this is the global
|
||||
// which holds the stack.
|
||||
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||
unsigned ExceptionHandler::handler_stack_index_ = 0;
|
||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
||||
PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
handler_installed_(install_handler)
|
||||
{
|
||||
Init(dump_path, -1);
|
||||
}
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd)
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
handler_installed_(install_handler)
|
||||
{
|
||||
Init(dump_path, server_fd);
|
||||
}
|
||||
: filter_(filter),
|
||||
callback_(callback),
|
||||
callback_context_(callback_context),
|
||||
minidump_descriptor_(descriptor),
|
||||
crash_handler_(NULL) {
|
||||
if (server_fd >= 0)
|
||||
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
UninstallHandlers();
|
||||
}
|
||||
|
||||
void ExceptionHandler::Init(const std::string &dump_path,
|
||||
const int server_fd)
|
||||
{
|
||||
crash_handler_ = NULL;
|
||||
if (0 <= server_fd)
|
||||
crash_generation_client_
|
||||
.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
||||
if (handler_installed_)
|
||||
InstallHandlers();
|
||||
|
||||
if (!IsOutOfProcess())
|
||||
set_dump_path(dump_path);
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD())
|
||||
minidump_descriptor_.UpdatePath();
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
if (handler_stack_ == NULL)
|
||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
||||
if (!handler_stack_)
|
||||
handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||
if (install_handler) {
|
||||
InstallAlternateStackLocked();
|
||||
InstallHandlersLocked();
|
||||
}
|
||||
handler_stack_->push_back(this);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
bool ExceptionHandler::InstallHandlers() {
|
||||
// We run the signal handlers on an alternative stack because we might have
|
||||
// crashed because of a stack overflow.
|
||||
ExceptionHandler::~ExceptionHandler() {
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
||||
handler_stack_->erase(handler);
|
||||
if (handler_stack_->empty()) {
|
||||
RestoreAlternateStackLocked();
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
}
|
||||
|
||||
// We use this value rather than SIGSTKSZ because we would end up overrunning
|
||||
// such a small stack.
|
||||
static const unsigned kSigStackSize = 8192;
|
||||
|
||||
signal_stack = malloc(kSigStackSize);
|
||||
stack_t stack;
|
||||
memset(&stack, 0, sizeof(stack));
|
||||
stack.ss_sp = signal_stack;
|
||||
stack.ss_size = kSigStackSize;
|
||||
|
||||
if (sys_sigaltstack(&stack, NULL) == -1)
|
||||
// Runs before crashing: normal context.
|
||||
// static
|
||||
bool ExceptionHandler::InstallHandlersLocked() {
|
||||
if (handlers_installed)
|
||||
return false;
|
||||
|
||||
// Fail if unable to store all the old handlers.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
// mask all exception signals when we're handling one of them.
|
||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
|
||||
// Mask all exception signals when we're handling one of them.
|
||||
for (int i = 0; i < kNumHandledSignals; ++i)
|
||||
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
|
||||
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
|
||||
struct sigaction* old = new struct sigaction;
|
||||
if (sigaction(kExceptionSignals[i], &sa, old) == -1)
|
||||
return false;
|
||||
old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
|
||||
// At this point it is impractical to back out changes, and so failure to
|
||||
// install a signal is intentionally ignored.
|
||||
}
|
||||
}
|
||||
handlers_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void ExceptionHandler::UninstallHandlers() {
|
||||
for (unsigned i = 0; i < old_handlers_.size(); ++i) {
|
||||
struct sigaction *action =
|
||||
reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
|
||||
sigaction(old_handlers_[i].first, action, NULL);
|
||||
delete action;
|
||||
}
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
||||
handler_stack_->erase(handler);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
old_handlers_.clear();
|
||||
}
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
void ExceptionHandler::UpdateNextID() {
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
next_minidump_id_ = guid_str;
|
||||
next_minidump_id_c_ = next_minidump_id_.c_str();
|
||||
|
||||
char minidump_path[PATH_MAX];
|
||||
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
|
||||
dump_path_c_,
|
||||
guid_str);
|
||||
|
||||
next_minidump_path_ = minidump_path;
|
||||
next_minidump_path_c_ = next_minidump_path_.c_str();
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::RestoreHandlersLocked() {
|
||||
if (!handlers_installed)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||
signal(kExceptionSignals[i], SIG_DFL);
|
||||
}
|
||||
}
|
||||
handlers_installed = false;
|
||||
}
|
||||
|
||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
||||
@ -262,18 +292,49 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// All the exception signals are blocked at this point.
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
|
||||
if (!handler_stack_->size()) {
|
||||
// Sometimes, Breakpad runs inside a process where some other buggy code
|
||||
// saves and restores signal handlers temporarily with 'signal'
|
||||
// instead of 'sigaction'. This loses the SA_SIGINFO flag associated
|
||||
// with this function. As a consequence, the values of 'info' and 'uc'
|
||||
// become totally bogus, generally inducing a crash.
|
||||
//
|
||||
// The following code tries to detect this case. When it does, it
|
||||
// resets the signal handlers with sigaction + SA_SIGINFO and returns.
|
||||
// This forces the signal to be thrown again, but this time the kernel
|
||||
// will call the function with the right arguments.
|
||||
struct sigaction cur_handler;
|
||||
if (sigaction(sig, NULL, &cur_handler) == 0 &&
|
||||
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
|
||||
// Reset signal handler with the right flags.
|
||||
sigemptyset(&cur_handler.sa_mask);
|
||||
sigaddset(&cur_handler.sa_mask, sig);
|
||||
|
||||
cur_handler.sa_sigaction = SignalHandler;
|
||||
cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||
|
||||
if (sigaction(sig, &cur_handler, NULL) == -1) {
|
||||
// When resetting the handler fails, try to reset the
|
||||
// default one to avoid an infinite loop here.
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = handler_stack_->size() - 1; i >= 0; --i) {
|
||||
if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
|
||||
// successfully handled: We are in an invalid state since an exception
|
||||
// signal has been delivered. We don't call the exit handlers because
|
||||
// they could end up corrupting on-disk state.
|
||||
break;
|
||||
}
|
||||
bool handled = false;
|
||||
for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
||||
handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc);
|
||||
}
|
||||
|
||||
// Upon returning from this signal handler, sig will become unmasked and then
|
||||
// it will be retriggered. If one of the ExceptionHandlers handled it
|
||||
// successfully, restore the default handler. Otherwise, restore the
|
||||
// previously installed handler. Then, when the signal is retriggered, it will
|
||||
// be delivered to the appropriate handler.
|
||||
if (handled) {
|
||||
signal(sig, SIG_DFL);
|
||||
} else {
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
@ -293,17 +354,11 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// No need to reissue the signal. It will automatically trigger again,
|
||||
// when we return from the signal handler.
|
||||
}
|
||||
|
||||
// As soon as we return from the signal handler, our signal will become
|
||||
// unmasked. At that time, we will get terminated with the same signal that
|
||||
// was triggered originally. This allows our parent to know that we crashed.
|
||||
// The default action for all the signals which we catch is Core, so
|
||||
// this is the end of us.
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
|
||||
struct ThreadArgument {
|
||||
pid_t pid; // the crashing process
|
||||
const MinidumpDescriptor* minidump_descriptor;
|
||||
ExceptionHandler* handler;
|
||||
const void* context; // a CrashContext structure
|
||||
size_t context_size;
|
||||
@ -350,14 +405,23 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
#endif
|
||||
context.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context),
|
||||
callback_context_)) {
|
||||
if (crash_handler_(&context, sizeof(context), callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
// This is a public interface to HandleSignal that allows the client to
|
||||
// generate a crash dump. This function may run in a compromised context.
|
||||
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
|
||||
siginfo_t siginfo;
|
||||
my_memset(&siginfo, 0, sizeof(siginfo_t));
|
||||
struct ucontext context;
|
||||
getcontext(&context);
|
||||
return HandleSignal(sig, &siginfo, &context);
|
||||
}
|
||||
|
||||
// This function may run in a compromised context: see the top of the file.
|
||||
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
if (IsOutOfProcess())
|
||||
@ -374,6 +438,7 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
|
||||
ThreadArgument thread_arg;
|
||||
thread_arg.handler = this;
|
||||
thread_arg.minidump_descriptor = &minidump_descriptor_;
|
||||
thread_arg.pid = getpid();
|
||||
thread_arg.context = context;
|
||||
thread_arg.context_size = sizeof(*context);
|
||||
@ -388,17 +453,18 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
// is the write() and read() calls will fail with EBADF
|
||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
||||
sys_pipe failed:";
|
||||
sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
|
||||
const pid_t child = sys_clone(
|
||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
&thread_arg, NULL, NULL, NULL);
|
||||
|
||||
int r, status;
|
||||
// Allow the child to ptrace us
|
||||
prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||
sys_prctl(PR_SET_PTRACER, child);
|
||||
SendContinueSignalToChild();
|
||||
do {
|
||||
r = sys_waitpid(child, &status, __WALL);
|
||||
@ -409,17 +475,14 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
|
||||
if (r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
|
||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
|
||||
if (callback_)
|
||||
success = callback_(dump_path_c_, next_minidump_id_c_,
|
||||
callback_context_, success);
|
||||
|
||||
success = callback_(minidump_descriptor_, callback_context_, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -431,9 +494,9 @@ void ExceptionHandler::SendContinueSignalToChild() {
|
||||
if(r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
||||
sys_write failed:";
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,9 +509,9 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
if(r == -1) {
|
||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
||||
sys_read failed:";
|
||||
sys_write(2, msg, sizeof(msg) - 1);
|
||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
||||
sys_write(2, "\n", 1);
|
||||
logger::write(msg, sizeof(msg) - 1);
|
||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||
logger::write("\n", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,43 +519,81 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
return google_breakpad::WriteMinidump(next_minidump_path_c_,
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
}
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_);
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
|
||||
bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
ExceptionHandler eh(dump_path, NULL, callback, callback_context, false);
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
|
||||
return eh.WriteMinidump();
|
||||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
#if !defined(__ARM_EABI__)
|
||||
// Allow ourselves to be dumped.
|
||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
|
||||
// Update the path of the minidump so that this can be called multiple times
|
||||
// and new files are created for each minidump. This is done before the
|
||||
// generation happens, as clients may want to access the MinidumpDescriptor
|
||||
// after this call to find the exact path to the minidump file.
|
||||
minidump_descriptor_.UpdatePath();
|
||||
} else if (minidump_descriptor_.IsFD()) {
|
||||
// Reposition the FD to its beginning and resize it to get rid of the
|
||||
// previous minidump info.
|
||||
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
|
||||
static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0));
|
||||
}
|
||||
|
||||
// Allow this process to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
CrashContext context;
|
||||
int getcontext_result = getcontext(&context.context);
|
||||
if (getcontext_result)
|
||||
return false;
|
||||
#if !defined(__ARM_EABI__)
|
||||
// FPU state is not part of ARM EABI ucontext_t.
|
||||
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
#endif
|
||||
context.tid = sys_gettid();
|
||||
|
||||
bool success = GenerateDump(&context);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
// Add an exception stream to the minidump for better reporting.
|
||||
memset(&context.siginfo, 0, sizeof(context.siginfo));
|
||||
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
|
||||
#if defined(__i386__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
|
||||
#elif defined(__x86_64__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
|
||||
#elif defined(__arm__)
|
||||
context.siginfo.si_addr =
|
||||
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
|
||||
#else
|
||||
return false;
|
||||
#endif // !defined(__ARM_EABI__)
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
void ExceptionHandler::AddMappingInfo(const std::string& name,
|
||||
void ExceptionHandler::AddMappingInfo(const string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
@ -501,7 +602,8 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
|
||||
info.start_addr = start_address;
|
||||
info.size = mapping_size;
|
||||
info.offset = file_offset;
|
||||
strncpy(info.name, name.c_str(), std::min(name.size(), sizeof(info)));
|
||||
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
|
||||
info.name[sizeof(info.name) - 1] = '\0';
|
||||
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
@ -509,4 +611,43 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
|
||||
mapping_list_.push_back(mapping);
|
||||
}
|
||||
|
||||
void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
// Don't allow registering the same pointer twice.
|
||||
return;
|
||||
}
|
||||
|
||||
AppMemory app_memory;
|
||||
app_memory.ptr = ptr;
|
||||
app_memory.length = length;
|
||||
app_memory_list_.push_back(app_memory);
|
||||
}
|
||||
|
||||
void ExceptionHandler::UnregisterAppMemory(void* ptr) {
|
||||
AppMemoryList::iterator iter =
|
||||
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
|
||||
if (iter != app_memory_list_.end()) {
|
||||
app_memory_list_.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
// This function is not run in a compromised context.
|
||||
MinidumpDescriptor descriptor(dump_path);
|
||||
descriptor.UpdatePath();
|
||||
if (!google_breakpad::WriteMinidump(descriptor.path(),
|
||||
child,
|
||||
child_blamed_thread))
|
||||
return false;
|
||||
|
||||
return callback ? callback(descriptor, callback_context, true) : true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -37,21 +37,17 @@
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "client/linux/android_ucontext.h"
|
||||
#endif
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
struct sigaction;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
@ -70,17 +66,18 @@ class ExceptionHandler;
|
||||
// use different minidump callbacks for different call sites.
|
||||
//
|
||||
// In either case, a callback function is called when a minidump is written,
|
||||
// which receives the unqiue id of the minidump. The caller can use this
|
||||
// id to collect and write additional application state, and to launch an
|
||||
// external crash-reporting application.
|
||||
// which receives the full path or file descriptor of the minidump. The
|
||||
// caller can collect and write additional application state to that minidump,
|
||||
// and launch an external crash-reporting application.
|
||||
//
|
||||
// Caller should try to make the callbacks as crash-friendly as possible,
|
||||
// it should avoid use heap memory allocation as much as possible.
|
||||
|
||||
class ExceptionHandler {
|
||||
public:
|
||||
// A callback function to run before Breakpad performs any substantial
|
||||
// processing of an exception. A FilterCallback is called before writing
|
||||
// a minidump. context is the parameter supplied by the user as
|
||||
// a minidump. |context| is the parameter supplied by the user as
|
||||
// callback_context when the handler was created.
|
||||
//
|
||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||
@ -90,10 +87,10 @@ class ExceptionHandler {
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// A callback function to run after the minidump has been written.
|
||||
// minidump_id is a unique id for the dump, so the minidump
|
||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
||||
// by the user as callback_context when the handler was created. succeeded
|
||||
// indicates whether a minidump file was successfully written.
|
||||
// |descriptor| contains the file descriptor or file path containing the
|
||||
// minidump. |context| is the parameter supplied by the user as
|
||||
// callback_context when the handler was created. |succeeded| indicates
|
||||
// whether a minidump file was successfully written.
|
||||
//
|
||||
// If an exception occurred and the callback returns true, Breakpad will
|
||||
// treat the exception as fully-handled, suppressing any other handlers from
|
||||
@ -105,9 +102,8 @@ class ExceptionHandler {
|
||||
// should normally return the value of |succeeded|, or when they wish to
|
||||
// not report an exception of handled, false. Callbacks will rarely want to
|
||||
// return true directly (unless |succeeded| is true).
|
||||
typedef bool (*MinidumpCallback)(const char *dump_path,
|
||||
const char *minidump_id,
|
||||
void *context,
|
||||
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded);
|
||||
|
||||
// In certain cases, a user may wish to handle the generation of the minidump
|
||||
@ -120,52 +116,73 @@ class ExceptionHandler {
|
||||
void* context);
|
||||
|
||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
||||
// Before writing a minidump, the optional filter callback will be called.
|
||||
// Before writing a minidump, the optional |filter| callback will be called.
|
||||
// Its return value determines whether or not Breakpad should write a
|
||||
// minidump. Minidump files will be written to dump_path, and the optional
|
||||
// callback is called after writing the dump file, as described above.
|
||||
// minidump. The minidump content will be written to the file path or file
|
||||
// descriptor from |descriptor|, and the optional |callback| is called after
|
||||
// writing the dump file, as described above.
|
||||
// If install_handler is true, then a minidump will be written whenever
|
||||
// an unhandled exception occurs. If it is false, minidumps will only
|
||||
// be written when WriteMinidump is called.
|
||||
ExceptionHandler(const std::string &dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
// If |server_fd| is valid, the minidump is generated out-of-process. If it
|
||||
// is -1, in-process generation will always be used.
|
||||
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
bool install_handler);
|
||||
|
||||
// Creates a new ExceptionHandler instance that can attempt to
|
||||
// perform out-of-process dump generation if server_fd is valid. If
|
||||
// server_fd is invalid, in-process dump generation will be
|
||||
// used. See the above ctor for a description of the other
|
||||
// parameters.
|
||||
ExceptionHandler(const std::string& dump_path,
|
||||
FilterCallback filter, MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const int server_fd);
|
||||
|
||||
~ExceptionHandler();
|
||||
|
||||
// Get and set the minidump path.
|
||||
std::string dump_path() const { return dump_path_; }
|
||||
void set_dump_path(const std::string &dump_path) {
|
||||
dump_path_ = dump_path;
|
||||
dump_path_c_ = dump_path_.c_str();
|
||||
UpdateNextID();
|
||||
const MinidumpDescriptor& minidump_descriptor() const {
|
||||
return minidump_descriptor_;
|
||||
}
|
||||
|
||||
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
|
||||
minidump_descriptor_ = descriptor;
|
||||
}
|
||||
|
||||
void set_crash_handler(HandlerCallback callback) {
|
||||
crash_handler_ = callback;
|
||||
}
|
||||
|
||||
// Writes a minidump immediately. This can be used to capture the
|
||||
// execution state independently of a crash. Returns true on success.
|
||||
// Writes a minidump immediately. This can be used to capture the execution
|
||||
// state independently of a crash.
|
||||
// Returns true on success.
|
||||
// If the ExceptionHandler has been created with a path, a new file is
|
||||
// generated for each minidump. The file path can be retrieved in the
|
||||
// MinidumpDescriptor passed to the MinidumpCallback or by accessing the
|
||||
// MinidumpDescriptor directly from the ExceptionHandler (with
|
||||
// minidump_descriptor()).
|
||||
// If the ExceptionHandler has been created with a file descriptor, the file
|
||||
// descriptor is repositioned to its beginning and the previous generated
|
||||
// minidump is overwritten.
|
||||
// Note that this method is not supposed to be called from a compromised
|
||||
// context as it uses the heap.
|
||||
bool WriteMinidump();
|
||||
|
||||
// Convenience form of WriteMinidump which does not require an
|
||||
// ExceptionHandler instance.
|
||||
static bool WriteMinidump(const std::string &dump_path,
|
||||
static bool WriteMinidump(const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context);
|
||||
void* callback_context);
|
||||
|
||||
// Write a minidump of |child| immediately. This can be used to
|
||||
// capture the execution state of |child| independently of a crash.
|
||||
// Pass a meaningful |child_blamed_thread| to make that thread in
|
||||
// the child process the one from which a crash signature is
|
||||
// extracted.
|
||||
//
|
||||
// WARNING: the return of this function *must* happen before
|
||||
// the code that will eventually reap |child| executes.
|
||||
// Otherwise there's a pernicious race condition in which |child|
|
||||
// exits, is reaped, another process created with its pid, then that
|
||||
// new process dumped.
|
||||
static bool WriteMinidumpForChild(pid_t child,
|
||||
pid_t child_blamed_thread,
|
||||
const string& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
|
||||
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
||||
// blob. It shouldn't be needed in any user code.
|
||||
@ -187,23 +204,32 @@ class ExceptionHandler {
|
||||
// Add information about a memory mapping. This can be used if
|
||||
// a custom library loader is used that maps things in a way
|
||||
// that the linux dumper can't handle by reading the maps file.
|
||||
void AddMappingInfo(const std::string& name,
|
||||
void AddMappingInfo(const string& name,
|
||||
const u_int8_t identifier[sizeof(MDGUID)],
|
||||
uintptr_t start_address,
|
||||
size_t mapping_size,
|
||||
size_t file_offset);
|
||||
|
||||
// Register a block of memory of length bytes starting at address ptr
|
||||
// to be copied to the minidump when a crash happens.
|
||||
void RegisterAppMemory(void* ptr, size_t length);
|
||||
|
||||
// Unregister a block of memory that was registered with RegisterAppMemory.
|
||||
void UnregisterAppMemory(void* ptr);
|
||||
|
||||
// Force signal handling for the specified signal.
|
||||
bool SimulateSignalDelivery(int sig);
|
||||
private:
|
||||
void Init(const std::string &dump_path,
|
||||
const int server_fd);
|
||||
bool InstallHandlers();
|
||||
void UninstallHandlers();
|
||||
// Save the old signal handlers and install new ones.
|
||||
static bool InstallHandlersLocked();
|
||||
// Restore the old signal handlers.
|
||||
static void RestoreHandlersLocked();
|
||||
|
||||
void PreresolveSymbols();
|
||||
bool GenerateDump(CrashContext *context);
|
||||
void SendContinueSignalToChild();
|
||||
void WaitForContinueSignal();
|
||||
|
||||
void UpdateNextID();
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||
static int ThreadEntry(void* arg);
|
||||
@ -216,32 +242,16 @@ class ExceptionHandler {
|
||||
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
|
||||
std::string dump_path_;
|
||||
std::string next_minidump_path_;
|
||||
std::string next_minidump_id_;
|
||||
MinidumpDescriptor minidump_descriptor_;
|
||||
|
||||
// Pointers to C-string representations of the above. These are set
|
||||
// when the above are set so we can avoid calling c_str during
|
||||
// an exception.
|
||||
const char* dump_path_c_;
|
||||
const char* next_minidump_path_c_;
|
||||
const char* next_minidump_id_c_;
|
||||
|
||||
const bool handler_installed_;
|
||||
void* signal_stack; // the handler stack.
|
||||
HandlerCallback crash_handler_;
|
||||
|
||||
// The global exception handler stack. This is need becuase there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
static std::vector<ExceptionHandler*> *handler_stack_;
|
||||
// The index of the handler that should handle the next exception.
|
||||
static unsigned handler_stack_index_;
|
||||
static pthread_mutex_t handler_stack_mutex_;
|
||||
|
||||
// A vector of the old signal handlers.
|
||||
std::vector<std::pair<int, struct sigaction *> > old_handlers_;
|
||||
|
||||
// We need to explicitly enable ptrace of parent processes on some
|
||||
// kernels, but we need to know the PID of the cloned process before we
|
||||
// can do this. We create a pipe which we can use to block the
|
||||
@ -252,6 +262,10 @@ class ExceptionHandler {
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
MappingList mapping_list_;
|
||||
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_list_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
77
3rdparty/google-breakpad/client/linux/handler/minidump_descriptor.cc
vendored
Normal file
77
3rdparty/google-breakpad/client/linux/handler/minidump_descriptor.cc
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2012 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.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/handler/minidump_descriptor.h"
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
: fd_(descriptor.fd_),
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL) {
|
||||
// The copy constructor is not allowed to be called on a MinidumpDescriptor
|
||||
// with a valid path_, as getting its c_path_ would require the heap which
|
||||
// can cause problems in compromised environments.
|
||||
assert(descriptor.path_.empty());
|
||||
}
|
||||
|
||||
MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
const MinidumpDescriptor& descriptor) {
|
||||
assert(descriptor.path_.empty());
|
||||
|
||||
fd_ = descriptor.fd_;
|
||||
directory_ = descriptor.directory_;
|
||||
path_.clear();
|
||||
if (c_path_) {
|
||||
// This descriptor already had a path set, so generate a new one.
|
||||
c_path_ = NULL;
|
||||
UpdatePath();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void MinidumpDescriptor::UpdatePath() {
|
||||
assert(fd_ == -1 && !directory_.empty());
|
||||
|
||||
GUID guid;
|
||||
char guid_str[kGUIDStringLength + 1];
|
||||
if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
path_.clear();
|
||||
path_ = directory_ + "/" + guid_str + ".dmp";
|
||||
c_path_ = path_.c_str();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
100
3rdparty/google-breakpad/client/linux/handler/minidump_descriptor.h
vendored
Normal file
100
3rdparty/google-breakpad/client/linux/handler/minidump_descriptor.h
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2012 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_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// The MinidumpDescriptor describes how to access a minidump: it can contain
|
||||
// either a file descriptor or a path.
|
||||
// Note that when using files, it is created with the path to a directory.
|
||||
// The actual path where the minidump is generated is created by this class.
|
||||
namespace google_breakpad {
|
||||
|
||||
class MinidumpDescriptor {
|
||||
public:
|
||||
MinidumpDescriptor() : fd_(-1) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(int fd)
|
||||
: fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||
|
||||
bool IsFD() const { return fd_ != -1; }
|
||||
|
||||
int fd() const { return fd_; }
|
||||
|
||||
string directory() const { return directory_; }
|
||||
|
||||
const char* path() const { return c_path_; }
|
||||
|
||||
// Updates the path so it is unique.
|
||||
// Should be called from a normal context: this methods uses the heap.
|
||||
void UpdatePath();
|
||||
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
private:
|
||||
// The file descriptor where the minidump is generated.
|
||||
int fd_;
|
||||
|
||||
// The directory where the minidump should be generated.
|
||||
string directory_;
|
||||
// The full path to the generated minidump.
|
||||
string path_;
|
||||
// The C string of |path_|. Precomputed so it can be access from a compromised
|
||||
// context.
|
||||
const char* c_path_;
|
||||
|
||||
off_t size_limit_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
|
48
3rdparty/google-breakpad/client/linux/log/log.cc
vendored
Normal file
48
3rdparty/google-breakpad/client/linux/log/log.cc
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2012 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.
|
||||
|
||||
#include "client/linux/log/log.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#else
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes) {
|
||||
#if defined(__ANDROID__)
|
||||
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
|
||||
#else
|
||||
return sys_write(2, buf, nbytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace logger
|
41
3rdparty/google-breakpad/client/linux/log/log.h
vendored
Normal file
41
3rdparty/google-breakpad/client/linux/log/log.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012, 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_LINUX_LOG_LOG_H_
|
||||
#define CLIENT_LINUX_LOG_LOG_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes);
|
||||
|
||||
} // namespace logger
|
||||
|
||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
@ -37,6 +37,7 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -90,7 +91,7 @@ class DirectoryReader {
|
||||
reinterpret_cast<kernel_dirent*>(buf_);
|
||||
|
||||
buf_used_ -= dent->d_reclen;
|
||||
memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -114,7 +115,7 @@ class LineReader {
|
||||
|
||||
assert(buf_used_ >= len + 1);
|
||||
buf_used_ -= len + 1;
|
||||
memmove(buf_, buf_ + len + 1, buf_used_);
|
||||
my_memmove(buf_, buf_ + len + 1, buf_used_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -27,6 +27,9 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
|
||||
// See linux_dumper.h for details.
|
||||
|
||||
// This code deals with the mechanics of getting information about a crashed
|
||||
// process. Since this code may run in a compromised address space, the same
|
||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
||||
@ -34,75 +37,22 @@
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||
static const char kDeletedSuffix[] = " (deleted)";
|
||||
|
||||
// Suspend a thread by attaching to it.
|
||||
static bool SuspendThread(pid_t pid) {
|
||||
// This may fail if the thread has just died or debugged.
|
||||
errno = 0;
|
||||
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
|
||||
errno != 0) {
|
||||
return false;
|
||||
}
|
||||
while (sys_waitpid(pid, NULL, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||
// when trying to dump the thread's stack, it also results in the minidumps
|
||||
// containing information about the trusted threads. This information is
|
||||
// generally completely meaningless and just pollutes the minidumps.
|
||||
// We thus test the stack pointer and exclude any threads that are part of
|
||||
// the seccomp sandbox's trusted code.
|
||||
user_regs_struct regs;
|
||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||
#if defined(__i386)
|
||||
!regs.esp
|
||||
#elif defined(__x86_64)
|
||||
!regs.rsp
|
||||
#endif
|
||||
) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resume a thread by detaching from it.
|
||||
static bool ResumeThread(pid_t pid) {
|
||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
inline static bool IsMappedFileOpenUnsafe(
|
||||
const google_breakpad::MappingInfo& mapping) {
|
||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
||||
@ -116,130 +66,68 @@ inline static bool IsMappedFileOpenUnsafe(
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxDumper::LinuxDumper(int pid)
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
LinuxDumper::LinuxDumper(pid_t pid)
|
||||
: pid_(pid),
|
||||
threads_suspended_(false),
|
||||
crash_address_(0),
|
||||
crash_signal_(0),
|
||||
crash_thread_(0),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_) {
|
||||
mappings_(&allocator_),
|
||||
auxv_(&allocator_, AT_MAX + 1) {
|
||||
}
|
||||
|
||||
LinuxDumper::~LinuxDumper() {
|
||||
}
|
||||
|
||||
bool LinuxDumper::Init() {
|
||||
return EnumerateThreads(&threads_) &&
|
||||
EnumerateMappings(&mappings_);
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsSuspend() {
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (!SuspendThread(threads_[i])) {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
memmove(&threads_[i], &threads_[i+1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
threads_suspended_ = true;
|
||||
return threads_.size() > 0;
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsResume() {
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
void
|
||||
LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
|
||||
assert(path);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
path[0] = '\0';
|
||||
|
||||
const unsigned pid_len = my_int_len(pid);
|
||||
|
||||
assert(node);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
assert(node_len < NAME_MAX);
|
||||
if (node_len >= NAME_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(node_len > 0);
|
||||
if (node_len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pid > 0);
|
||||
if (pid <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t total_length = 6 + pid_len + 1 + node_len;
|
||||
|
||||
assert(total_length < NAME_MAX);
|
||||
if (total_length >= NAME_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(path, "/proc/", 6);
|
||||
my_itos(path + 6, pid, pid_len);
|
||||
memcpy(path + 6 + pid_len, "/", 1);
|
||||
memcpy(path + 6 + pid_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
int mapping_id,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
uint8_t identifier[sizeof(MDGUID)])
|
||||
{
|
||||
assert(mapping_id == -1 || mapping_id < mappings_.size());
|
||||
assert(!member || mapping_id < mappings_.size());
|
||||
my_memset(identifier, 0, sizeof(MDGUID));
|
||||
if (IsMappedFileOpenUnsafe(mapping))
|
||||
return false;
|
||||
|
||||
// Special-case linux-gate because it's not a real file.
|
||||
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
|
||||
const uintptr_t kPageSize = getpagesize();
|
||||
void* linux_gate = NULL;
|
||||
if (pid_ == sys_getpid()) {
|
||||
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
|
||||
} else {
|
||||
linux_gate = allocator_.Alloc(kPageSize);
|
||||
CopyFromProcess(linux_gate, pid_,
|
||||
reinterpret_cast<const void*>(mapping.start_addr),
|
||||
kPageSize);
|
||||
}
|
||||
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
|
||||
}
|
||||
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
assert(filename_len < NAME_MAX);
|
||||
if (filename_len >= NAME_MAX)
|
||||
return false;
|
||||
memcpy(filename, mapping.name, filename_len);
|
||||
my_memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||
|
||||
int fd = sys_open(filename, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct kernel_stat st;
|
||||
if (sys_fstat(fd, &st) != 0) {
|
||||
sys_close(fd);
|
||||
return false;
|
||||
}
|
||||
#if defined(__x86_64)
|
||||
#define sys_mmap2 sys_mmap
|
||||
#endif
|
||||
void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
sys_close(fd);
|
||||
if (base == MAP_FAILED)
|
||||
MemoryMappedFile mapped_file(filename);
|
||||
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||
return false;
|
||||
|
||||
bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier);
|
||||
sys_munmap(base, st.st_size);
|
||||
if (success && mapping_id != -1 && filename_modified) {
|
||||
bool success =
|
||||
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
if (success && member && filename_modified) {
|
||||
mappings_[mapping_id]->name[filename_len -
|
||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
||||
}
|
||||
@ -247,48 +135,50 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
return success;
|
||||
}
|
||||
|
||||
void*
|
||||
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
|
||||
bool LinuxDumper::ReadAuxv() {
|
||||
char auxv_path[NAME_MAX];
|
||||
BuildProcPath(auxv_path, pid, "auxv");
|
||||
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If BuildProcPath errors out due to invalid input, we'll handle it when
|
||||
// we try to sys_open the file.
|
||||
|
||||
// Find the AT_SYSINFO_EHDR entry for linux-gate.so
|
||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||
// information.
|
||||
int fd = sys_open(auxv_path, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
elf_aux_entry one_aux_entry;
|
||||
bool res = false;
|
||||
while (sys_read(fd,
|
||||
&one_aux_entry,
|
||||
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
|
||||
one_aux_entry.a_type != AT_NULL) {
|
||||
if (one_aux_entry.a_type == AT_SYSINFO_EHDR) {
|
||||
close(fd);
|
||||
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
|
||||
if (one_aux_entry.a_type <= AT_MAX) {
|
||||
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
return NULL;
|
||||
sys_close(fd);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
||||
bool LinuxDumper::EnumerateMappings() {
|
||||
char maps_path[NAME_MAX];
|
||||
BuildProcPath(maps_path, pid_, "maps");
|
||||
if (!BuildProcPath(maps_path, pid_, "maps"))
|
||||
return false;
|
||||
|
||||
// linux_gate_loc is the beginning of the kernel's mapping of
|
||||
// linux-gate.so in the process. It doesn't actually show up in the
|
||||
// maps list as a filename, so we use the aux vector to find it's
|
||||
// load location and special case it's entry when creating the list
|
||||
// of mappings.
|
||||
const void* linux_gate_loc;
|
||||
linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_);
|
||||
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
|
||||
// aux vector entry, which gives the information necessary to special
|
||||
// case its entry when creating the list of mappings.
|
||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||
// information.
|
||||
const void* linux_gate_loc =
|
||||
reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
|
||||
// Although the initial executable is usually the first mapping, it's not
|
||||
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
|
||||
// actual entry point to find the mapping.
|
||||
const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
|
||||
|
||||
const int fd = sys_open(maps_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
@ -317,8 +207,8 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
||||
}
|
||||
// Merge adjacent mappings with the same name into one module,
|
||||
// assuming they're a single library mapped by the dynamic linker
|
||||
if (name && result->size()) {
|
||||
MappingInfo* module = (*result)[result->size() - 1];
|
||||
if (name && !mappings_.empty()) {
|
||||
MappingInfo* module = mappings_.back();
|
||||
if ((start_addr == module->start_addr + module->size) &&
|
||||
(my_strlen(name) == my_strlen(module->name)) &&
|
||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
|
||||
@ -328,16 +218,34 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
||||
}
|
||||
}
|
||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||
memset(module, 0, sizeof(MappingInfo));
|
||||
my_memset(module, 0, sizeof(MappingInfo));
|
||||
module->start_addr = start_addr;
|
||||
module->size = end_addr - start_addr;
|
||||
module->offset = offset;
|
||||
if (name != NULL) {
|
||||
const unsigned l = my_strlen(name);
|
||||
if (l < sizeof(module->name))
|
||||
memcpy(module->name, name, l);
|
||||
my_memcpy(module->name, name, l);
|
||||
}
|
||||
// If this is the entry-point mapping, and it's not already the
|
||||
// first one, then we need to make it be first. This is because
|
||||
// the minidump format assumes the first module is the one that
|
||||
// corresponds to the main executable (as codified in
|
||||
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
|
||||
if (entry_point_loc &&
|
||||
(entry_point_loc >=
|
||||
reinterpret_cast<void*>(module->start_addr)) &&
|
||||
(entry_point_loc <
|
||||
reinterpret_cast<void*>(module->start_addr+module->size)) &&
|
||||
!mappings_.empty()) {
|
||||
// push the module onto the front of the list.
|
||||
mappings_.resize(mappings_.size() + 1);
|
||||
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
|
||||
mappings_[idx] = mappings_[idx - 1];
|
||||
mappings_[0] = module;
|
||||
} else {
|
||||
mappings_.push_back(module);
|
||||
}
|
||||
result->push_back(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,114 +254,7 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
||||
|
||||
sys_close(fd);
|
||||
|
||||
return result->size() > 0;
|
||||
}
|
||||
|
||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
||||
// pid.
|
||||
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
|
||||
char task_path[NAME_MAX];
|
||||
BuildProcPath(task_path, pid_, "task");
|
||||
|
||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
|
||||
|
||||
// The directory may contain duplicate entries which we filter by assuming
|
||||
// that they are consecutive.
|
||||
int last_tid = -1;
|
||||
const char* dent_name;
|
||||
while (dir_reader->GetNextEntry(&dent_name)) {
|
||||
if (my_strcmp(dent_name, ".") &&
|
||||
my_strcmp(dent_name, "..")) {
|
||||
int tid = 0;
|
||||
if (my_strtoui(&tid, dent_name) &&
|
||||
last_tid != tid) {
|
||||
last_tid = tid;
|
||||
result->push_back(tid);
|
||||
}
|
||||
}
|
||||
dir_reader->PopEntry();
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
// available.
|
||||
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
||||
assert(info != NULL);
|
||||
char status_path[NAME_MAX];
|
||||
BuildProcPath(status_path, tid, "status");
|
||||
|
||||
const int fd = open(status_path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
||||
const char* line;
|
||||
unsigned line_len;
|
||||
|
||||
info->ppid = info->tgid = -1;
|
||||
|
||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
||||
if (my_strncmp("Tgid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->tgid, line + 6);
|
||||
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->ppid, line + 6);
|
||||
}
|
||||
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
|
||||
if (info->ppid == -1 || info->tgid == -1)
|
||||
return false;
|
||||
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__i386)
|
||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
||||
if (sys_ptrace(
|
||||
PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*> (offsetof(struct user,
|
||||
u_debugreg[0]) + i *
|
||||
sizeof(debugreg_t)),
|
||||
&info->dregs[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
|
||||
return GetStackInfo(&info->stack, &info->stack_len,
|
||||
(uintptr_t) stack_pointer);
|
||||
return !mappings_.empty();
|
||||
}
|
||||
|
||||
// Get information about the stack, given the stack pointer. We don't try to
|
||||
@ -482,25 +283,6 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) {
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
uint8_t* const remote = (uint8_t*) src;
|
||||
|
||||
while (done < length) {
|
||||
const size_t l = length - done > word_size ? word_size : length - done;
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in.
|
||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
const uintptr_t addr = (uintptr_t) address;
|
||||
@ -530,11 +312,10 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
||||
char exe_link[NAME_MAX];
|
||||
char new_path[NAME_MAX];
|
||||
BuildProcPath(exe_link, pid_, "exe");
|
||||
ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX);
|
||||
if (new_path_len <= 0 || new_path_len == NAME_MAX)
|
||||
if (!BuildProcPath(exe_link, pid_, "exe"))
|
||||
return false;
|
||||
if (!SafeReadLink(exe_link, new_path))
|
||||
return false;
|
||||
new_path[new_path_len] = '\0';
|
||||
if (my_strcmp(path, new_path) != 0)
|
||||
return false;
|
||||
|
||||
@ -548,7 +329,7 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(path, exe_link, NAME_MAX);
|
||||
my_memcpy(path, exe_link, NAME_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,14 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
|
||||
// is a base class for extracting information of a crashed process. It
|
||||
// was originally a complete implementation using the ptrace API, but
|
||||
// has been refactored to allow derived implementations supporting both
|
||||
// ptrace and core dump. A portion of the original implementation is now
|
||||
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
|
||||
// details).
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||
|
||||
@ -34,9 +42,7 @@
|
||||
#include <linux/limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/user.h>
|
||||
#endif
|
||||
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
@ -44,31 +50,18 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
#endif
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
#if defined(__i386) || defined(__ARM_EABI__)
|
||||
#if !defined(__ANDROID__)
|
||||
typedef Elf32_auxv_t elf_aux_entry;
|
||||
#else
|
||||
// Android is missing this structure definition
|
||||
typedef struct
|
||||
{
|
||||
uint32_t a_type; /* Entry type */
|
||||
union
|
||||
{
|
||||
uint32_t a_val; /* Integer value */
|
||||
} a_un;
|
||||
} elf_aux_entry;
|
||||
|
||||
#if !defined(AT_SYSINFO_EHDR)
|
||||
#define AT_SYSINFO_EHDR 33
|
||||
#endif
|
||||
#endif // __ANDROID__
|
||||
#elif defined(__x86_64__)
|
||||
#elif defined(__x86_64)
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
#endif
|
||||
|
||||
typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
|
||||
// When we find the VDSO mapping in the process's address space, this
|
||||
// is the name we use for it when writing it to the minidump.
|
||||
// This should always be less than NAME_MAX!
|
||||
@ -79,10 +72,7 @@ struct ThreadInfo {
|
||||
pid_t tgid; // thread group id
|
||||
pid_t ppid; // parent process
|
||||
|
||||
// Even on platforms where the stack grows down, the following will point to
|
||||
// the smallest address in the stack.
|
||||
const void* stack; // pointer to the stack area
|
||||
size_t stack_len; // length of the stack to copy
|
||||
uintptr_t stack_pointer; // thread stack pointer
|
||||
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
@ -96,12 +86,8 @@ struct ThreadInfo {
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
||||
#if defined(__ANDROID__)
|
||||
struct pt_regs regs;
|
||||
#else
|
||||
struct user_regs regs;
|
||||
struct user_fpregs fpregs;
|
||||
#endif // __ANDROID__
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -118,21 +104,27 @@ class LinuxDumper {
|
||||
public:
|
||||
explicit LinuxDumper(pid_t pid);
|
||||
|
||||
virtual ~LinuxDumper();
|
||||
|
||||
// Parse the data for |threads| and |mappings|.
|
||||
bool Init();
|
||||
virtual bool Init();
|
||||
|
||||
// Return true if the dumper performs a post-mortem dump.
|
||||
virtual bool IsPostMortem() const = 0;
|
||||
|
||||
// Suspend/resume all threads in the given process.
|
||||
bool ThreadsSuspend();
|
||||
bool ThreadsResume();
|
||||
virtual bool ThreadsSuspend() = 0;
|
||||
virtual bool ThreadsResume() = 0;
|
||||
|
||||
// Read information about the given thread. Returns true on success. One must
|
||||
// have called |ThreadsSuspend| first.
|
||||
bool ThreadInfoGet(pid_t tid, ThreadInfo* info);
|
||||
// Read information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
|
||||
|
||||
// These are only valid after a call to |Init|.
|
||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
||||
const MappingInfo* FindMapping(const void* address) const;
|
||||
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
|
||||
|
||||
// Find a block of memory to take as the stack given the top of stack pointer.
|
||||
// stack: (output) the lowest address in the memory area
|
||||
@ -142,30 +134,41 @@ class LinuxDumper {
|
||||
|
||||
PageAllocator* allocator() { return &allocator_; }
|
||||
|
||||
// memcpy from a remote process.
|
||||
static void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
// Copy content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|.
|
||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) = 0;
|
||||
|
||||
// Builds a proc path for a certain pid for a node. path is a
|
||||
// character array that is overwritten, and node is the final node
|
||||
// without any slashes.
|
||||
void BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result.|node| is the final node without any slashes. Returns true on
|
||||
// success.
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
|
||||
|
||||
// Generate a File ID from the .text section of a mapped entry.
|
||||
// mapping_id may be -1 if this is not a member of mappings_.
|
||||
// If not a member, mapping_id is ignored.
|
||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
int mapping_id,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
uint8_t identifier[sizeof(MDGUID)]);
|
||||
|
||||
// Utility method to find the location of where the kernel has
|
||||
// mapped linux-gate.so in memory(shows up in /proc/pid/maps as
|
||||
// [vdso], but we can't guarantee that it's the only virtual dynamic
|
||||
// shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR
|
||||
// is the safest way to go.)
|
||||
void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const;
|
||||
private:
|
||||
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const;
|
||||
bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
|
||||
uintptr_t crash_address() const { return crash_address_; }
|
||||
void set_crash_address(uintptr_t crash_address) {
|
||||
crash_address_ = crash_address;
|
||||
}
|
||||
|
||||
int crash_signal() const { return crash_signal_; }
|
||||
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
|
||||
|
||||
pid_t crash_thread() const { return crash_thread_; }
|
||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
||||
|
||||
protected:
|
||||
bool ReadAuxv();
|
||||
|
||||
virtual bool EnumerateMappings();
|
||||
|
||||
virtual bool EnumerateThreads() = 0;
|
||||
|
||||
// For the case where a running program has been deleted, it'll show up in
|
||||
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
|
||||
@ -178,13 +181,28 @@ class LinuxDumper {
|
||||
// Returns true if |path| is modified.
|
||||
bool HandleDeletedFileInMapping(char* path) const;
|
||||
|
||||
// ID of the crashed process.
|
||||
const pid_t pid_;
|
||||
|
||||
// Virtual address at which the process crashed.
|
||||
uintptr_t crash_address_;
|
||||
|
||||
// Signal that terminated the crashed process.
|
||||
int crash_signal_;
|
||||
|
||||
// ID of the crashed thread.
|
||||
pid_t crash_thread_;
|
||||
|
||||
mutable PageAllocator allocator_;
|
||||
|
||||
bool threads_suspended_;
|
||||
wasteful_vector<pid_t> threads_; // the ids of all the threads
|
||||
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
|
||||
// IDs of all the threads.
|
||||
wasteful_vector<pid_t> threads_;
|
||||
|
||||
// Info from /proc/<pid>/maps.
|
||||
wasteful_vector<MappingInfo*> mappings_;
|
||||
|
||||
// Info from /proc/<pid>/auxv
|
||||
wasteful_vector<elf_aux_val_t> auxv_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
291
3rdparty/google-breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc
vendored
Normal file
291
3rdparty/google-breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
|
||||
// See linux_ptrace_dumper.h for detals.
|
||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
||||
|
||||
// This code deals with the mechanics of getting information about a crashed
|
||||
// process. Since this code may run in a compromised address space, the same
|
||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
||||
// use the alternative allocator.
|
||||
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "client/linux/minidump_writer/directory_reader.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
// Suspends a thread by attaching to it.
|
||||
static bool SuspendThread(pid_t pid) {
|
||||
// This may fail if the thread has just died or debugged.
|
||||
errno = 0;
|
||||
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
|
||||
errno != 0) {
|
||||
return false;
|
||||
}
|
||||
while (sys_waitpid(pid, NULL, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||
// when trying to dump the thread's stack, it also results in the minidumps
|
||||
// containing information about the trusted threads. This information is
|
||||
// generally completely meaningless and just pollutes the minidumps.
|
||||
// We thus test the stack pointer and exclude any threads that are part of
|
||||
// the seccomp sandbox's trusted code.
|
||||
user_regs_struct regs;
|
||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||
#if defined(__i386)
|
||||
!regs.esp
|
||||
#elif defined(__x86_64)
|
||||
!regs.rsp
|
||||
#endif
|
||||
) {
|
||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// Resumes a thread by detaching from it.
|
||||
static bool ResumeThread(pid_t pid) {
|
||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
|
||||
: LinuxDumper(pid),
|
||||
threads_suspended_(false) {
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
|
||||
const char* node) const {
|
||||
if (!path || !node || pid <= 0)
|
||||
return false;
|
||||
|
||||
size_t node_len = my_strlen(node);
|
||||
if (node_len == 0)
|
||||
return false;
|
||||
|
||||
const unsigned pid_len = my_uint_len(pid);
|
||||
const size_t total_length = 6 + pid_len + 1 + node_len;
|
||||
if (total_length >= NAME_MAX)
|
||||
return false;
|
||||
|
||||
my_memcpy(path, "/proc/", 6);
|
||||
my_uitos(path + 6, pid, pid_len);
|
||||
path[6 + pid_len] = '/';
|
||||
my_memcpy(path + 6 + pid_len + 1, node, node_len);
|
||||
path[total_length] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
uint8_t* const remote = (uint8_t*) src;
|
||||
|
||||
while (done < length) {
|
||||
const size_t l = (length - done > word_size) ? word_size : (length - done);
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
my_memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
// available.
|
||||
bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
if (index >= threads_.size())
|
||||
return false;
|
||||
|
||||
pid_t tid = threads_[index];
|
||||
|
||||
assert(info != NULL);
|
||||
char status_path[NAME_MAX];
|
||||
if (!BuildProcPath(status_path, tid, "status"))
|
||||
return false;
|
||||
|
||||
const int fd = sys_open(status_path, O_RDONLY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
|
||||
LineReader* const line_reader = new(allocator_) LineReader(fd);
|
||||
const char* line;
|
||||
unsigned line_len;
|
||||
|
||||
info->ppid = info->tgid = -1;
|
||||
|
||||
while (line_reader->GetNextLine(&line, &line_len)) {
|
||||
if (my_strncmp("Tgid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->tgid, line + 6);
|
||||
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
|
||||
my_strtoui(&info->ppid, line + 6);
|
||||
}
|
||||
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
sys_close(fd);
|
||||
|
||||
if (info->ppid == -1 || info->tgid == -1)
|
||||
return false;
|
||||
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__i386)
|
||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
||||
if (sys_ptrace(
|
||||
PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*> (offsetof(struct user,
|
||||
u_debugreg[0]) + i *
|
||||
sizeof(debugreg_t)),
|
||||
&info->dregs[i]) == -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint8_t* stack_pointer;
|
||||
#if defined(__i386)
|
||||
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||
#elif defined(__x86_64)
|
||||
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||
#elif defined(__ARM_EABI__)
|
||||
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::IsPostMortem() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ThreadsSuspend() {
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (!SuspendThread(threads_[i])) {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
my_memmove(&threads_[i], &threads_[i+1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
threads_suspended_ = true;
|
||||
return threads_.size() > 0;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ThreadsResume() {
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
||||
// pid.
|
||||
bool LinuxPtraceDumper::EnumerateThreads() {
|
||||
char task_path[NAME_MAX];
|
||||
if (!BuildProcPath(task_path, pid_, "task"))
|
||||
return false;
|
||||
|
||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
|
||||
|
||||
// The directory may contain duplicate entries which we filter by assuming
|
||||
// that they are consecutive.
|
||||
int last_tid = -1;
|
||||
const char* dent_name;
|
||||
while (dir_reader->GetNextEntry(&dent_name)) {
|
||||
if (my_strcmp(dent_name, ".") &&
|
||||
my_strcmp(dent_name, "..")) {
|
||||
int tid = 0;
|
||||
if (my_strtoui(&tid, dent_name) &&
|
||||
last_tid != tid) {
|
||||
last_tid = tid;
|
||||
threads_.push_back(tid);
|
||||
}
|
||||
}
|
||||
dir_reader->PopEntry();
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
92
3rdparty/google-breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h
vendored
Normal file
92
3rdparty/google-breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
|
||||
// class, which is derived from google_breakpad::LinuxDumper to extract
|
||||
// information from a crashed process via ptrace.
|
||||
// This class was originally splitted from google_breakpad::LinuxDumper.
|
||||
|
||||
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class LinuxPtraceDumper : public LinuxDumper {
|
||||
public:
|
||||
// Constructs a dumper for extracting information of a given process
|
||||
// with a process ID of |pid|.
|
||||
explicit LinuxPtraceDumper(pid_t pid);
|
||||
|
||||
// Implements LinuxDumper::BuildProcPath().
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||
// result. |node| is the final node without any slashes. Returns true on
|
||||
// success.
|
||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
|
||||
|
||||
// Implements LinuxDumper::CopyFromProcess().
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method uses ptrace to extract
|
||||
// the content from the target process.
|
||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
// Reads information about the |index|-th thread of |threads_|.
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
|
||||
|
||||
// Implements LinuxDumper::IsPostMortem().
|
||||
// Always returns false to indicate this dumper performs a dump of
|
||||
// a crashed process via ptrace.
|
||||
virtual bool IsPostMortem() const;
|
||||
|
||||
// Implements LinuxDumper::ThreadsSuspend().
|
||||
// Suspends all threads in the given process. Returns true on success.
|
||||
virtual bool ThreadsSuspend();
|
||||
|
||||
// Implements LinuxDumper::ThreadsResume().
|
||||
// Resumes all threads in the given process. Returns true on success.
|
||||
virtual bool ThreadsResume();
|
||||
|
||||
protected:
|
||||
// Implements LinuxDumper::EnumerateThreads().
|
||||
// Enumerates all threads of the given process into |threads_|.
|
||||
virtual bool EnumerateThreads();
|
||||
|
||||
private:
|
||||
// Set to true if all threads of the crashed process are suspended.
|
||||
bool threads_suspended_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_
|
File diff suppressed because it is too large
Load Diff
@ -31,36 +31,94 @@
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list>
|
||||
#include <utility>
|
||||
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
struct MappingEntry {
|
||||
MappingInfo first;
|
||||
u_int8_t second[sizeof(MDGUID)];
|
||||
};
|
||||
|
||||
// A list of <MappingInfo, GUID>
|
||||
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
|
||||
typedef std::list<MappingEntry> MappingList;
|
||||
|
||||
// Write a minidump to the filesystem. This function does not malloc nor use
|
||||
// These entries store a list of memory regions that the client wants included
|
||||
// in the minidump.
|
||||
struct AppMemory {
|
||||
void* ptr;
|
||||
size_t length;
|
||||
|
||||
bool operator==(const struct AppMemory& other) const {
|
||||
return ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool operator==(const void* other) const {
|
||||
return ptr == other;
|
||||
}
|
||||
};
|
||||
typedef std::list<AppMemory> AppMemoryList;
|
||||
|
||||
// Writes a minidump to the filesystem. These functions do not malloc nor use
|
||||
// libc functions which may. Thus, it can be used in contexts where the state
|
||||
// of the heap may be corrupt.
|
||||
// filename: the filename to write to. This is opened O_EXCL and fails if
|
||||
// open fails.
|
||||
// minidump_path: the path to the file to write to. This is opened O_EXCL and
|
||||
// fails open fails.
|
||||
// crashing_process: the pid of the crashing process. This must be trusted.
|
||||
// blob: a blob of data from the crashing process. See exception_handler.h
|
||||
// blob_size: the length of |blob|, in bytes
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
// Same as above but takes an open file descriptor instead of a path.
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
|
||||
// This overload also allows passing a list of known mappings.
|
||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
||||
// Alternate form of WriteMinidump() that works with processes that
|
||||
// are not expected to have crashed. If |process_blamed_thread| is
|
||||
// meaningful, it will be the one from which a crash signature is
|
||||
// extracted. It is not expected that this function will be called
|
||||
// from a compromised context, but it is safe to do so.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
pid_t process_blamed_thread);
|
||||
|
||||
// These overloads also allow passing a list of known mappings and
|
||||
// a list of additional memory regions to be included in the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings);
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
|
||||
// These overloads also allow passing a file size limit for the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata,
|
||||
LinuxDumper* dumper);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
@ -35,9 +35,33 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; };
|
||||
162F64F3161C577500CD68D5 /* arch_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 162F64F1161C577500CD68D5 /* arch_utilities.h */; };
|
||||
162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; };
|
||||
162F64F5161C579B00CD68D5 /* arch_utilities.h in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F1161C577500CD68D5 /* arch_utilities.h */; };
|
||||
163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 163201D41443019E00C4DBF5 /* ConfigFile.h */; };
|
||||
163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; };
|
||||
163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; };
|
||||
16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C917147D45AE00776EAD /* BreakpadDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
16E02DB8147410F0008C604D /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16E02DB4147410D4008C604D /* uploader.mm */; };
|
||||
3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; };
|
||||
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
|
||||
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; };
|
||||
4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; };
|
||||
4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; };
|
||||
4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; };
|
||||
4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; };
|
||||
4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; };
|
||||
4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; };
|
||||
4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
|
||||
4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
|
||||
4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
|
||||
4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; };
|
||||
8B3101C611F0CD9F00FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
|
||||
8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||
8B3101CA11F0CDB000FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
|
||||
@ -47,9 +71,6 @@
|
||||
8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||
8B3102EB11F0D78000FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||
8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||
8B4BDAAF12012BC5009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
|
||||
8B4BDABE12012CEF009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
|
||||
8B4BDAC512012D05009C7060 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
||||
D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */; };
|
||||
D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; };
|
||||
@ -72,7 +93,6 @@
|
||||
D246419112BAA52F005170D0 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; };
|
||||
D246419512BAA54C005170D0 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; };
|
||||
D246419612BAA55A005170D0 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; };
|
||||
D246419C12BAA65F005170D0 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
|
||||
D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
|
||||
D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
|
||||
D24641EC12BAC6FB005170D0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; };
|
||||
@ -119,7 +139,6 @@
|
||||
D2F9A53B121383A1002747C1 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; };
|
||||
D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
|
||||
D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||
D2F9A540121383A1002747C1 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B4BDAA7120124EA009C7060 /* libcrypto.dylib */; };
|
||||
D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; };
|
||||
D2F9A553121383DC002747C1 /* crash_generation_server_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */; };
|
||||
F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; };
|
||||
@ -540,17 +559,27 @@
|
||||
0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
162F64F0161C577500CD68D5 /* arch_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arch_utilities.cc; path = ../../common/mac/arch_utilities.cc; sourceTree = "<group>"; };
|
||||
162F64F1161C577500CD68D5 /* arch_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = arch_utilities.h; path = ../../common/mac/arch_utilities.h; sourceTree = "<group>"; };
|
||||
163201D41443019E00C4DBF5 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigFile.h; path = crash_generation/ConfigFile.h; sourceTree = "<group>"; };
|
||||
163201D51443019E00C4DBF5 /* ConfigFile.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ConfigFile.mm; path = crash_generation/ConfigFile.mm; sourceTree = "<group>"; };
|
||||
163202431443201300C4DBF5 /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uploader.h; path = sender/uploader.h; sourceTree = "<group>"; };
|
||||
16C7C917147D45AE00776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = "<group>"; };
|
||||
16E02DB4147410D4008C604D /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = uploader.mm; path = sender/uploader.mm; sourceTree = "<group>"; };
|
||||
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
|
||||
3329D4EC0FA16D820007BBC5 /* Breakpad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Breakpad.xib; path = sender/Breakpad.xib; sourceTree = "<group>"; };
|
||||
33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = "<group>"; };
|
||||
4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bootstrap_compat.cc; path = ../../common/mac/bootstrap_compat.cc; sourceTree = SOURCE_ROOT; };
|
||||
4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap_compat.h; path = ../../common/mac/bootstrap_compat.h; sourceTree = SOURCE_ROOT; };
|
||||
4D72CA0D13DFAD5C006CABE3 /* md5.cc */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = md5.cc; path = ../../common/md5.cc; sourceTree = SOURCE_ROOT; };
|
||||
4DBE4769134A4F080072546A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
|
||||
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = ../../common/mac/GTMDefines.h; sourceTree = SOURCE_ROOT; };
|
||||
8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMGarbageCollection.h; path = ../../common/mac/GTMGarbageCollection.h; sourceTree = SOURCE_ROOT; };
|
||||
8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8B4BDAA7120124EA009C7060 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
|
||||
8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = tests/minidump_generator_test.cc; sourceTree = "<group>"; };
|
||||
D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test_helper.cc; path = tests/minidump_generator_test_helper.cc; sourceTree = "<group>"; };
|
||||
@ -572,28 +601,28 @@
|
||||
D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server.cc; path = crash_generation/crash_generation_server.cc; sourceTree = "<group>"; };
|
||||
D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server_test.cc; path = tests/crash_generation_server_test.cc; sourceTree = "<group>"; };
|
||||
D2F9A546121383A1002747C1 /* crash_generation_server_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_generation_server_test; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43469011C72976004F095F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
DE43469011C72976004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BreakpadFramework_Test.mm; path = tests/BreakpadFramework_Test.mm; sourceTree = "<group>"; };
|
||||
F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = "<group>"; };
|
||||
F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = "<group>"; };
|
||||
@ -661,7 +690,7 @@
|
||||
F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = "<group>"; };
|
||||
F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = "<group>"; };
|
||||
F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = "<group>"; };
|
||||
F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = testapp/English.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = "<group>"; };
|
||||
F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = "<group>"; };
|
||||
@ -691,7 +720,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D246418412BAA4BA005170D0 /* Foundation.framework in Frameworks */,
|
||||
D246419C12BAA65F005170D0 /* libcrypto.dylib in Frameworks */,
|
||||
4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -707,8 +736,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */,
|
||||
D2F9A540121383A1002747C1 /* libcrypto.dylib in Frameworks */,
|
||||
D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */,
|
||||
4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -727,7 +756,6 @@
|
||||
files = (
|
||||
8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */,
|
||||
F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */,
|
||||
8B4BDAAF12012BC5009C7060 /* libcrypto.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -746,8 +774,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */,
|
||||
8B4BDABE12012CEF009C7060 /* libcrypto.dylib in Frameworks */,
|
||||
D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */,
|
||||
4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -763,8 +791,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */,
|
||||
8B4BDAC512012D05009C7060 /* libcrypto.dylib in Frameworks */,
|
||||
D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */,
|
||||
4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -833,17 +861,34 @@
|
||||
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8B4BDAA7120124EA009C7060 /* libcrypto.dylib */,
|
||||
8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */,
|
||||
F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */,
|
||||
F92C554A0ECCF530009BE4BA /* Carbon.framework */,
|
||||
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
|
||||
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
|
||||
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
|
||||
4DBE4769134A4F080072546A /* CoreServices.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
16C7C915147D45AE00776EAD /* apple */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C916147D45AE00776EAD /* Framework */,
|
||||
);
|
||||
name = apple;
|
||||
path = ../apple;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
16C7C916147D45AE00776EAD /* Framework */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C917147D45AE00776EAD /* BreakpadDefines.h */,
|
||||
);
|
||||
path = Framework;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D244536912426EE7009BBCE0 /* processor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -890,6 +935,7 @@
|
||||
D244540A12439BA0009BBCE0 /* memory_unittest.cc */,
|
||||
F92C53870ECCE6C0009BE4BA /* convert_UTF.c */,
|
||||
F92C53880ECCE6C0009BE4BA /* convert_UTF.h */,
|
||||
4D72CA0D13DFAD5C006CABE3 /* md5.cc */,
|
||||
F92C53850ECCE6AD009BE4BA /* string_conversion.cc */,
|
||||
F92C53860ECCE6AD009BE4BA /* string_conversion.h */,
|
||||
F92C53840ECCE68D009BE4BA /* mac */,
|
||||
@ -900,6 +946,8 @@
|
||||
F92C53840ECCE68D009BE4BA /* mac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
162F64F0161C577500CD68D5 /* arch_utilities.cc */,
|
||||
162F64F1161C577500CD68D5 /* arch_utilities.h */,
|
||||
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */,
|
||||
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */,
|
||||
F9C77E0F0F7DDF650045F7DB /* testing */,
|
||||
@ -911,6 +959,8 @@
|
||||
F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */,
|
||||
F92C53780ECCE635009BE4BA /* MachIPC.h */,
|
||||
F92C53790ECCE635009BE4BA /* MachIPC.mm */,
|
||||
4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */,
|
||||
4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */,
|
||||
F92C537A0ECCE635009BE4BA /* macho_id.cc */,
|
||||
F92C537B0ECCE635009BE4BA /* macho_id.h */,
|
||||
F92C537C0ECCE635009BE4BA /* macho_utilities.cc */,
|
||||
@ -928,6 +978,7 @@
|
||||
F92C538D0ECCE6F2009BE4BA /* client */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16C7C915147D45AE00776EAD /* apple */,
|
||||
F92C53990ECCE78E009BE4BA /* mac */,
|
||||
F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */,
|
||||
F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */,
|
||||
@ -953,6 +1004,8 @@
|
||||
F92C53B50ECCE799009BE4BA /* crash_generation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
163201D41443019E00C4DBF5 /* ConfigFile.h */,
|
||||
163201D51443019E00C4DBF5 /* ConfigFile.mm */,
|
||||
D2F9A4C4121336C7002747C1 /* client_info.h */,
|
||||
D2F9A4C5121336C7002747C1 /* crash_generation_client.h */,
|
||||
D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */,
|
||||
@ -980,6 +1033,8 @@
|
||||
F92C56A60ECE04B6009BE4BA /* sender */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
16E02DB4147410D4008C604D /* uploader.mm */,
|
||||
163202431443201300C4DBF5 /* uploader.h */,
|
||||
F9B6309F100FF96B00D0F4AC /* goArrow.png */,
|
||||
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
|
||||
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
|
||||
@ -1093,6 +1148,9 @@
|
||||
D2F9A4C9121336C7002747C1 /* client_info.h in Headers */,
|
||||
D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */,
|
||||
D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */,
|
||||
163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */,
|
||||
16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */,
|
||||
162F64F3161C577500CD68D5 /* arch_utilities.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1337,6 +1395,7 @@
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
||||
compatibilityVersion = "Xcode 3.1";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
English,
|
||||
@ -1615,6 +1674,8 @@
|
||||
F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */,
|
||||
D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */,
|
||||
D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */,
|
||||
163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */,
|
||||
162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1637,6 +1698,8 @@
|
||||
D246419612BAA55A005170D0 /* macho_id.cc in Sources */,
|
||||
D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */,
|
||||
D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */,
|
||||
4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */,
|
||||
4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1674,6 +1737,8 @@
|
||||
D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */,
|
||||
D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */,
|
||||
D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */,
|
||||
4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */,
|
||||
4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1683,6 +1748,8 @@
|
||||
files = (
|
||||
F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */,
|
||||
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */,
|
||||
163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */,
|
||||
4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1690,6 +1757,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */,
|
||||
162F64F5161C579B00CD68D5 /* arch_utilities.h in Sources */,
|
||||
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */,
|
||||
F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */,
|
||||
F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */,
|
||||
@ -1698,11 +1767,13 @@
|
||||
F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */,
|
||||
F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */,
|
||||
F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */,
|
||||
4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */,
|
||||
F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */,
|
||||
F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */,
|
||||
F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */,
|
||||
F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */,
|
||||
F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */,
|
||||
4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1713,6 +1784,7 @@
|
||||
F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */,
|
||||
F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */,
|
||||
F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */,
|
||||
16E02DB8147410F0008C604D /* uploader.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1740,6 +1812,8 @@
|
||||
F93803D60F8083B7004D428B /* macho_walker.cc in Sources */,
|
||||
F93803D70F8083B7004D428B /* string_utilities.cc in Sources */,
|
||||
D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */,
|
||||
4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */,
|
||||
4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -1779,6 +1853,8 @@
|
||||
F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */,
|
||||
D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */,
|
||||
D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */,
|
||||
4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */,
|
||||
4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -2044,9 +2120,6 @@
|
||||
buildSettings = {
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
|
||||
SDKROOT = macosx10.5;
|
||||
"SDKROOT[arch=i386]" = macosx10.4;
|
||||
"SDKROOT[arch=ppc]" = macosx10.4;
|
||||
"SDKROOT[arch=x86_64]" = macosx10.6;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -2080,6 +2153,7 @@
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_generator_test_helper;
|
||||
@ -2094,6 +2168,7 @@
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
GCC_MODEL_TUNING = G5;
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
INSTALL_PATH = /usr/local/bin;
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = minidump_generator_test_helper;
|
||||
@ -2109,7 +2184,7 @@
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
$inherited,
|
||||
"$(inherited)",
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
@ -2124,6 +2199,13 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = gtest;
|
||||
};
|
||||
@ -2136,6 +2218,13 @@
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
PREBINDING = NO;
|
||||
PRODUCT_NAME = gtest;
|
||||
ZERO_LINK = NO;
|
||||
@ -2167,24 +2256,36 @@
|
||||
D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\\\"$(SRCROOT)/build/Debug\\\"",
|
||||
);
|
||||
PRODUCT_NAME = handler_test;
|
||||
PRODUCT_NAME = crash_generation_server_test;
|
||||
};
|
||||
name = "Debug With Code Coverage";
|
||||
};
|
||||
D2F9A545121383A1002747C1 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\\\"$(SRCROOT)/build/Debug\\\"",
|
||||
);
|
||||
PRODUCT_NAME = handler_test;
|
||||
PRODUCT_NAME = crash_generation_server_test;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -2271,7 +2372,14 @@
|
||||
F93803C10F808210004D428B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../..,
|
||||
../../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
PRODUCT_NAME = generator_test;
|
||||
};
|
||||
name = Release;
|
||||
@ -2318,7 +2426,14 @@
|
||||
F93DE32F0F82C55700608B94 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../..,
|
||||
../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/build/Debug\"",
|
||||
@ -2417,7 +2532,14 @@
|
||||
F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../..,
|
||||
../../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
PRODUCT_NAME = generator_test;
|
||||
};
|
||||
name = "Debug With Code Coverage";
|
||||
@ -2433,7 +2555,14 @@
|
||||
F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = ../..;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
../../..,
|
||||
../..,
|
||||
../../testing,
|
||||
../../testing/include,
|
||||
../../testing/gtest,
|
||||
../../testing/gtest/include,
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/build/Debug\"",
|
||||
|
@ -41,6 +41,10 @@
|
||||
// modify the default behavior to suit your needs and wants and
|
||||
// desires.
|
||||
|
||||
// A service name associated with the original bootstrap parent port, saved in
|
||||
// OnDemandServer and restored in Inspector.
|
||||
#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent"
|
||||
|
||||
typedef void *BreakpadRef;
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -50,49 +54,7 @@ extern "C" {
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
// Keys for configuration file
|
||||
#define kReporterMinidumpDirectoryKey "MinidumpDir"
|
||||
#define kReporterMinidumpIDKey "MinidumpID"
|
||||
|
||||
// Filename for recording uploaded IDs
|
||||
#define kReporterLogFilename "uploads.log"
|
||||
|
||||
// The default subdirectory of the Library to put crash dumps in
|
||||
// The subdirectory is
|
||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
||||
#define kDefaultLibrarySubdirectory "Breakpad"
|
||||
|
||||
// Specify some special keys to be used in the configuration file that is
|
||||
// generated by Breakpad and consumed by the crash_sender.
|
||||
#define BREAKPAD_PRODUCT "BreakpadProduct"
|
||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay"
|
||||
#define BREAKPAD_VERSION "BreakpadVersion"
|
||||
#define BREAKPAD_VENDOR "BreakpadVendor"
|
||||
#define BREAKPAD_URL "BreakpadURL"
|
||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval"
|
||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm"
|
||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout"
|
||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit"
|
||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation"
|
||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation"
|
||||
#define BREAKPAD_REPORTER_EXE_LOCATION \
|
||||
"BreakpadReporterExeLocation"
|
||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles"
|
||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize"
|
||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments"
|
||||
#define BREAKPAD_COMMENTS "BreakpadComments"
|
||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail"
|
||||
#define BREAKPAD_EMAIL "BreakpadEmail"
|
||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType"
|
||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters"
|
||||
|
||||
// The keys below are NOT user supplied, and are used internally.
|
||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime"
|
||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime"
|
||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime"
|
||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile"
|
||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
|
||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
|
||||
#include "BreakpadDefines.h"
|
||||
|
||||
// Optional user-defined function to dec to decide if we should handle
|
||||
// this crash or forward it along.
|
||||
@ -224,13 +186,15 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
|
||||
// completeness. They are calculated by Breakpad during initialization &
|
||||
// crash-dump generation, or entered in by the user.
|
||||
//
|
||||
// BREAKPAD_PROCESS_START_TIME The time the process started.
|
||||
// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
|
||||
// process started
|
||||
//
|
||||
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
|
||||
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
|
||||
// process crashed.
|
||||
//
|
||||
// BREAKPAD_PROCESS_UP_TIME The total time the process has been
|
||||
// running. This parameter is not set
|
||||
// until the crash-dump-generation phase.
|
||||
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
|
||||
// has been running. This parameter is not
|
||||
// set until the crash-dump-generation phase.
|
||||
//
|
||||
// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
|
||||
// parameter dictionary correspond to log
|
||||
|
@ -39,20 +39,19 @@
|
||||
#define DEBUGLOG if (gDebugLog) fprintf
|
||||
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||
|
||||
#import "common/mac/MachIPC.h"
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
#import "client/mac/Framework/Breakpad.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <sys/stat.h>
|
||||
#import <sys/sysctl.h>
|
||||
|
||||
#import "client/mac/crash_generation/Inspector.h"
|
||||
#import "client/mac/handler/exception_handler.h"
|
||||
#import "client/mac/Framework/Breakpad.h"
|
||||
#import "client/mac/Framework/OnDemandServer.h"
|
||||
#import "client/mac/handler/protected_memory_allocator.h"
|
||||
|
||||
#import <sys/stat.h>
|
||||
#import <sys/sysctl.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "common/mac/MachIPC.h"
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
|
||||
using google_breakpad::KeyValueEntry;
|
||||
using google_breakpad::MachPortSender;
|
||||
|
@ -27,13 +27,12 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <iostream>
|
||||
#import <mach/mach.h>
|
||||
#import <servers/bootstrap.h>
|
||||
#import <stdio.h>
|
||||
#import <stdlib.h>
|
||||
#import <sys/stat.h>
|
||||
#import <unistd.h>
|
||||
#include <mach/mach.h>
|
||||
#include <servers/bootstrap.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//==============================================================================
|
||||
// class OnDemandServer :
|
||||
|
@ -29,11 +29,23 @@
|
||||
|
||||
#import "OnDemandServer.h"
|
||||
|
||||
#import "Breakpad.h"
|
||||
#include "common/mac/bootstrap_compat.h"
|
||||
|
||||
#if DEBUG
|
||||
#define PRINT_MACH_RESULT(result_, message_) \
|
||||
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
|
||||
#if defined(MAC_OS_X_VERSION_10_5) && \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
|
||||
printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ );
|
||||
#else
|
||||
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
|
||||
PRINT_MACH_RESULT(result_, message_)
|
||||
#endif
|
||||
#else
|
||||
#define PRINT_MACH_RESULT(result_, message_)
|
||||
#define PRINT_BOOTSTRAP_RESULT(result_, message_)
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
@ -67,34 +79,66 @@ kern_return_t OnDemandServer::Initialize(const char *server_command,
|
||||
bool unregister_on_cleanup) {
|
||||
unregister_on_cleanup_ = unregister_on_cleanup;
|
||||
|
||||
kern_return_t kr =
|
||||
bootstrap_create_server(bootstrap_port,
|
||||
const_cast<char*>(server_command),
|
||||
geteuid(), // server uid
|
||||
true,
|
||||
&server_port_);
|
||||
mach_port_t self_task = mach_task_self();
|
||||
|
||||
mach_port_t bootstrap_port;
|
||||
kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "bootstrap_create_server() : ");
|
||||
PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): ");
|
||||
return kr;
|
||||
}
|
||||
|
||||
mach_port_t bootstrap_subset_port;
|
||||
kr = bootstrap_subset(bootstrap_port, self_task, &bootstrap_subset_port);
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): ");
|
||||
return kr;
|
||||
}
|
||||
|
||||
// The inspector will be invoked with its bootstrap port set to the subset,
|
||||
// but the sender will need access to the original bootstrap port. Although
|
||||
// the original port is the subset's parent, bootstrap_parent can't be used
|
||||
// because it requires extra privileges. Stash the original bootstrap port
|
||||
// in the subset by registering it under a known name. The inspector will
|
||||
// recover this port and set it as its own bootstrap port in Inspector.mm
|
||||
// Inspector::ResetBootstrapPort.
|
||||
kr = breakpad::BootstrapRegister(
|
||||
bootstrap_subset_port,
|
||||
const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
|
||||
bootstrap_port);
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_register(): ");
|
||||
return kr;
|
||||
}
|
||||
|
||||
kr = bootstrap_create_server(bootstrap_subset_port,
|
||||
const_cast<char*>(server_command),
|
||||
geteuid(), // server uid
|
||||
true,
|
||||
&server_port_);
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_server(): ");
|
||||
return kr;
|
||||
}
|
||||
|
||||
strlcpy(service_name_, service_name, sizeof(service_name_));
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// Create a service called service_name, and return send rights to
|
||||
// that port in service_port_.
|
||||
kr = bootstrap_create_service(server_port_,
|
||||
const_cast<char*>(service_name),
|
||||
&service_port_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
|
||||
#pragma clang diagnostic pop
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): ");
|
||||
|
||||
// perhaps the service has already been created - try to look it up
|
||||
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "bootstrap_look_up() : ");
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): ");
|
||||
Unregister(); // clean up server port
|
||||
return kr;
|
||||
}
|
||||
@ -131,9 +175,9 @@ void OnDemandServer::Unregister() {
|
||||
|
||||
if (server_port_ != MACH_PORT_NULL) {
|
||||
// unregister the service
|
||||
kern_return_t kr = bootstrap_register(server_port_,
|
||||
service_name_,
|
||||
MACH_PORT_NULL);
|
||||
kern_return_t kr = breakpad::BootstrapRegister(server_port_,
|
||||
service_name_,
|
||||
MACH_PORT_NULL);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");
|
||||
|
83
3rdparty/google-breakpad/client/mac/crash_generation/ConfigFile.h
vendored
Normal file
83
3rdparty/google-breakpad/client/mac/crash_generation/ConfigFile.h
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
//
|
||||
// Utility class that can persist a SimpleStringDictionary to disk.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
BOOL EnsureDirectoryPathExists(NSString *dirPath);
|
||||
|
||||
//=============================================================================
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile() {
|
||||
config_file_ = -1;
|
||||
config_file_path_[0] = 0;
|
||||
has_created_file_ = false;
|
||||
};
|
||||
|
||||
~ConfigFile() {
|
||||
};
|
||||
|
||||
void WriteFile(const char* directory,
|
||||
const SimpleStringDictionary *configurationParameters,
|
||||
const char *dump_dir,
|
||||
const char *minidump_id);
|
||||
|
||||
const char *GetFilePath() { return config_file_path_; }
|
||||
|
||||
void Unlink() {
|
||||
if (config_file_ != -1)
|
||||
unlink(config_file_path_);
|
||||
|
||||
config_file_ = -1;
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL WriteData(const void *data, size_t length);
|
||||
|
||||
BOOL AppendConfigData(const char *key,
|
||||
const void *data,
|
||||
size_t length);
|
||||
|
||||
BOOL AppendConfigString(const char *key,
|
||||
const char *value);
|
||||
|
||||
BOOL AppendCrashTimeParameters(const char *processStartTimeString);
|
||||
|
||||
int config_file_; // descriptor for config file
|
||||
char config_file_path_[PATH_MAX]; // Path to configuration file
|
||||
bool has_created_file_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
@ -33,9 +33,10 @@
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
#include <mach/mach.h>
|
||||
|
||||
#define VERBOSE 0
|
||||
#import "client/mac/crash_generation/ConfigFile.h"
|
||||
#import "client/mac/handler/minidump_generator.h"
|
||||
|
||||
extern bool gDebugLog;
|
||||
|
||||
@ -78,52 +79,10 @@ using google_breakpad::MinidumpGenerator;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static BOOL EnsureDirectoryPathExists(NSString *dirPath);
|
||||
|
||||
//=============================================================================
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile() {
|
||||
config_file_ = -1;
|
||||
config_file_path_[0] = 0;
|
||||
has_created_file_ = false;
|
||||
};
|
||||
|
||||
~ConfigFile() {
|
||||
};
|
||||
|
||||
void WriteFile(const SimpleStringDictionary *configurationParameters,
|
||||
const char *dump_dir,
|
||||
const char *minidump_id);
|
||||
|
||||
const char *GetFilePath() { return config_file_path_; }
|
||||
|
||||
void Unlink() {
|
||||
if (config_file_ != -1)
|
||||
unlink(config_file_path_);
|
||||
|
||||
config_file_ = -1;
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL WriteData(const void *data, size_t length);
|
||||
|
||||
BOOL AppendConfigData(const char *key,
|
||||
const void *data,
|
||||
size_t length);
|
||||
|
||||
BOOL AppendConfigString(const char *key,
|
||||
const char *value);
|
||||
|
||||
int config_file_; // descriptor for config file
|
||||
char config_file_path_[PATH_MAX]; // Path to configuration file
|
||||
bool has_created_file_;
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
class MinidumpLocation {
|
||||
public:
|
||||
MinidumpLocation(const NSString *minidumpDir) {
|
||||
MinidumpLocation(NSString *minidumpDir) {
|
||||
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
|
||||
assert(minidumpDir);
|
||||
if (!EnsureDirectoryPathExists(minidumpDir)) {
|
||||
@ -163,6 +122,18 @@ class Inspector {
|
||||
void Inspect(const char *receive_port_name);
|
||||
|
||||
private:
|
||||
// The Inspector is invoked with its bootstrap port set to the bootstrap
|
||||
// subset established in OnDemandServer.mm OnDemandServer::Initialize.
|
||||
// For proper communication with the system, the sender (which will inherit
|
||||
// the Inspector's bootstrap port) needs the per-session bootstrap namespace
|
||||
// available directly in its bootstrap port. OnDemandServer stashed this
|
||||
// port into the subset namespace under a special name. ResetBootstrapPort
|
||||
// recovers this port and switches this task to use it as its own bootstrap
|
||||
// (ensuring that children like the sender will inherit it), and saves the
|
||||
// subset in bootstrap_subset_port_ for use by ServiceCheckIn and
|
||||
// ServiceCheckOut.
|
||||
kern_return_t ResetBootstrapPort();
|
||||
|
||||
kern_return_t ServiceCheckIn(const char *receive_port_name);
|
||||
kern_return_t ServiceCheckOut(const char *receive_port_name);
|
||||
|
||||
@ -172,7 +143,9 @@ class Inspector {
|
||||
kern_return_t SendAcknowledgement();
|
||||
void LaunchReporter(const char *inConfigFilePath);
|
||||
|
||||
void SetCrashTimeParameters();
|
||||
// The bootstrap port in which the inspector is registered and into which it
|
||||
// must check in.
|
||||
mach_port_t bootstrap_subset_port_;
|
||||
|
||||
mach_port_t service_rcv_port_;
|
||||
|
||||
|
@ -31,10 +31,10 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <servers/bootstrap.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <sys/time.h>
|
||||
|
||||
#import "client/mac/crash_generation/Inspector.h"
|
||||
|
||||
@ -43,167 +43,22 @@
|
||||
|
||||
#import "common/mac/SimpleStringDictionary.h"
|
||||
#import "common/mac/MachIPC.h"
|
||||
#include "common/mac/bootstrap_compat.h"
|
||||
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if VERBOSE
|
||||
bool gDebugLog = true;
|
||||
#else
|
||||
bool gDebugLog = false;
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//=============================================================================
|
||||
static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
|
||||
// If we got a relative path, prepend the current directory
|
||||
if (![dirPath isAbsolutePath])
|
||||
dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath];
|
||||
|
||||
NSString *path = dirPath;
|
||||
|
||||
// Ensure that no file exists within the path which would block creation
|
||||
while (1) {
|
||||
BOOL isDir;
|
||||
if ([mgr fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (isDir)
|
||||
break;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
path = [path stringByDeletingLastPathComponent];
|
||||
}
|
||||
|
||||
// Path now contains the first valid directory (or is empty)
|
||||
if (![path length])
|
||||
return NO;
|
||||
|
||||
NSString *common =
|
||||
[dirPath commonPrefixWithString:path options:NSLiteralSearch];
|
||||
|
||||
// If everything is good
|
||||
if ([common isEqualToString:dirPath])
|
||||
return YES;
|
||||
|
||||
// Break up the difference into components
|
||||
NSString *diff = [dirPath substringFromIndex:[common length] + 1];
|
||||
NSArray *components = [diff pathComponents];
|
||||
NSUInteger count = [components count];
|
||||
|
||||
// Rebuild the path one component at a time
|
||||
NSDictionary *attrs =
|
||||
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
|
||||
forKey:NSFilePosixPermissions];
|
||||
path = common;
|
||||
for (NSUInteger i = 0; i < count; ++i) {
|
||||
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
|
||||
|
||||
if (![mgr createDirectoryAtPath:path attributes:attrs])
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::WriteData(const void *data, size_t length) {
|
||||
size_t result = write(config_file_, data, length);
|
||||
|
||||
return result == length;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::AppendConfigData(const char *key,
|
||||
const void *data, size_t length) {
|
||||
assert(config_file_ != -1);
|
||||
|
||||
if (!key) {
|
||||
DEBUGLOG(stderr, "Breakpad: Missing Key\n");
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key :
|
||||
"<Unknown Key>");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Write the key, \n, length of data (ascii integer), \n, data
|
||||
char buffer[16];
|
||||
char nl = '\n';
|
||||
BOOL result = WriteData(key, strlen(key));
|
||||
|
||||
snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
|
||||
result &= WriteData(buffer, strlen(buffer));
|
||||
result &= WriteData(data, length);
|
||||
result &= WriteData(&nl, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
BOOL ConfigFile::AppendConfigString(const char *key,
|
||||
const char *value) {
|
||||
return AppendConfigData(key, value, strlen(value));
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters,
|
||||
const char *dump_dir,
|
||||
const char *minidump_id) {
|
||||
|
||||
assert(config_file_ == -1);
|
||||
|
||||
// Open and write out configuration file preamble
|
||||
strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
|
||||
sizeof(config_file_path_));
|
||||
config_file_ = mkstemp(config_file_path_);
|
||||
|
||||
if (config_file_ == -1) {
|
||||
DEBUGLOG(stderr,
|
||||
"mkstemp(config_file_path_) == -1 (%s)\n",
|
||||
strerror(errno));
|
||||
void Inspector::Inspect(const char *receive_port_name) {
|
||||
kern_return_t result = ResetBootstrapPort();
|
||||
if (result != KERN_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
|
||||
}
|
||||
|
||||
has_created_file_ = true;
|
||||
|
||||
// Add the minidump dir
|
||||
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
|
||||
AppendConfigString(kReporterMinidumpIDKey, minidump_id);
|
||||
|
||||
// Write out the configuration parameters
|
||||
BOOL result = YES;
|
||||
const SimpleStringDictionary &dictionary = *configurationParameters;
|
||||
|
||||
const KeyValueEntry *entry = NULL;
|
||||
SimpleStringDictionaryIterator iter(dictionary);
|
||||
|
||||
while ((entry = iter.Next())) {
|
||||
DEBUGLOG(stderr,
|
||||
"config: (%s) -> (%s)\n",
|
||||
entry->GetKey(),
|
||||
entry->GetValue());
|
||||
result = AppendConfigString(entry->GetKey(), entry->GetValue());
|
||||
|
||||
if (!result)
|
||||
break;
|
||||
}
|
||||
|
||||
close(config_file_);
|
||||
config_file_ = -1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void Inspector::Inspect(const char *receive_port_name) {
|
||||
kern_return_t result = ServiceCheckIn(receive_port_name);
|
||||
result = ServiceCheckIn(receive_port_name);
|
||||
|
||||
if (result == KERN_SUCCESS) {
|
||||
result = ReadMessages();
|
||||
@ -240,11 +95,56 @@ void Inspector::Inspect(const char *receive_port_name) {
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
kern_return_t Inspector::ResetBootstrapPort() {
|
||||
// A reasonable default, in case anything fails.
|
||||
bootstrap_subset_port_ = bootstrap_port;
|
||||
|
||||
mach_port_t self_task = mach_task_self();
|
||||
|
||||
kern_return_t kr = task_get_bootstrap_port(self_task,
|
||||
&bootstrap_subset_port_);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)",
|
||||
mach_error_string(kr), kr);
|
||||
return kr;
|
||||
}
|
||||
|
||||
mach_port_t bootstrap_parent_port;
|
||||
kr = bootstrap_look_up(bootstrap_subset_port_,
|
||||
const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
|
||||
&bootstrap_parent_port);
|
||||
if (kr != BOOTSTRAP_SUCCESS) {
|
||||
NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)",
|
||||
#if defined(MAC_OS_X_VERSION_10_5) && \
|
||||
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
bootstrap_strerror(kr),
|
||||
#else
|
||||
mach_error_string(kr),
|
||||
#endif
|
||||
kr);
|
||||
return kr;
|
||||
}
|
||||
|
||||
kr = task_set_bootstrap_port(self_task, bootstrap_parent_port);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)",
|
||||
mach_error_string(kr), kr);
|
||||
return kr;
|
||||
}
|
||||
|
||||
// Some things access the bootstrap port through this global variable
|
||||
// instead of calling task_get_bootstrap_port.
|
||||
bootstrap_port = bootstrap_parent_port;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
|
||||
// We need to get the mach port representing this service, so we can
|
||||
// get information from the crashed process.
|
||||
kern_return_t kr = bootstrap_check_in(bootstrap_port,
|
||||
kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_,
|
||||
(char*)receive_port_name,
|
||||
&service_rcv_port_);
|
||||
|
||||
@ -275,9 +175,9 @@ kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
|
||||
}
|
||||
|
||||
// Unregister the service associated with the receive port.
|
||||
kr = bootstrap_register(bootstrap_port,
|
||||
(char*)receive_port_name,
|
||||
MACH_PORT_NULL);
|
||||
kr = breakpad::BootstrapRegister(bootstrap_subset_port_,
|
||||
(char*)receive_port_name,
|
||||
MACH_PORT_NULL);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
|
||||
@ -363,30 +263,6 @@ kern_return_t Inspector::ReadMessages() {
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Sets keys in the parameters dictionary that are specific to process uptime.
|
||||
// The two we set are process up time, and process crash time.
|
||||
void Inspector::SetCrashTimeParameters() {
|
||||
// Set process uptime parameter
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
char processUptimeString[32], processCrashtimeString[32];
|
||||
const char *processStartTimeString =
|
||||
config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME);
|
||||
|
||||
// Set up time if we've received the start time.
|
||||
if (processStartTimeString) {
|
||||
time_t processStartTime = strtol(processStartTimeString, NULL, 10);
|
||||
time_t processUptime = tv.tv_sec - processStartTime;
|
||||
sprintf(processUptimeString, "%zd", processUptime);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
|
||||
}
|
||||
|
||||
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
|
||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
|
||||
processCrashtimeString);
|
||||
}
|
||||
|
||||
bool Inspector::InspectTask() {
|
||||
// keep the task quiet while we're looking at it
|
||||
task_suspend(remote_task_);
|
||||
@ -397,7 +273,6 @@ bool Inspector::InspectTask() {
|
||||
const char *minidumpDirectory =
|
||||
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
||||
|
||||
SetCrashTimeParameters();
|
||||
// If the client app has not specified a minidump directory,
|
||||
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
|
||||
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
|
||||
@ -448,7 +323,8 @@ bool Inspector::InspectTask() {
|
||||
[minidumpPath UTF8String]);
|
||||
|
||||
|
||||
config_file_.WriteFile( &config_params_,
|
||||
config_file_.WriteFile( 0,
|
||||
&config_params_,
|
||||
minidumpLocation.GetPath(),
|
||||
minidumpLocation.GetID());
|
||||
|
||||
|
@ -37,6 +37,8 @@ namespace google_breakpad {
|
||||
|
||||
CrashGenerationServer::CrashGenerationServer(
|
||||
const char *mach_port_name,
|
||||
FilterCallback filter,
|
||||
void *filter_context,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
@ -44,6 +46,8 @@ CrashGenerationServer::CrashGenerationServer(
|
||||
bool generate_dumps,
|
||||
const std::string &dump_path)
|
||||
: dump_callback_(dump_callback),
|
||||
filter_(filter),
|
||||
filter_context_(filter_context),
|
||||
dump_context_(dump_context),
|
||||
exit_callback_(exit_callback),
|
||||
exit_context_(exit_context),
|
||||
@ -110,7 +114,7 @@ bool CrashGenerationServer::WaitForOneMessage() {
|
||||
|
||||
bool result;
|
||||
std::string dump_path;
|
||||
if (generate_dumps_) {
|
||||
if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
|
||||
ScopedTaskSuspend suspend(remote_task);
|
||||
|
||||
MinidumpGenerator generator(remote_task, handler_thread);
|
||||
|
@ -65,10 +65,14 @@ class CrashGenerationServer {
|
||||
|
||||
typedef void (*OnClientExitingCallback)(void *context,
|
||||
const ClientInfo &client_info);
|
||||
// If a FilterCallback returns false, the dump will not be written.
|
||||
typedef bool (*FilterCallback)(void *context);
|
||||
|
||||
// Create an instance with the given parameters.
|
||||
//
|
||||
// mach_port_name: Named server port to listen on.
|
||||
// filter: Callback for a client to cancel writing a dump.
|
||||
// filter_context: Context for the filter callback.
|
||||
// dump_callback: Callback for a client crash dump request.
|
||||
// dump_context: Context for client crash dump request callback.
|
||||
// exit_callback: Callback for client process exit.
|
||||
@ -80,6 +84,8 @@ class CrashGenerationServer {
|
||||
// dump_path: Path for generating dumps; required only if true is
|
||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
||||
CrashGenerationServer(const char *mach_port_name,
|
||||
FilterCallback filter,
|
||||
void *filter_context,
|
||||
OnClientDumpRequestCallback dump_callback,
|
||||
void *dump_context,
|
||||
OnClientExitingCallback exit_callback,
|
||||
@ -109,6 +115,9 @@ class CrashGenerationServer {
|
||||
// if a quit message was received or if an error occurred.
|
||||
bool WaitForOneMessage();
|
||||
|
||||
FilterCallback filter_;
|
||||
void *filter_context_;
|
||||
|
||||
OnClientDumpRequestCallback dump_callback_;
|
||||
void *dump_context_;
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
|
||||
#include "breakpad_nlist_64.h"
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <fcntl.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/loader.h>
|
||||
@ -189,25 +190,25 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
|
||||
struct exec buf;
|
||||
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
|
||||
(N_BADMAG(buf) && *((long *)&buf) != magic &&
|
||||
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
|
||||
/* The following is the big-endian ppc64 check */
|
||||
(*((long*)&buf)) != FAT_MAGIC) {
|
||||
(N_BADMAG(buf) && *((uint32_t *)&buf) != magic &&
|
||||
CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
|
||||
/* The following is the big-endian ppc64 check */
|
||||
(*((uint32_t*)&buf)) != FAT_MAGIC)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Deal with fat file if necessary */
|
||||
unsigned arch_offset = 0;
|
||||
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
|
||||
if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC ||
|
||||
/* The following is the big-endian ppc64 check */
|
||||
*((unsigned int *)&buf) == FAT_MAGIC) {
|
||||
/* Get host info */
|
||||
host_t host = mach_host_self();
|
||||
unsigned i = HOST_BASIC_INFO_COUNT;
|
||||
unsigned hic = HOST_BASIC_INFO_COUNT;
|
||||
struct host_basic_info hbi;
|
||||
kern_return_t kr;
|
||||
if ((kr = host_info(host, HOST_BASIC_INFO,
|
||||
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
|
||||
(host_info_t)(&hbi), &hic)) != KERN_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
mach_port_deallocate(mach_task_self(), host);
|
||||
@ -222,7 +223,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
}
|
||||
|
||||
/* Convert fat_narchs to host byte order */
|
||||
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch);
|
||||
fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch);
|
||||
|
||||
/* Read in the fat archs */
|
||||
struct fat_arch *fat_archs =
|
||||
@ -232,7 +233,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
}
|
||||
if (read(fd, (char *)fat_archs,
|
||||
sizeof(struct fat_arch) * fh.nfat_arch) !=
|
||||
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) {
|
||||
(ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) {
|
||||
free(fat_archs);
|
||||
return -1;
|
||||
}
|
||||
@ -243,15 +244,15 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
*/
|
||||
for (unsigned i = 0; i < fh.nfat_arch; i++) {
|
||||
fat_archs[i].cputype =
|
||||
NXSwapBigIntToHost(fat_archs[i].cputype);
|
||||
CFSwapInt32BigToHost(fat_archs[i].cputype);
|
||||
fat_archs[i].cpusubtype =
|
||||
NXSwapBigIntToHost(fat_archs[i].cpusubtype);
|
||||
CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
|
||||
fat_archs[i].offset =
|
||||
NXSwapBigIntToHost(fat_archs[i].offset);
|
||||
CFSwapInt32BigToHost(fat_archs[i].offset);
|
||||
fat_archs[i].size =
|
||||
NXSwapBigIntToHost(fat_archs[i].size);
|
||||
CFSwapInt32BigToHost(fat_archs[i].size);
|
||||
fat_archs[i].align =
|
||||
NXSwapBigIntToHost(fat_archs[i].align);
|
||||
CFSwapInt32BigToHost(fat_archs[i].align);
|
||||
}
|
||||
|
||||
struct fat_arch *fap = NULL;
|
||||
@ -296,7 +297,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
return -1;
|
||||
}
|
||||
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
|
||||
mh.sizeofcmds) {
|
||||
(ssize_t)mh.sizeofcmds) {
|
||||
free(load_commands);
|
||||
return -1;
|
||||
}
|
||||
@ -304,7 +305,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
struct load_command *lcp = load_commands;
|
||||
// iterate through all load commands, looking for
|
||||
// LC_SYMTAB load command
|
||||
for (long i = 0; i < mh.ncmds; i++) {
|
||||
for (uint32_t i = 0; i < mh.ncmds; i++) {
|
||||
if (lcp->cmdsize % sizeof(word_type) != 0 ||
|
||||
lcp->cmdsize <= 0 ||
|
||||
(char *)lcp + lcp->cmdsize >
|
||||
@ -360,7 +361,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
||||
if (read(fd, (char *)space, m) != m)
|
||||
break;
|
||||
n -= m;
|
||||
long savpos = lseek(fd, 0, SEEK_CUR);
|
||||
off_t savpos = lseek(fd, 0, SEEK_CUR);
|
||||
if (savpos == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -35,16 +35,43 @@ extern "C" { // needed to compile on Leopard
|
||||
#include <stdio.h>
|
||||
}
|
||||
|
||||
#include "breakpad_nlist_64.h"
|
||||
#include <assert.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <dlfcn.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/task_info.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_nlist_64.h"
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_6
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
|
||||
|
||||
// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
|
||||
// <mach/task_info.h> in the Mac OS X 10.6 SDK.
|
||||
#define TASK_DYLD_INFO 17
|
||||
struct task_dyld_info {
|
||||
mach_vm_address_t all_image_info_addr;
|
||||
mach_vm_size_t all_image_info_size;
|
||||
};
|
||||
typedef struct task_dyld_info task_dyld_info_data_t;
|
||||
typedef struct task_dyld_info *task_dyld_info_t;
|
||||
#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
|
||||
|
||||
#endif
|
||||
|
||||
#endif // !TARGET_OS_IPHONE
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
@ -234,8 +261,8 @@ bool FindTextSection(DynamicImage& image) {
|
||||
reinterpret_cast<const mach_segment_command_type *>(cmd);
|
||||
|
||||
if (!strcmp(seg->segname, "__TEXT")) {
|
||||
image.vmaddr_ = seg->vmaddr;
|
||||
image.vmsize_ = seg->vmsize;
|
||||
image.vmaddr_ = static_cast<mach_vm_address_t>(seg->vmaddr);
|
||||
image.vmsize_ = static_cast<mach_vm_size_t>(seg->vmsize);
|
||||
image.slide_ = 0;
|
||||
|
||||
if (seg->fileoff == 0 && seg->filesize != 0) {
|
||||
@ -336,13 +363,49 @@ static uint64_t LookupSymbol(const char* symbol_name,
|
||||
return list.n_value;
|
||||
}
|
||||
|
||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
|
||||
const char *imageSymbolName = "_dyld_all_image_infos";
|
||||
const char *dyldPath = "/usr/lib/dyld";
|
||||
#if TARGET_OS_IPHONE
|
||||
static bool HasTaskDyldInfo() {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static SInt32 GetOSVersionInternal() {
|
||||
SInt32 os_version = 0;
|
||||
Gestalt(gestaltSystemVersion, &os_version);
|
||||
return os_version;
|
||||
}
|
||||
|
||||
if (Is64Bit())
|
||||
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
|
||||
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
|
||||
static SInt32 GetOSVersion() {
|
||||
static SInt32 os_version = GetOSVersionInternal();
|
||||
return os_version;
|
||||
}
|
||||
|
||||
static bool HasTaskDyldInfo() {
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
|
||||
return true;
|
||||
#else
|
||||
return GetOSVersion() >= 0x1060;
|
||||
#endif
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
|
||||
if (HasTaskDyldInfo()) {
|
||||
task_dyld_info_data_t task_dyld_info;
|
||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
|
||||
if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
|
||||
&count) != KERN_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint64_t)task_dyld_info.all_image_info_addr;
|
||||
} else {
|
||||
const char *imageSymbolName = "_dyld_all_image_infos";
|
||||
const char *dyldPath = "/usr/lib/dyld";
|
||||
|
||||
if (Is64Bit())
|
||||
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
|
||||
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -412,8 +475,6 @@ void ReadImageInfo(DynamicImages& images,
|
||||
mach_header_bytes) != KERN_SUCCESS)
|
||||
continue;
|
||||
|
||||
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
|
||||
|
||||
// Read the file name from the task's memory space.
|
||||
string file_path;
|
||||
if (info.file_path_) {
|
||||
@ -429,7 +490,7 @@ void ReadImageInfo(DynamicImages& images,
|
||||
header_size,
|
||||
info.load_address_,
|
||||
file_path,
|
||||
info.file_mod_date_,
|
||||
static_cast<uintptr_t>(info.file_mod_date_),
|
||||
images.task_,
|
||||
images.cpu_type_);
|
||||
|
||||
|
@ -45,6 +45,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mach_vm_compat.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
@ -281,6 +283,8 @@ class DynamicImages {
|
||||
return CPU_TYPE_POWERPC;
|
||||
#elif defined(__ppc64__)
|
||||
return CPU_TYPE_POWERPC64;
|
||||
#elif defined(__arm__)
|
||||
return CPU_TYPE_ARM;
|
||||
#else
|
||||
#error "GetNativeCPUType not implemented for this architecture"
|
||||
#endif
|
||||
|
@ -28,16 +28,25 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <map>
|
||||
#include <mach/exc.h>
|
||||
#include <mach/mig.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "common/mac/macho_utilities.h"
|
||||
#include "common/mac/scoped_task_suspend-inl.h"
|
||||
#include "google_breakpad/common/minidump_exception_mac.h"
|
||||
|
||||
#ifndef USE_PROTECTED_ALLOCATIONS
|
||||
#if TARGET_OS_IPHONE
|
||||
#define USE_PROTECTED_ALLOCATIONS 1
|
||||
#else
|
||||
#define USE_PROTECTED_ALLOCATIONS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If USE_PROTECTED_ALLOCATIONS is activated then the
|
||||
// gBreakpadAllocator needs to be setup in other code
|
||||
@ -48,9 +57,15 @@
|
||||
extern ProtectedMemoryAllocator *gBreakpadAllocator;
|
||||
#endif
|
||||
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static union {
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
|
||||
#endif
|
||||
google_breakpad::ExceptionHandler *handler;
|
||||
} gProtectedData;
|
||||
|
||||
using std::map;
|
||||
|
||||
// These structures and techniques are illustrated in
|
||||
@ -87,11 +102,11 @@ struct ExceptionReplyMessage {
|
||||
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
|
||||
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#if !TARGET_OS_IPHONE
|
||||
extern "C" {
|
||||
// Forward declarations for functions that need "C" style compilation
|
||||
boolean_t exc_server(mach_msg_header_t *request,
|
||||
mach_msg_header_t *reply);
|
||||
boolean_t exc_server(mach_msg_header_t* request,
|
||||
mach_msg_header_t* reply);
|
||||
|
||||
// This symbol must be visible to dlsym() - see
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
|
||||
@ -102,128 +117,98 @@ extern "C"
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count)
|
||||
__attribute__((visibility("default")));
|
||||
}
|
||||
#endif
|
||||
|
||||
kern_return_t ForwardException(mach_port_t task,
|
||||
mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count);
|
||||
kern_return_t ForwardException(mach_port_t task,
|
||||
mach_port_t failed_thread,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count);
|
||||
|
||||
kern_return_t exception_raise(mach_port_t target_port,
|
||||
mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t exception_code,
|
||||
mach_msg_type_number_t exception_code_count);
|
||||
#if TARGET_OS_IPHONE
|
||||
// Implementation is based on the implementation generated by mig.
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||
mach_msg_header_t* OutHeadP) {
|
||||
OutHeadP->msgh_bits =
|
||||
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
|
||||
OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
|
||||
/* Minimal size: routine() will update it if different */
|
||||
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
|
||||
OutHeadP->msgh_local_port = MACH_PORT_NULL;
|
||||
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
|
||||
|
||||
kern_return_t
|
||||
exception_raise_state(mach_port_t target_port,
|
||||
mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t exception_code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t *target_flavor,
|
||||
thread_state_t in_thread_state,
|
||||
mach_msg_type_number_t in_thread_state_count,
|
||||
thread_state_t out_thread_state,
|
||||
mach_msg_type_number_t *out_thread_state_count);
|
||||
if (InHeadP->msgh_id != 2401) {
|
||||
((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
|
||||
((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
exception_raise_state_identity(mach_port_t target_port,
|
||||
mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t exception_code,
|
||||
mach_msg_type_number_t exception_code_count,
|
||||
thread_state_flavor_t *target_flavor,
|
||||
thread_state_t in_thread_state,
|
||||
mach_msg_type_number_t in_thread_state_count,
|
||||
thread_state_t out_thread_state,
|
||||
mach_msg_type_number_t *out_thread_state_count);
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Request;
|
||||
|
||||
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
Request* In0P = (Request*)InHeadP;
|
||||
Reply* OutP = (Reply*)OutHeadP;
|
||||
|
||||
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count);
|
||||
if (In0P->task.name != mach_task_self()) {
|
||||
return FALSE;
|
||||
}
|
||||
OutP->RetCode = ForwardException(In0P->task.name,
|
||||
In0P->thread.name,
|
||||
In0P->exception,
|
||||
In0P->code,
|
||||
In0P->codeCnt);
|
||||
OutP->NDR = NDR_record;
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
boolean_t breakpad_exc_server(mach_msg_header_t* request,
|
||||
mach_msg_header_t* reply) {
|
||||
return exc_server(request, reply);
|
||||
}
|
||||
|
||||
|
||||
|
||||
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count) {
|
||||
|
||||
// Callback from exc_server()
|
||||
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count) {
|
||||
if (task != mach_task_self()) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
return ForwardException(task, failed_thread, exception, code, code_count);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
ExceptionHandler::ExceptionHandler(const string &dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context,
|
||||
void* callback_context,
|
||||
bool install_handler,
|
||||
const char *port_name)
|
||||
const char* port_name)
|
||||
: dump_path_(),
|
||||
filter_(filter),
|
||||
callback_(callback),
|
||||
@ -239,15 +224,17 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
|
||||
// This will update to the ID and C-string pointers
|
||||
set_dump_path(dump_path);
|
||||
MinidumpGenerator::GatherSystemInformation();
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (port_name)
|
||||
crash_generation_client_.reset(new CrashGenerationClient(port_name));
|
||||
#endif
|
||||
Setup(install_handler);
|
||||
}
|
||||
|
||||
// special constructor if we want to bypass minidump writing and
|
||||
// simply get a callback with the exception information
|
||||
ExceptionHandler::ExceptionHandler(DirectCallback callback,
|
||||
void *callback_context,
|
||||
void* callback_context,
|
||||
bool install_handler)
|
||||
: dump_path_(),
|
||||
filter_(NULL),
|
||||
@ -299,18 +286,18 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
|
||||
bool ExceptionHandler::WriteMinidump(const string &dump_path,
|
||||
bool write_exception_stream,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
void* callback_context) {
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
|
||||
NULL);
|
||||
NULL);
|
||||
return handler.WriteMinidump(write_exception_stream);
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
||||
mach_port_t child_blamed_thread,
|
||||
const string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void *callback_context) {
|
||||
mach_port_t child_blamed_thread,
|
||||
const string &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
ScopedTaskSuspend suspend(child);
|
||||
|
||||
MinidumpGenerator generator(child, MACH_PORT_NULL);
|
||||
@ -318,20 +305,22 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
||||
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
|
||||
|
||||
generator.SetExceptionInformation(EXC_BREAKPOINT,
|
||||
#if defined (__i386__) || defined(__x86_64__)
|
||||
EXC_I386_BPT,
|
||||
#elif defined (__ppc__) || defined (__ppc64__)
|
||||
EXC_PPC_BREAKPOINT,
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
EXC_I386_BPT,
|
||||
#elif defined(__ppc__) || defined(__ppc64__)
|
||||
EXC_PPC_BREAKPOINT,
|
||||
#elif defined(__arm__)
|
||||
EXC_ARM_BREAKPOINT,
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
0,
|
||||
child_blamed_thread);
|
||||
0,
|
||||
child_blamed_thread);
|
||||
bool result = generator.Write(dump_filename.c_str());
|
||||
|
||||
if (callback) {
|
||||
return callback(dump_path.c_str(), dump_id.c_str(),
|
||||
callback_context, result);
|
||||
callback_context, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -339,8 +328,10 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
||||
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
ucontext_t* task_context,
|
||||
mach_port_t thread_name,
|
||||
bool exit_after_write) {
|
||||
bool exit_after_write,
|
||||
bool report_current_thread) {
|
||||
bool result = false;
|
||||
|
||||
if (directCallback_) {
|
||||
@ -352,25 +343,30 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
if (exit_after_write)
|
||||
_exit(exception_type);
|
||||
}
|
||||
#if !TARGET_OS_IPHONE
|
||||
} else if (IsOutOfProcess()) {
|
||||
if (exception_type && exception_code) {
|
||||
// If this is a real exception, give the filter (if any) a chance to
|
||||
// decide if this should be sent.
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
return false;
|
||||
return crash_generation_client_->RequestDumpForException(
|
||||
exception_type,
|
||||
exception_code,
|
||||
exception_subcode,
|
||||
thread_name);
|
||||
exception_type,
|
||||
exception_code,
|
||||
exception_subcode,
|
||||
thread_name);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
string minidump_id;
|
||||
|
||||
// Putting the MinidumpGenerator in its own context will ensure that the
|
||||
// destructor is executed, closing the newly created minidump file.
|
||||
if (!dump_path_.empty()) {
|
||||
MinidumpGenerator md;
|
||||
MinidumpGenerator md(mach_task_self(),
|
||||
report_current_thread ? MACH_PORT_NULL :
|
||||
mach_thread_self());
|
||||
md.SetTaskContext(task_context);
|
||||
if (exception_type && exception_code) {
|
||||
// If this is a real exception, give the filter (if any) a chance to
|
||||
// decide if this should be sent.
|
||||
@ -411,13 +407,13 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
|
||||
current.count = EXC_TYPES_COUNT;
|
||||
mach_port_t current_task = mach_task_self();
|
||||
kern_return_t result = task_get_exception_ports(current_task,
|
||||
s_exception_mask,
|
||||
current.masks,
|
||||
¤t.count,
|
||||
current.ports,
|
||||
current.behaviors,
|
||||
current.flavors);
|
||||
task_get_exception_ports(current_task,
|
||||
s_exception_mask,
|
||||
current.masks,
|
||||
¤t.count,
|
||||
current.ports,
|
||||
current.behaviors,
|
||||
current.flavors);
|
||||
|
||||
// Find the first exception handler that matches the exception
|
||||
unsigned int found;
|
||||
@ -435,48 +431,16 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
|
||||
mach_port_t target_port = current.ports[found];
|
||||
exception_behavior_t target_behavior = current.behaviors[found];
|
||||
thread_state_flavor_t target_flavor = current.flavors[found];
|
||||
|
||||
mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
|
||||
breakpad_thread_state_data_t thread_state;
|
||||
kern_return_t result;
|
||||
switch (target_behavior) {
|
||||
case EXCEPTION_DEFAULT:
|
||||
result = exception_raise(target_port, failed_thread, task, exception,
|
||||
code, code_count);
|
||||
break;
|
||||
|
||||
case EXCEPTION_STATE:
|
||||
result = thread_get_state(failed_thread, target_flavor, thread_state,
|
||||
&thread_state_count);
|
||||
if (result == KERN_SUCCESS)
|
||||
result = exception_raise_state(target_port, failed_thread, task,
|
||||
exception, code,
|
||||
code_count, &target_flavor,
|
||||
thread_state, thread_state_count,
|
||||
thread_state, &thread_state_count);
|
||||
if (result == KERN_SUCCESS)
|
||||
result = thread_set_state(failed_thread, target_flavor, thread_state,
|
||||
thread_state_count);
|
||||
break;
|
||||
|
||||
case EXCEPTION_STATE_IDENTITY:
|
||||
result = thread_get_state(failed_thread, target_flavor, thread_state,
|
||||
&thread_state_count);
|
||||
if (result == KERN_SUCCESS)
|
||||
result = exception_raise_state_identity(target_port, failed_thread,
|
||||
task, exception, code,
|
||||
code_count, &target_flavor,
|
||||
thread_state,
|
||||
thread_state_count,
|
||||
thread_state,
|
||||
&thread_state_count);
|
||||
if (result == KERN_SUCCESS)
|
||||
result = thread_set_state(failed_thread, target_flavor, thread_state,
|
||||
thread_state_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "** Unknown exception behavior\n");
|
||||
fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
|
||||
result = KERN_FAILURE;
|
||||
break;
|
||||
}
|
||||
@ -484,22 +448,10 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Callback from exc_server()
|
||||
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t code_count) {
|
||||
if (task != mach_task_self()) {
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
return ForwardException(task, failed_thread, exception, code, code_count);
|
||||
}
|
||||
|
||||
// static
|
||||
void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
ExceptionHandler *self =
|
||||
reinterpret_cast<ExceptionHandler *>(exception_handler_class);
|
||||
void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
||||
ExceptionHandler* self =
|
||||
reinterpret_cast<ExceptionHandler*>(exception_handler_class);
|
||||
ExceptionMessage receive;
|
||||
|
||||
// Wait for the exception info
|
||||
@ -534,7 +486,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
self->SuspendThreads();
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if(gBreakpadAllocator)
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#endif
|
||||
|
||||
@ -544,10 +496,12 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
|
||||
thread = receive.thread.name;
|
||||
exception_type = EXC_BREAKPOINT;
|
||||
#if defined (__i386__) || defined(__x86_64__)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
exception_code = EXC_I386_BPT;
|
||||
#elif defined (__ppc__) || defined (__ppc64__)
|
||||
#elif defined(__ppc__) || defined(__ppc64__)
|
||||
exception_code = EXC_PPC_BREAKPOINT;
|
||||
#elif defined(__arm__)
|
||||
exception_code = EXC_ARM_BREAKPOINT;
|
||||
#else
|
||||
#error architecture not supported
|
||||
#endif
|
||||
@ -556,11 +510,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
// Write out the dump and save the result for later retrieval
|
||||
self->last_minidump_write_result_ =
|
||||
self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
0, thread,
|
||||
false);
|
||||
0, NULL, thread,
|
||||
false, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if(gBreakpadAllocator)
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
|
||||
@ -575,13 +529,13 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
// exceptions that occur in the parent process are caught and
|
||||
// processed. If the exception was not caused by this task, we
|
||||
// still need to call into the exception server and have it return
|
||||
// KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
|
||||
// KERN_FAILURE (see catch_exception_raise) in order for the kernel
|
||||
// to move onto the host exception handler for the child task
|
||||
if (receive.task.name == mach_task_self()) {
|
||||
self->SuspendThreads();
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if(gBreakpadAllocator)
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#endif
|
||||
|
||||
@ -591,26 +545,35 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
|
||||
// Generate the minidump with the exception data.
|
||||
self->WriteMinidumpWithException(receive.exception, receive.code[0],
|
||||
subcode, receive.thread.name, true);
|
||||
subcode, NULL, receive.thread.name,
|
||||
true, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
// This may have become protected again within
|
||||
// WriteMinidumpWithException, but it needs to be unprotected for
|
||||
// UninstallHandler.
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#endif
|
||||
|
||||
self->UninstallHandler(true);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if(gBreakpadAllocator)
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
}
|
||||
// Pass along the exception to the server, which will setup the
|
||||
// message and call breakpad_exception_raise() and put the return
|
||||
// message and call catch_exception_raise() and put the return
|
||||
// code into the reply.
|
||||
ExceptionReplyMessage reply;
|
||||
if (!exc_server(&receive.header, &reply.header))
|
||||
if (!breakpad_exc_server(&receive.header, &reply.header))
|
||||
exit(1);
|
||||
|
||||
// Send a reply and exit
|
||||
result = mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,7 +581,53 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Unprotect();
|
||||
#endif
|
||||
gProtectedData.handler->WriteMinidumpWithException(
|
||||
EXC_SOFTWARE,
|
||||
MD_EXCEPTION_CODE_MAC_ABORT,
|
||||
0,
|
||||
static_cast<ucontext_t*>(uc),
|
||||
mach_thread_self(),
|
||||
true,
|
||||
true);
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if (gBreakpadAllocator)
|
||||
gBreakpadAllocator->Protect();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ExceptionHandler::InstallHandler() {
|
||||
// If a handler is already installed, something is really wrong.
|
||||
if (gProtectedData.handler != NULL) {
|
||||
return false;
|
||||
}
|
||||
#if TARGET_OS_IPHONE
|
||||
if (!IsOutOfProcess()) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaddset(&sa.sa_mask, SIGABRT);
|
||||
sa.sa_sigaction = ExceptionHandler::SignalHandler;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
|
||||
scoped_ptr<struct sigaction> old(new struct sigaction);
|
||||
if (sigaction(SIGABRT, &sa, old.get()) == -1) {
|
||||
return false;
|
||||
}
|
||||
old_handler_.swap(old);
|
||||
gProtectedData.handler = this;
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
|
||||
mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
|
||||
@ -626,7 +635,6 @@ bool ExceptionHandler::InstallHandler() {
|
||||
#else
|
||||
previous_ = new ExceptionParameters();
|
||||
#endif
|
||||
|
||||
}
|
||||
catch (std::bad_alloc) {
|
||||
return false;
|
||||
@ -657,6 +665,16 @@ bool ExceptionHandler::InstallHandler() {
|
||||
bool ExceptionHandler::UninstallHandler(bool in_exception) {
|
||||
kern_return_t result = KERN_SUCCESS;
|
||||
|
||||
if (old_handler_.get()) {
|
||||
sigaction(SIGABRT, old_handler_.get(), NULL);
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
old_handler_.reset();
|
||||
gProtectedData.handler = NULL;
|
||||
}
|
||||
|
||||
if (installed_exception_handler_) {
|
||||
mach_port_t current_task = mach_task_self();
|
||||
|
||||
@ -715,7 +733,7 @@ bool ExceptionHandler::Setup(bool install_handler) {
|
||||
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
|
||||
}
|
||||
|
||||
return result == KERN_SUCCESS ? true : false;
|
||||
return result == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
bool ExceptionHandler::Teardown() {
|
||||
@ -736,7 +754,7 @@ bool ExceptionHandler::Teardown() {
|
||||
}
|
||||
|
||||
handler_thread_ = NULL;
|
||||
handler_port_ = NULL;
|
||||
handler_port_ = MACH_PORT_NULL;
|
||||
pthread_mutex_destroy(&minidump_write_mutex_);
|
||||
|
||||
return result == KERN_SUCCESS;
|
||||
|
@ -37,12 +37,16 @@
|
||||
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
@ -152,7 +156,11 @@ class ExceptionHandler {
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const {
|
||||
#if TARGET_OS_IPHONE
|
||||
return false;
|
||||
#else
|
||||
return crash_generation_client_.get() != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
@ -174,17 +182,24 @@ class ExceptionHandler {
|
||||
// success, false otherwise.
|
||||
bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
|
||||
|
||||
// All minidump writing goes through this one routine
|
||||
// All minidump writing goes through this one routine.
|
||||
// |task_context| can be NULL. If not, it will be used to retrieve the
|
||||
// context of the current thread, instead of using |thread_get_state|.
|
||||
bool WriteMinidumpWithException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
ucontext_t *task_context,
|
||||
mach_port_t thread_name,
|
||||
bool exit_after_write);
|
||||
bool exit_after_write,
|
||||
bool report_current_thread);
|
||||
|
||||
// When installed, this static function will be call from a newly created
|
||||
// pthread with |this| as the argument
|
||||
static void *WaitForMessage(void *exception_handler_class);
|
||||
|
||||
// Signal handler for SIGABRT.
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit ExceptionHandler(const ExceptionHandler &);
|
||||
void operator=(const ExceptionHandler &);
|
||||
@ -250,8 +265,14 @@ class ExceptionHandler {
|
||||
// True, if we're using the mutext to indicate when mindump writing occurs
|
||||
bool use_minidump_write_mutex_;
|
||||
|
||||
// Old signal handler for SIGABRT. Used to be able to restore it when
|
||||
// uninstalling.
|
||||
scoped_ptr<struct sigaction> old_handler_;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
// Client for out-of-process dump generation.
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
49
3rdparty/google-breakpad/client/mac/handler/mach_vm_compat.h
vendored
Normal file
49
3rdparty/google-breakpad/client/mac/handler/mach_vm_compat.h
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2011, 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_MAC_GENERATOR_MACH_VM_COMPAT_H_
|
||||
#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
|
||||
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
// On iOS 5, mach/mach_vm.h is not supported anymore. As the architecture is 32
|
||||
// bits, we can use the simple vm_ functions instead of the mach_vm_ ones.
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <mach/vm_map.h>
|
||||
#define mach_vm_address_t vm_address_t
|
||||
#define mach_vm_deallocate vm_deallocate
|
||||
#define mach_vm_read vm_read
|
||||
#define mach_vm_region vm_region
|
||||
#define mach_vm_region_recurse vm_region_recurse
|
||||
#define mach_vm_size_t vm_size_t
|
||||
#else
|
||||
#include <mach/mach_vm.h>
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
|
@ -31,9 +31,7 @@
|
||||
#include <cstdio>
|
||||
|
||||
#include <mach/host_info.h>
|
||||
#include <mach/i386/thread_status.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/ppc/thread_status.h>
|
||||
#include <mach/machine.h>
|
||||
#include <mach/vm_statistics.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/loader.h>
|
||||
@ -43,8 +41,20 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
#include <mach/arm/thread_status.h>
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
#include <mach/ppc/thread_status.h>
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
#include <mach/i386/thread_status.h>
|
||||
#endif
|
||||
|
||||
#include "client/minidump_file_writer-inl.h"
|
||||
#include "common/mac/file_id.h"
|
||||
#include "common/mac/macho_id.h"
|
||||
#include "common/mac/string_utilities.h"
|
||||
|
||||
using MacStringUtils::ConvertToString;
|
||||
@ -68,6 +78,7 @@ MinidumpGenerator::MinidumpGenerator()
|
||||
crashing_task_(mach_task_self()),
|
||||
handler_thread_(mach_thread_self()),
|
||||
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||
task_context_(NULL),
|
||||
dynamic_images_(NULL),
|
||||
memory_blocks_(&allocator_) {
|
||||
GatherSystemInformation();
|
||||
@ -85,6 +96,7 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
|
||||
crashing_task_(crashing_task),
|
||||
handler_thread_(handler_thread),
|
||||
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||
task_context_(NULL),
|
||||
dynamic_images_(NULL),
|
||||
memory_blocks_(&allocator_) {
|
||||
if (crashing_task != mach_task_self()) {
|
||||
@ -126,14 +138,19 @@ void MinidumpGenerator::GatherSystemInformation() {
|
||||
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
||||
&error);
|
||||
|
||||
if (!data)
|
||||
if (!data) {
|
||||
CFRelease(sys_vers);
|
||||
return;
|
||||
}
|
||||
|
||||
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
||||
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
||||
NULL));
|
||||
if (!list)
|
||||
if (!list) {
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
CFStringRef build_version = static_cast<CFStringRef>
|
||||
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
||||
@ -154,6 +171,10 @@ void MinidumpGenerator::GatherSystemInformation() {
|
||||
os_build_number_ = IntegerValueAtIndex(product_str, 2);
|
||||
}
|
||||
|
||||
void MinidumpGenerator::SetTaskContext(ucontext_t *task_context) {
|
||||
task_context_ = task_context;
|
||||
}
|
||||
|
||||
string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
|
||||
string *unique_name) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
||||
@ -251,37 +272,45 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
|
||||
kern_return_t result =
|
||||
mach_vm_region_recurse(crashing_task_, &stack_region_base,
|
||||
&stack_region_size, &nesting_level,
|
||||
region_info,
|
||||
&info_count);
|
||||
region_info, &info_count);
|
||||
|
||||
if (start_addr < stack_region_base) {
|
||||
// probably stack corruption, since mach_vm_region had to go
|
||||
if (result != KERN_SUCCESS || start_addr < stack_region_base) {
|
||||
// Failure or stack corruption, since mach_vm_region had to go
|
||||
// higher in the process address space to find a valid region.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((cpu_type_ & CPU_ARCH_ABI64) &&
|
||||
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_64BIT) ||
|
||||
(!(cpu_type_ & CPU_ARCH_ABI64) &&
|
||||
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_32BIT)) {
|
||||
// The stack for thread 0 needs to extend all the way to
|
||||
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
|
||||
// for many processes, the stack is first created in one page
|
||||
// below this, and is then later extended to a much larger size by
|
||||
// creating a new VM region immediately below the initial page.
|
||||
unsigned int tag = submap_info.user_tag;
|
||||
|
||||
// You can see this for yourself by running vmmap on a "hello,
|
||||
// world" program
|
||||
// If the user tag is VM_MEMORY_STACK, look for more readable regions with
|
||||
// the same tag placed immediately above the computed stack region. Under
|
||||
// some circumstances, the stack for thread 0 winds up broken up into
|
||||
// multiple distinct abutting regions. This can happen for several reasons,
|
||||
// including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
|
||||
// the access on stack pages by calling mprotect.
|
||||
if (tag == VM_MEMORY_STACK) {
|
||||
while (true) {
|
||||
mach_vm_address_t next_region_base = stack_region_base +
|
||||
stack_region_size;
|
||||
mach_vm_address_t proposed_next_region_base = next_region_base;
|
||||
mach_vm_size_t next_region_size;
|
||||
nesting_level = 0;
|
||||
info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||
result = mach_vm_region_recurse(crashing_task_, &next_region_base,
|
||||
&next_region_size, &nesting_level,
|
||||
region_info, &info_count);
|
||||
if (result != KERN_SUCCESS ||
|
||||
next_region_base != proposed_next_region_base ||
|
||||
submap_info.user_tag != tag ||
|
||||
(submap_info.protection & VM_PROT_READ) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Because of the above, we'll add 4k to include the original
|
||||
// stack frame page.
|
||||
// This method of finding the stack region needs to be done in
|
||||
// a better way; the breakpad issue 247 is tracking this.
|
||||
stack_region_size += 0x1000;
|
||||
stack_region_size += next_region_size;
|
||||
}
|
||||
}
|
||||
|
||||
return result == KERN_SUCCESS ?
|
||||
stack_region_base + stack_region_size - start_addr : 0;
|
||||
return stack_region_base + stack_region_size - start_addr;
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||
@ -336,14 +365,22 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM:
|
||||
return WriteStackARM(state, stack_location);
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
case CPU_TYPE_POWERPC:
|
||||
return WriteStackPPC(state, stack_location);
|
||||
case CPU_TYPE_POWERPC64:
|
||||
return WriteStackPPC64(state, stack_location);
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
case CPU_TYPE_I386:
|
||||
return WriteStackX86(state, stack_location);
|
||||
case CPU_TYPE_X86_64:
|
||||
return WriteStackX86_64(state, stack_location);
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -352,14 +389,22 @@ bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location) {
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM:
|
||||
return WriteContextARM(state, register_location);
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
case CPU_TYPE_POWERPC:
|
||||
return WriteContextPPC(state, register_location);
|
||||
case CPU_TYPE_POWERPC64:
|
||||
return WriteContextPPC64(state, register_location);
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
case CPU_TYPE_I386:
|
||||
return WriteContextX86(state, register_location);
|
||||
case CPU_TYPE_X86_64:
|
||||
return WriteContextX86_64(state, register_location);
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -368,20 +413,87 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||
u_int64_t MinidumpGenerator::CurrentPCForStack(
|
||||
breakpad_thread_state_data_t state) {
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM:
|
||||
return CurrentPCForStackARM(state);
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
case CPU_TYPE_POWERPC:
|
||||
return CurrentPCForStackPPC(state);
|
||||
case CPU_TYPE_POWERPC64:
|
||||
return CurrentPCForStackPPC64(state);
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
case CPU_TYPE_I386:
|
||||
return CurrentPCForStackX86(state);
|
||||
case CPU_TYPE_X86_64:
|
||||
return CurrentPCForStackX86_64(state);
|
||||
#endif
|
||||
default:
|
||||
assert("Unknown CPU type!");
|
||||
assert(0 && "Unknown CPU type!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
arm_thread_state_t *machine_state =
|
||||
reinterpret_cast<arm_thread_state_t *>(state);
|
||||
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
|
||||
return WriteStackFromStartAddress(start_addr, stack_location);
|
||||
}
|
||||
|
||||
u_int64_t
|
||||
MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
|
||||
arm_thread_state_t *machine_state =
|
||||
reinterpret_cast<arm_thread_state_t *>(state);
|
||||
|
||||
return REGISTER_FROM_THREADSTATE(machine_state, pc);
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location)
|
||||
{
|
||||
TypedMDRVA<MDRawContextARM> context(&writer_);
|
||||
arm_thread_state_t *machine_state =
|
||||
reinterpret_cast<arm_thread_state_t *>(state);
|
||||
|
||||
if (!context.Allocate())
|
||||
return false;
|
||||
|
||||
*register_location = context.location();
|
||||
MDRawContextARM *context_ptr = context.get();
|
||||
context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a])
|
||||
|
||||
context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp);
|
||||
context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr);
|
||||
context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc);
|
||||
context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
|
||||
|
||||
AddGPR(0);
|
||||
AddGPR(1);
|
||||
AddGPR(2);
|
||||
AddGPR(3);
|
||||
AddGPR(4);
|
||||
AddGPR(5);
|
||||
AddGPR(6);
|
||||
AddGPR(7);
|
||||
AddGPR(8);
|
||||
AddGPR(9);
|
||||
AddGPR(10);
|
||||
AddGPR(11);
|
||||
AddGPR(12);
|
||||
#undef AddReg
|
||||
#undef AddGPR
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PCC_SUPPORT
|
||||
bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
ppc_thread_state_t *machine_state =
|
||||
@ -539,6 +651,9 @@ bool MinidumpGenerator::WriteContextPPC64(
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location) {
|
||||
i386_thread_state_t *machine_state =
|
||||
@ -607,7 +722,7 @@ bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
|
||||
AddReg(eflags);
|
||||
|
||||
AddReg(eip);
|
||||
#undef AddReg(a)
|
||||
#undef AddReg
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -653,28 +768,51 @@ bool MinidumpGenerator::WriteContextX86_64(
|
||||
AddReg(cs);
|
||||
AddReg(fs);
|
||||
AddReg(gs);
|
||||
#undef AddReg(a)
|
||||
#undef AddReg
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
|
||||
thread_state_t state,
|
||||
mach_msg_type_number_t *count) {
|
||||
if (task_context_ && target_thread == mach_thread_self()) {
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM: {
|
||||
size_t final_size =
|
||||
std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t));
|
||||
memcpy(state, &task_context_->uc_mcontext->__ss, final_size);
|
||||
*count = final_size;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
thread_state_flavor_t flavor;
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM:
|
||||
flavor = ARM_THREAD_STATE;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
case CPU_TYPE_POWERPC:
|
||||
flavor = PPC_THREAD_STATE;
|
||||
break;
|
||||
case CPU_TYPE_POWERPC64:
|
||||
flavor = PPC_THREAD_STATE64;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
case CPU_TYPE_I386:
|
||||
flavor = i386_THREAD_STATE;
|
||||
break;
|
||||
case CPU_TYPE_X86_64:
|
||||
flavor = x86_THREAD_STATE64;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -760,26 +898,25 @@ bool MinidumpGenerator::WriteMemoryListStream(
|
||||
mach_msg_type_number_t stateCount
|
||||
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
||||
|
||||
if (thread_get_state(exception_thread_,
|
||||
BREAKPAD_MACHINE_THREAD_STATE,
|
||||
state,
|
||||
&stateCount) == KERN_SUCCESS) {
|
||||
if (GetThreadState(exception_thread_, state, &stateCount)) {
|
||||
u_int64_t ip = CurrentPCForStack(state);
|
||||
// Bound it to the upper and lower bounds of the region
|
||||
// it's contained within. If it's not in a known memory region,
|
||||
// don't bother trying to write it.
|
||||
mach_vm_address_t addr = ip;
|
||||
mach_vm_address_t addr = static_cast<vm_address_t>(ip);
|
||||
mach_vm_size_t size;
|
||||
natural_t nesting_level = 0;
|
||||
vm_region_submap_info_64 info;
|
||||
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
||||
vm_region_recurse_info_t recurse_info;
|
||||
recurse_info = reinterpret_cast<vm_region_recurse_info_t>(&info);
|
||||
|
||||
kern_return_t ret =
|
||||
mach_vm_region_recurse(crashing_task_,
|
||||
&addr,
|
||||
&size,
|
||||
&nesting_level,
|
||||
(vm_region_recurse_info_t)&info,
|
||||
recurse_info,
|
||||
&info_count);
|
||||
if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
|
||||
// Try to get 128 bytes before and after the IP, but
|
||||
@ -791,7 +928,8 @@ bool MinidumpGenerator::WriteMemoryListStream(
|
||||
std::min(uintptr_t(ip + (kIPMemorySize / 2)),
|
||||
uintptr_t(addr + size));
|
||||
ip_memory_d.memory.data_size =
|
||||
end_of_range - ip_memory_d.start_of_memory_range;
|
||||
end_of_range -
|
||||
static_cast<uintptr_t>(ip_memory_d.start_of_memory_range);
|
||||
have_ip_memory = true;
|
||||
// This needs to get appended to the list even though
|
||||
// the memory bytes aren't filled in yet so the entire
|
||||
@ -904,10 +1042,18 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
||||
MDRawSystemInfo *info_ptr = info.get();
|
||||
|
||||
switch (cpu_type_) {
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
case CPU_TYPE_ARM:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
case CPU_TYPE_POWERPC:
|
||||
case CPU_TYPE_POWERPC64:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
case CPU_TYPE_I386:
|
||||
case CPU_TYPE_X86_64:
|
||||
if (cpu_type_ == CPU_TYPE_I386)
|
||||
@ -971,13 +1117,18 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
||||
|
||||
#endif // __i386__ || __x86_64_
|
||||
break;
|
||||
#endif // HAS_X86_SUPPORT
|
||||
default:
|
||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
info_ptr->number_of_processors = number_of_processors;
|
||||
info_ptr->number_of_processors = static_cast<uint8_t>(number_of_processors);
|
||||
#if TARGET_OS_IPHONE
|
||||
info_ptr->platform_id = MD_OS_IOS;
|
||||
#else
|
||||
info_ptr->platform_id = MD_OS_MAC_OS_X;
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
MDLocationDescriptor build_string_loc;
|
||||
|
||||
@ -1017,7 +1168,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||
// We'll skip the executable module, because they don't have
|
||||
// LC_ID_DYLIB load commands, and the crash processing server gets
|
||||
// version information from the Plist file, anyway.
|
||||
if (index != (uint32_t)FindExecutableModule()) {
|
||||
if (index != static_cast<uint32_t>(FindExecutableModule())) {
|
||||
module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
|
||||
module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
|
||||
// Convert MAC dylib version format, which is a 32 bit number, to the
|
||||
@ -1034,7 +1185,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||
module->version_info.file_version_lo |= (modVersion & 0xff);
|
||||
}
|
||||
|
||||
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
|
||||
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -1080,7 +1231,11 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
||||
module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
|
||||
module->module_name_rva = string_location.rva;
|
||||
|
||||
if (!WriteCVRecord(module, cpu_type, name))
|
||||
bool in_memory = false;
|
||||
#if TARGET_OS_IPHONE
|
||||
in_memory = true;
|
||||
#endif
|
||||
if (!WriteCVRecord(module, cpu_type, name, in_memory))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -1118,7 +1273,7 @@ int MinidumpGenerator::FindExecutableModule() {
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||
const char *module_path) {
|
||||
const char *module_path, bool in_memory) {
|
||||
TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
|
||||
|
||||
// Only return the last path component of the full module path
|
||||
@ -1144,15 +1299,33 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||
cv_ptr->age = 0;
|
||||
|
||||
// Get the module identifier
|
||||
FileID file_id(module_path);
|
||||
unsigned char identifier[16];
|
||||
bool result = false;
|
||||
if (in_memory) {
|
||||
MacFileUtilities::MachoID macho(module_path,
|
||||
reinterpret_cast<void *>(module->base_of_image),
|
||||
static_cast<size_t>(module->size_of_image));
|
||||
result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
|
||||
if (!result)
|
||||
result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
|
||||
}
|
||||
|
||||
if (file_id.MachoIdentifier(cpu_type, identifier)) {
|
||||
cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
|
||||
(uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
|
||||
(uint32_t)identifier[3];
|
||||
cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
|
||||
cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
|
||||
if (!result) {
|
||||
FileID file_id(module_path);
|
||||
result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE,
|
||||
identifier);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
cv_ptr->signature.data1 =
|
||||
static_cast<uint32_t>(identifier[0]) << 24 |
|
||||
static_cast<uint32_t>(identifier[1]) << 16 |
|
||||
static_cast<uint32_t>(identifier[2]) << 8 |
|
||||
static_cast<uint32_t>(identifier[3]);
|
||||
cv_ptr->signature.data2 =
|
||||
static_cast<uint16_t>(identifier[4] << 8) | identifier[5];
|
||||
cv_ptr->signature.data3 =
|
||||
static_cast<uint16_t>(identifier[6] << 8) | identifier[7];
|
||||
cv_ptr->signature.data4[0] = identifier[8];
|
||||
cv_ptr->signature.data4[1] = identifier[9];
|
||||
cv_ptr->signature.data4[2] = identifier[10];
|
||||
@ -1170,8 +1343,9 @@ bool MinidumpGenerator::WriteModuleListStream(
|
||||
MDRawDirectory *module_list_stream) {
|
||||
TypedMDRVA<MDRawModuleList> list(&writer_);
|
||||
|
||||
int image_count = dynamic_images_ ?
|
||||
dynamic_images_->GetImageCount() : _dyld_image_count();
|
||||
size_t image_count = dynamic_images_ ?
|
||||
static_cast<size_t>(dynamic_images_->GetImageCount()) :
|
||||
_dyld_image_count();
|
||||
|
||||
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
|
||||
return false;
|
||||
@ -1182,7 +1356,7 @@ bool MinidumpGenerator::WriteModuleListStream(
|
||||
|
||||
// Write out the executable module as the first one
|
||||
MDRawModule module;
|
||||
int executableIndex = FindExecutableModule();
|
||||
size_t executableIndex = FindExecutableModule();
|
||||
|
||||
if (!WriteModuleStream(executableIndex, &module)) {
|
||||
return false;
|
||||
@ -1191,7 +1365,7 @@ bool MinidumpGenerator::WriteModuleListStream(
|
||||
list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
|
||||
int destinationIndex = 1; // Write all other modules after this one
|
||||
|
||||
for (int i = 0; i < image_count; ++i) {
|
||||
for (size_t i = 0; i < image_count; ++i) {
|
||||
if (i != executableIndex) {
|
||||
if (!WriteModuleStream(i, &module)) {
|
||||
return false;
|
||||
@ -1231,21 +1405,14 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
|
||||
info_ptr->process_kernel_time =
|
||||
static_cast<u_int32_t>(usage.ru_stime.tv_sec);
|
||||
}
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
|
||||
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
|
||||
static_cast<int>(info_ptr->process_id) };
|
||||
u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
|
||||
size_t size;
|
||||
if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) {
|
||||
mach_vm_address_t addr;
|
||||
if (mach_vm_allocate(mach_task_self(),
|
||||
&addr,
|
||||
size,
|
||||
true) == KERN_SUCCESS) {
|
||||
struct kinfo_proc *proc = (struct kinfo_proc *)addr;
|
||||
if (!sysctl(mib, mibsize, proc, &size, NULL, 0))
|
||||
info_ptr->process_create_time =
|
||||
static_cast<u_int32_t>(proc->kp_proc.p_starttime.tv_sec);
|
||||
mach_vm_deallocate(mach_task_self(), addr, size);
|
||||
}
|
||||
struct kinfo_proc proc;
|
||||
size_t size = sizeof(proc);
|
||||
if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) {
|
||||
info_ptr->process_create_time =
|
||||
static_cast<u_int32_t>(proc.kp_proc.p_starttime.tv_sec);
|
||||
}
|
||||
|
||||
// Speed
|
||||
|
@ -33,6 +33,7 @@
|
||||
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -42,17 +43,24 @@
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
#include "dynamic_images.h"
|
||||
#include "mach_vm_compat.h"
|
||||
|
||||
#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
|
||||
#define HAS_PPC_SUPPORT
|
||||
#endif
|
||||
#if defined(__arm__)
|
||||
#define HAS_ARM_SUPPORT
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
#define HAS_X86_SUPPORT
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL;
|
||||
const u_int32_t TOP_OF_THREAD0_STACK_32BIT = 0xbffff000;
|
||||
|
||||
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
|
||||
// breakpad_thread_state_t structure.
|
||||
#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64
|
||||
#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM
|
||||
// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
|
||||
// i386_thread_state_t structure. There's no good way to tell what version of
|
||||
// the SDK we're compiling against so we just toggle on the same preprocessor
|
||||
@ -74,7 +82,7 @@ class MinidumpGenerator {
|
||||
MinidumpGenerator();
|
||||
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
|
||||
|
||||
~MinidumpGenerator();
|
||||
virtual ~MinidumpGenerator();
|
||||
|
||||
// Return <dir>/<unique_name>.dmp
|
||||
// Sets |unique_name| (if requested) to the unique name for the minidump
|
||||
@ -94,17 +102,28 @@ class MinidumpGenerator {
|
||||
exception_thread_ = thread_name;
|
||||
}
|
||||
|
||||
// Specify the task context. If |task_context| is not NULL, it will be used
|
||||
// to retrieve the context of the current thread, instead of using
|
||||
// |thread_get_state|.
|
||||
void SetTaskContext(ucontext_t *task_context);
|
||||
|
||||
// Gather system information. This should be call at least once before using
|
||||
// the MinidumpGenerator class.
|
||||
static void GatherSystemInformation();
|
||||
|
||||
protected:
|
||||
// Overridable Stream writers
|
||||
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
|
||||
|
||||
// Overridable Helper
|
||||
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
|
||||
|
||||
private:
|
||||
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
|
||||
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
|
||||
|
||||
// Stream writers
|
||||
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
|
||||
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
|
||||
bool WriteExceptionStream(MDRawDirectory *exception_stream);
|
||||
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
|
||||
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
|
||||
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
|
||||
@ -120,14 +139,21 @@ class MinidumpGenerator {
|
||||
MDMemoryDescriptor *stack_location);
|
||||
bool WriteContext(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location);
|
||||
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
|
||||
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||
const char *module_path);
|
||||
const char *module_path, bool in_memory);
|
||||
bool WriteModuleStream(unsigned int index, MDRawModule *module);
|
||||
size_t CalculateStackSize(mach_vm_address_t start_addr);
|
||||
int FindExecutableModule();
|
||||
|
||||
// Per-CPU implementations of these methods
|
||||
#ifdef HAS_ARM_SUPPORT
|
||||
bool WriteStackARM(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location);
|
||||
bool WriteContextARM(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location);
|
||||
u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
|
||||
#endif
|
||||
#ifdef HAS_PPC_SUPPORT
|
||||
bool WriteStackPPC(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location);
|
||||
bool WriteContextPPC(breakpad_thread_state_data_t state,
|
||||
@ -138,6 +164,8 @@ class MinidumpGenerator {
|
||||
bool WriteContextPPC64(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location);
|
||||
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
|
||||
#endif
|
||||
#ifdef HAS_X86_SUPPORT
|
||||
bool WriteStackX86(breakpad_thread_state_data_t state,
|
||||
MDMemoryDescriptor *stack_location);
|
||||
bool WriteContextX86(breakpad_thread_state_data_t state,
|
||||
@ -148,14 +176,17 @@ class MinidumpGenerator {
|
||||
bool WriteContextX86_64(breakpad_thread_state_data_t state,
|
||||
MDLocationDescriptor *register_location);
|
||||
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
|
||||
#endif
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit MinidumpGenerator(const MinidumpGenerator &);
|
||||
void operator=(const MinidumpGenerator &);
|
||||
|
||||
protected:
|
||||
// Use this writer to put the data to disk
|
||||
MinidumpFileWriter writer_;
|
||||
|
||||
private:
|
||||
// Exception information
|
||||
int exception_type_;
|
||||
int exception_code_;
|
||||
@ -173,6 +204,9 @@ class MinidumpGenerator {
|
||||
static int os_minor_version_;
|
||||
static int os_build_number_;
|
||||
|
||||
// Context of the task to dump.
|
||||
ucontext_t *task_context_;
|
||||
|
||||
// Information about dynamically loaded code
|
||||
DynamicImages *dynamic_images_;
|
||||
|
||||
@ -180,6 +214,7 @@ class MinidumpGenerator {
|
||||
// directly from the system, even while handling an exception.
|
||||
mutable PageAllocator allocator_;
|
||||
|
||||
protected:
|
||||
// Blocks of memory written to the dump. These are all currently
|
||||
// written while writing the thread list stream, but saved here
|
||||
// so a memory list stream can be written afterwards.
|
||||
|
@ -72,7 +72,7 @@
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
|
||||
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.c */; };
|
||||
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; };
|
||||
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
|
||||
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
|
||||
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; };
|
||||
@ -157,7 +157,7 @@
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; };
|
||||
F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; };
|
||||
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
|
||||
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
|
||||
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
|
||||
@ -236,7 +236,7 @@
|
||||
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
|
||||
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
|
||||
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
|
||||
F9721FA80E8B0E4800D7E813 /* md5.c */,
|
||||
F9721FA80E8B0E4800D7E813 /* md5.cc */,
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
|
||||
@ -451,7 +451,14 @@
|
||||
isa = PBXProject;
|
||||
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 1;
|
||||
knownRegions = (
|
||||
English,
|
||||
Japanese,
|
||||
French,
|
||||
German,
|
||||
);
|
||||
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@ -594,7 +601,7 @@
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
|
||||
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */,
|
||||
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */,
|
||||
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
|
||||
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
|
||||
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */,
|
||||
|
@ -55,21 +55,17 @@ void DynamicImagesTests::ReadTaskMemoryTest() {
|
||||
// pick test2 as a symbol we know to be valid to read
|
||||
// anything will work, really
|
||||
void *addr = reinterpret_cast<void*>(&test2);
|
||||
void *buf;
|
||||
std::vector<uint8_t> buf(getpagesize());
|
||||
|
||||
fprintf(stderr, "reading 0x%p\n", addr);
|
||||
buf = google_breakpad::ReadTaskMemory(mach_task_self(),
|
||||
addr,
|
||||
getpagesize(),
|
||||
&kr);
|
||||
kr = google_breakpad::ReadTaskMemory(mach_task_self(),
|
||||
(uint64_t)addr,
|
||||
getpagesize(),
|
||||
buf);
|
||||
|
||||
CPTAssert(kr == KERN_SUCCESS);
|
||||
|
||||
CPTAssert(buf != NULL);
|
||||
|
||||
CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize()));
|
||||
|
||||
free(buf);
|
||||
CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize()));
|
||||
}
|
||||
|
||||
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
|
||||
@ -79,7 +75,5 @@ void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
|
||||
|
||||
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
|
||||
|
||||
d->TestPrint();
|
||||
|
||||
CPTAssert(d->GetImageCount() > 0);
|
||||
}
|
||||
|
@ -32,17 +32,11 @@
|
||||
// It will perform throttling based on the parameters passed to it and will
|
||||
// prompt the user to send the minidump.
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "client/mac/Framework/Breakpad.h"
|
||||
#include "client/mac/sender/uploader.h"
|
||||
#import "GTMDefines.h"
|
||||
|
||||
#define kClientIdPreferenceKey @"clientid"
|
||||
|
||||
extern NSString *const kGoogleServerType;
|
||||
extern NSString *const kSocorroServerType;
|
||||
extern NSString *const kDefaultServerType;
|
||||
|
||||
// We're sublcassing NSTextField in order to override a particular
|
||||
// method (see the implementation) that lets us reject changes if they
|
||||
// are longer than a particular length. Bindings would normally solve
|
||||
@ -87,29 +81,12 @@ extern NSString *const kDefaultServerType;
|
||||
NSString *countdownMessage_; // Message indicating time
|
||||
// left for input.
|
||||
@private
|
||||
int configFile_; // File descriptor for config file
|
||||
NSMutableDictionary *parameters_; // Key value pairs of data (STRONG)
|
||||
NSData *minidumpContents_; // The data in the minidump (STRONG)
|
||||
NSData *logFileData_; // An NSdata for the tar,
|
||||
// bz2'd log file.
|
||||
NSTimeInterval remainingDialogTime_; // Keeps track of how long
|
||||
// we have until we cancel
|
||||
// the dialog
|
||||
NSTimer *messageTimer_; // Timer we use to update
|
||||
// the dialog
|
||||
NSMutableDictionary *serverDictionary_; // The dictionary mapping a
|
||||
// server type name to a
|
||||
// dictionary of server
|
||||
// parameter names.
|
||||
NSMutableDictionary *socorroDictionary_; // The dictionary for
|
||||
// Socorro.
|
||||
NSMutableDictionary *googleDictionary_; // The dictionary for
|
||||
// Google.
|
||||
NSMutableDictionary *extraServerVars_; // A dictionary containing
|
||||
// extra key/value pairs
|
||||
// that are uploaded to the
|
||||
// crash server with the
|
||||
// minidump.
|
||||
Uploader* uploader_; // Uploader we use to send the data.
|
||||
}
|
||||
|
||||
// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
|
||||
|
@ -27,31 +27,26 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import <pwd.h>
|
||||
#import <sys/stat.h>
|
||||
#import <unistd.h>
|
||||
#import "client/mac/sender/crash_report_sender.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <pwd.h>
|
||||
#import <sys/stat.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <unistd.h>
|
||||
|
||||
#import "common/mac/HTTPMultipartUpload.h"
|
||||
|
||||
#import "crash_report_sender.h"
|
||||
#import "client/apple/Framework/BreakpadDefines.h"
|
||||
#import "common/mac/GTMLogger.h"
|
||||
#import "common/mac/HTTPMultipartUpload.h"
|
||||
|
||||
|
||||
#define kLastSubmission @"LastSubmission"
|
||||
const int kMinidumpFileLengthLimit = 800000;
|
||||
const int kUserCommentsMaxLength = 1500;
|
||||
const int kEmailMaxLength = 64;
|
||||
|
||||
#define kApplePrefsSyncExcludeAllKey \
|
||||
@"com.apple.PreferenceSync.ExcludeAllSyncKeys"
|
||||
|
||||
NSString *const kGoogleServerType = @"google";
|
||||
NSString *const kSocorroServerType = @"socorro";
|
||||
NSString *const kDefaultServerType = @"google";
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface NSView (ResizabilityExtentions)
|
||||
@ -160,18 +155,8 @@ NSString *const kDefaultServerType = @"google";
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
@interface Reporter(PrivateMethods)
|
||||
+ (uid_t)consoleUID;
|
||||
|
||||
- (id)initWithConfigurationFD:(int)fd;
|
||||
|
||||
- (NSString *)readString;
|
||||
- (NSData *)readData:(ssize_t)length;
|
||||
|
||||
- (BOOL)readConfigurationData;
|
||||
- (BOOL)readMinidumpData;
|
||||
- (BOOL)readLogFileData;
|
||||
- (id)initWithConfigFile:(const char *)configFile;
|
||||
|
||||
// Returns YES if it has been long enough since the last report that we should
|
||||
// submit a report for this crash.
|
||||
@ -221,30 +206,6 @@ NSString *const kDefaultServerType = @"google";
|
||||
- (NSInteger)runModalWindow:(NSWindow*)window
|
||||
withTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
// Returns a unique client id (user-specific), creating a persistent
|
||||
// one in the user defaults, if necessary.
|
||||
- (NSString*)clientID;
|
||||
|
||||
// Returns a dictionary that can be used to map Breakpad parameter names to
|
||||
// URL parameter names.
|
||||
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType;
|
||||
|
||||
// Helper method to set HTTP parameters based on server type. This is
|
||||
// called right before the upload - crashParameters will contain, on exit,
|
||||
// URL parameters that should be sent with the minidump.
|
||||
- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters;
|
||||
|
||||
// Initialization helper to create dictionaries mapping Breakpad
|
||||
// parameters to URL parameters
|
||||
- (void)createServerParameterDictionaries;
|
||||
|
||||
// Accessor method for the URL parameter dictionary
|
||||
- (NSMutableDictionary *)urlParameterDictionary;
|
||||
|
||||
// This method adds a key/value pair to the dictionary that
|
||||
// will be uploaded to the crash server.
|
||||
- (void)addServerParameter:(id)value forKey:(NSString *)key;
|
||||
|
||||
// This method is used to periodically update the UI with how many
|
||||
// seconds are left in the dialog display.
|
||||
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
|
||||
@ -255,291 +216,24 @@ NSString *const kDefaultServerType = @"google";
|
||||
// in their comments/email.
|
||||
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
|
||||
|
||||
// Records the uploaded crash ID to the log file.
|
||||
- (void)logUploadWithID:(const char *)uploadID;
|
||||
- (void)report;
|
||||
|
||||
@end
|
||||
|
||||
@implementation Reporter
|
||||
//=============================================================================
|
||||
+ (uid_t)consoleUID {
|
||||
SCDynamicStoreRef store =
|
||||
SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL);
|
||||
uid_t uid = -2; // Default to "nobody"
|
||||
if (store) {
|
||||
CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL);
|
||||
|
||||
if (user)
|
||||
CFRelease(user);
|
||||
else
|
||||
uid = -2;
|
||||
|
||||
CFRelease(store);
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (id)initWithConfigurationFD:(int)fd {
|
||||
- (id)initWithConfigFile:(const char *)configFile {
|
||||
if ((self = [super init])) {
|
||||
configFile_ = fd;
|
||||
remainingDialogTime_ = 0;
|
||||
uploader_ = [[Uploader alloc] initWithConfigFile:configFile];
|
||||
if (!uploader_) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Because the reporter is embedded in the framework (and many copies
|
||||
// of the framework may exist) its not completely certain that the OS
|
||||
// will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our
|
||||
// Info.plist. To make sure, also set the key directly if needed.
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) {
|
||||
[ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey];
|
||||
}
|
||||
|
||||
[self createServerParameterDictionaries];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSString *)readString {
|
||||
NSMutableString *str = [NSMutableString stringWithCapacity:32];
|
||||
char ch[2] = { 0 };
|
||||
|
||||
while (read(configFile_, &ch[0], 1) == 1) {
|
||||
if (ch[0] == '\n') {
|
||||
// Break if this is the first newline after reading some other string
|
||||
// data.
|
||||
if ([str length])
|
||||
break;
|
||||
} else {
|
||||
[str appendString:[NSString stringWithUTF8String:ch]];
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (NSData *)readData:(ssize_t)length {
|
||||
NSMutableData *data = [NSMutableData dataWithLength:length];
|
||||
char *bytes = (char *)[data bytes];
|
||||
|
||||
if (read(configFile_, bytes, length) != length)
|
||||
return nil;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)readConfigurationData {
|
||||
parameters_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
while (1) {
|
||||
NSString *key = [self readString];
|
||||
|
||||
if (![key length])
|
||||
break;
|
||||
|
||||
// Read the data. Try to convert to a UTF-8 string, or just save
|
||||
// the data
|
||||
NSString *lenStr = [self readString];
|
||||
ssize_t len = [lenStr intValue];
|
||||
NSData *data = [self readData:len];
|
||||
id value = [[NSString alloc] initWithData:data
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
// If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX
|
||||
// that indicates that it should be uploaded to the server along
|
||||
// with the minidump, so we treat it specially.
|
||||
if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) {
|
||||
NSString *urlParameterKey =
|
||||
[key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]];
|
||||
if ([urlParameterKey length]) {
|
||||
if (value) {
|
||||
[self addServerParameter:value
|
||||
forKey:urlParameterKey];
|
||||
} else {
|
||||
[self addServerParameter:data
|
||||
forKey:urlParameterKey];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[parameters_ setObject:(value ? value : data) forKey:key];
|
||||
}
|
||||
[value release];
|
||||
}
|
||||
|
||||
// generate a unique client ID based on this host's MAC address
|
||||
// then add a key/value pair for it
|
||||
NSString *clientID = [self clientID];
|
||||
[parameters_ setObject:clientID forKey:@"guid"];
|
||||
|
||||
close(configFile_);
|
||||
configFile_ = -1;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Per user per machine
|
||||
- (NSString *)clientID {
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey];
|
||||
if (crashClientID) {
|
||||
return crashClientID;
|
||||
}
|
||||
|
||||
// Otherwise, if we have no client id, generate one!
|
||||
srandom((int)[[NSDate date] timeIntervalSince1970]);
|
||||
long clientId1 = random();
|
||||
long clientId2 = random();
|
||||
long clientId3 = random();
|
||||
crashClientID = [NSString stringWithFormat:@"%x%x%x",
|
||||
clientId1, clientId2, clientId3];
|
||||
|
||||
[ud setObject:crashClientID forKey:kClientIdPreferenceKey];
|
||||
[ud synchronize];
|
||||
return crashClientID;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)readLogFileData {
|
||||
unsigned int logFileCounter = 0;
|
||||
|
||||
NSString *logPath;
|
||||
size_t logFileTailSize =
|
||||
[[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue];
|
||||
|
||||
NSMutableArray *logFilenames; // An array of NSString, one per log file
|
||||
logFilenames = [[NSMutableArray alloc] init];
|
||||
|
||||
char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX";
|
||||
char *tmpDir = mkdtemp(tmpDirTemplate);
|
||||
|
||||
// Construct key names for the keys we expect to contain log file paths
|
||||
for(logFileCounter = 0;; logFileCounter++) {
|
||||
NSString *logFileKey = [NSString stringWithFormat:@"%@%d",
|
||||
@BREAKPAD_LOGFILE_KEY_PREFIX,
|
||||
logFileCounter];
|
||||
|
||||
logPath = [parameters_ objectForKey:logFileKey];
|
||||
|
||||
// They should all be consecutive, so if we don't find one, assume
|
||||
// we're done
|
||||
|
||||
if (!logPath) {
|
||||
break;
|
||||
}
|
||||
|
||||
NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath];
|
||||
|
||||
if (entireLogFile == nil) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSRange fileRange;
|
||||
|
||||
// Truncate the log file, only if necessary
|
||||
|
||||
if ([entireLogFile length] <= logFileTailSize) {
|
||||
fileRange = NSMakeRange(0, [entireLogFile length]);
|
||||
} else {
|
||||
fileRange = NSMakeRange([entireLogFile length] - logFileTailSize,
|
||||
logFileTailSize);
|
||||
}
|
||||
|
||||
char tmpFilenameTemplate[100];
|
||||
|
||||
// Generate a template based on the log filename
|
||||
sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir,
|
||||
[[logPath lastPathComponent] fileSystemRepresentation]);
|
||||
|
||||
char *tmpFile = mktemp(tmpFilenameTemplate);
|
||||
|
||||
NSData *logSubdata = [entireLogFile subdataWithRange:fileRange];
|
||||
NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile];
|
||||
[logSubdata writeToFile:tmpFileString atomically:NO];
|
||||
|
||||
[logFilenames addObject:[tmpFileString lastPathComponent]];
|
||||
[entireLogFile release];
|
||||
}
|
||||
|
||||
if ([logFilenames count] == 0) {
|
||||
[logFilenames release];
|
||||
logFileData_ = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
// now, bzip all files into one
|
||||
NSTask *tarTask = [[NSTask alloc] init];
|
||||
|
||||
[tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]];
|
||||
[tarTask setLaunchPath:@"/usr/bin/tar"];
|
||||
|
||||
NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf",
|
||||
@"log.tar.bz2",nil];
|
||||
[bzipArgs addObjectsFromArray:logFilenames];
|
||||
|
||||
[logFilenames release];
|
||||
|
||||
[tarTask setArguments:bzipArgs];
|
||||
[tarTask launch];
|
||||
[tarTask waitUntilExit];
|
||||
[tarTask release];
|
||||
|
||||
NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir];
|
||||
logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile];
|
||||
if (logFileData_ == nil) {
|
||||
GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)readMinidumpData {
|
||||
NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
|
||||
NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
|
||||
|
||||
if (![minidumpID length])
|
||||
return NO;
|
||||
|
||||
NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID];
|
||||
path = [path stringByAppendingPathExtension:@"dmp"];
|
||||
|
||||
// check the size of the minidump and limit it to a reasonable size
|
||||
// before attempting to load into memory and upload
|
||||
const char *fileName = [path fileSystemRepresentation];
|
||||
struct stat fileStatus;
|
||||
|
||||
BOOL success = YES;
|
||||
|
||||
if (!stat(fileName, &fileStatus)) {
|
||||
if (fileStatus.st_size > kMinidumpFileLengthLimit) {
|
||||
fprintf(stderr, "Breakpad Reporter: minidump file too large " \
|
||||
"to upload : %d\n", (int)fileStatus.st_size);
|
||||
success = NO;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \
|
||||
"file length\n");
|
||||
success = NO;
|
||||
}
|
||||
|
||||
if (success) {
|
||||
minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path];
|
||||
success = ([minidumpContents_ length] ? YES : NO);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
// something wrong with the minidump file -- delete it
|
||||
unlink(fileName);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (BOOL)askUserPermissionToSend {
|
||||
// Initialize Cocoa, needed to display the alert
|
||||
@ -560,12 +254,14 @@ NSString *const kDefaultServerType = @"google";
|
||||
|
||||
buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
|
||||
|
||||
// Extract info from the user into the parameters_ dictionary
|
||||
// Extract info from the user into the uploader_.
|
||||
if ([self commentsValue]) {
|
||||
[parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS];
|
||||
[[uploader_ parameters] setObject:[self commentsValue]
|
||||
forKey:@BREAKPAD_COMMENTS];
|
||||
}
|
||||
if ([self emailValue]) {
|
||||
[parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL];
|
||||
[[uploader_ parameters] setObject:[self emailValue]
|
||||
forKey:@BREAKPAD_EMAIL];
|
||||
}
|
||||
} else {
|
||||
// Create an alert panel to tell the user something happened
|
||||
@ -804,9 +500,9 @@ doCommandBySelector:(SEL)commandSelector {
|
||||
#pragma mark -
|
||||
//=============================================================================
|
||||
- (BOOL)reportIntervalElapsed {
|
||||
float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
|
||||
floatValue];
|
||||
NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
|
||||
float interval = [[[uploader_ parameters]
|
||||
objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue];
|
||||
NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
NSMutableDictionary *programDict =
|
||||
[NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
|
||||
@ -831,29 +527,30 @@ doCommandBySelector:(SEL)commandSelector {
|
||||
}
|
||||
|
||||
- (BOOL)isOnDemand {
|
||||
return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND]
|
||||
return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND]
|
||||
isEqualToString:@"YES"];
|
||||
}
|
||||
|
||||
- (BOOL)shouldSubmitSilently {
|
||||
return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
|
||||
return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM]
|
||||
isEqualToString:@"YES"];
|
||||
}
|
||||
|
||||
- (BOOL)shouldRequestComments {
|
||||
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
|
||||
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS]
|
||||
isEqualToString:@"YES"];
|
||||
}
|
||||
|
||||
- (BOOL)shouldRequestEmail {
|
||||
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL]
|
||||
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL]
|
||||
isEqualToString:@"YES"];
|
||||
}
|
||||
|
||||
- (NSString*)shortDialogMessage {
|
||||
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
NSString *displayName =
|
||||
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
if (![displayName length])
|
||||
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
|
||||
displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
|
||||
|
||||
if ([self isOnDemand]) {
|
||||
return [NSString
|
||||
@ -867,11 +564,12 @@ doCommandBySelector:(SEL)commandSelector {
|
||||
}
|
||||
|
||||
- (NSString*)explanatoryDialogText {
|
||||
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
NSString *displayName =
|
||||
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||
if (![displayName length])
|
||||
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
|
||||
displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
|
||||
|
||||
NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR];
|
||||
NSString *vendor = [[uploader_ parameters] objectForKey:@BREAKPAD_VENDOR];
|
||||
if (![vendor length])
|
||||
vendor = @"unknown vendor";
|
||||
|
||||
@ -888,8 +586,8 @@ doCommandBySelector:(SEL)commandSelector {
|
||||
|
||||
- (NSTimeInterval)messageTimeout {
|
||||
// Get the timeout value for the notification.
|
||||
NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
|
||||
floatValue];
|
||||
NSTimeInterval timeout = [[[uploader_ parameters]
|
||||
objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue];
|
||||
// Require a timeout of at least a minute (except 0, which means no timeout).
|
||||
if (timeout > 0.001 && timeout < 60.0) {
|
||||
timeout = 60.0;
|
||||
@ -897,194 +595,13 @@ doCommandBySelector:(SEL)commandSelector {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
- (void)createServerParameterDictionaries {
|
||||
serverDictionary_ = [[NSMutableDictionary alloc] init];
|
||||
socorroDictionary_ = [[NSMutableDictionary alloc] init];
|
||||
googleDictionary_ = [[NSMutableDictionary alloc] init];
|
||||
extraServerVars_ = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType];
|
||||
[serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType];
|
||||
|
||||
[googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME];
|
||||
[googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL];
|
||||
[googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS];
|
||||
[googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT];
|
||||
[googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION];
|
||||
|
||||
[socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS];
|
||||
[socorroDictionary_ setObject:@"CrashTime"
|
||||
forKey:@BREAKPAD_PROCESS_CRASH_TIME];
|
||||
[socorroDictionary_ setObject:@"StartupTime"
|
||||
forKey:@BREAKPAD_PROCESS_START_TIME];
|
||||
[socorroDictionary_ setObject:@"Version"
|
||||
forKey:@BREAKPAD_VERSION];
|
||||
[socorroDictionary_ setObject:@"ProductName"
|
||||
forKey:@BREAKPAD_PRODUCT];
|
||||
[socorroDictionary_ setObject:@"Email"
|
||||
forKey:@BREAKPAD_EMAIL];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {
|
||||
if (serverType == nil || [serverType length] == 0) {
|
||||
return [serverDictionary_ objectForKey:kDefaultServerType];
|
||||
}
|
||||
return [serverDictionary_ objectForKey:serverType];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)urlParameterDictionary {
|
||||
NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE];
|
||||
return [self dictionaryForServerType:serverType];
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters {
|
||||
NSDictionary *urlParameterNames = [self urlParameterDictionary];
|
||||
|
||||
id key;
|
||||
NSEnumerator *enumerator = [parameters_ keyEnumerator];
|
||||
|
||||
while ((key = [enumerator nextObject])) {
|
||||
// The key from parameters_ corresponds to a key in
|
||||
// urlParameterNames. The value in parameters_ gets stored in
|
||||
// crashParameters with a key that is the value in
|
||||
// urlParameterNames.
|
||||
|
||||
// For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and
|
||||
// urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP
|
||||
// URL parameter becomes [pname => "FOOBAR"].
|
||||
NSString *breakpadParameterName = (NSString *)key;
|
||||
NSString *urlParameter = [urlParameterNames
|
||||
objectForKey:breakpadParameterName];
|
||||
if (urlParameter) {
|
||||
[crashParameters setObject:[parameters_ objectForKey:key]
|
||||
forKey:urlParameter];
|
||||
}
|
||||
}
|
||||
|
||||
// Now, add the parameters that were added by the application.
|
||||
enumerator = [extraServerVars_ keyEnumerator];
|
||||
|
||||
while ((key = [enumerator nextObject])) {
|
||||
NSString *urlParameterName = (NSString *)key;
|
||||
NSString *urlParameterValue =
|
||||
[extraServerVars_ objectForKey:urlParameterName];
|
||||
[crashParameters setObject:urlParameterValue
|
||||
forKey:urlParameterName];
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)addServerParameter:(id)value forKey:(NSString *)key {
|
||||
[extraServerVars_ setObject:value forKey:key];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)report {
|
||||
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
|
||||
HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url];
|
||||
NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary];
|
||||
|
||||
if (![self populateServerDictionary:uploadParameters]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[upload setParameters:uploadParameters];
|
||||
|
||||
// Add minidump file
|
||||
if (minidumpContents_) {
|
||||
[upload addFileContents:minidumpContents_ name:@"upload_file_minidump"];
|
||||
|
||||
// Send it
|
||||
NSError *error = nil;
|
||||
NSData *data = [upload send:&error];
|
||||
NSString *result = [[NSString alloc] initWithData:data
|
||||
encoding:NSUTF8StringEncoding];
|
||||
const char *reportID = "ERR";
|
||||
|
||||
if (error) {
|
||||
fprintf(stderr, "Breakpad Reporter: Send Error: %s\n",
|
||||
[[error description] UTF8String]);
|
||||
} else {
|
||||
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
|
||||
reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
|
||||
[self logUploadWithID:reportID];
|
||||
}
|
||||
|
||||
// rename the minidump file according to the id returned from the server
|
||||
NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
|
||||
NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey];
|
||||
|
||||
NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp",
|
||||
minidumpDir, minidumpID];
|
||||
NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp",
|
||||
minidumpDir, reportID];
|
||||
|
||||
const char *src = [srcString fileSystemRepresentation];
|
||||
const char *dest = [destString fileSystemRepresentation];
|
||||
|
||||
if (rename(src, dest) == 0) {
|
||||
GTMLoggerInfo(@"Breakpad Reporter: Renamed %s to %s after successful " \
|
||||
"upload",src, dest);
|
||||
}
|
||||
else {
|
||||
// can't rename - don't worry - it's not important for users
|
||||
GTMLoggerDebug(@"Breakpad Reporter: successful upload report ID = %s\n",
|
||||
reportID );
|
||||
}
|
||||
[result release];
|
||||
}
|
||||
|
||||
if (logFileData_) {
|
||||
HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url];
|
||||
|
||||
[uploadParameters setObject:@"log" forKey:@"type"];
|
||||
[logUpload setParameters:uploadParameters];
|
||||
[logUpload addFileContents:logFileData_ name:@"log"];
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *data = [logUpload send:&error];
|
||||
NSString *result = [[NSString alloc] initWithData:data
|
||||
encoding:NSUTF8StringEncoding];
|
||||
[result release];
|
||||
[logUpload release];
|
||||
}
|
||||
|
||||
[upload release];
|
||||
}
|
||||
|
||||
- (void)logUploadWithID:(const char *)uploadID {
|
||||
NSString *minidumpDir =
|
||||
[parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
|
||||
NSString *logFilePath = [NSString stringWithFormat:@"%@/%s",
|
||||
minidumpDir, kReporterLogFilename];
|
||||
NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n",
|
||||
[[NSDate date] timeIntervalSince1970], uploadID];
|
||||
NSData *logData = [logLine dataUsingEncoding:kCFStringEncodingUTF8];
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
if ([fileManager fileExistsAtPath:logFilePath]) {
|
||||
NSFileHandle *logFileHandle =
|
||||
[NSFileHandle fileHandleForWritingAtPath:logFilePath];
|
||||
[logFileHandle seekToEndOfFile];
|
||||
[logFileHandle writeData:logData];
|
||||
[logFileHandle closeFile];
|
||||
} else {
|
||||
[fileManager createFileAtPath:logFilePath
|
||||
contents:logData
|
||||
attributes:nil];
|
||||
}
|
||||
[uploader_ report];
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
- (void)dealloc {
|
||||
[parameters_ release];
|
||||
[minidumpContents_ release];
|
||||
[logFileData_ release];
|
||||
[googleDictionary_ release];
|
||||
[socorroDictionary_ release];
|
||||
[serverDictionary_ release];
|
||||
[extraServerVars_ release];
|
||||
[uploader_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -1175,40 +692,12 @@ int main(int argc, const char *argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Open the file before (potentially) switching to console user
|
||||
int configFile = open(argv[1], O_RDONLY, 0600);
|
||||
|
||||
if (configFile == -1) {
|
||||
GTMLoggerDebug(@"Couldn't open config file %s - %s",
|
||||
argv[1],
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
// we want to avoid a build-up of old config files even if they
|
||||
// have been incorrectly written by the framework
|
||||
unlink(argv[1]);
|
||||
|
||||
if (configFile == -1) {
|
||||
GTMLoggerDebug(@"Couldn't unlink config file %s - %s",
|
||||
argv[1],
|
||||
strerror(errno));
|
||||
Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]];
|
||||
if (!reporter) {
|
||||
GTMLoggerDebug(@"reporter initialization failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile];
|
||||
|
||||
// Gather the configuration data
|
||||
if (![reporter readConfigurationData]) {
|
||||
GTMLoggerDebug(@"reporter readConfigurationData failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Read the minidump into memory before we (potentially) switch from the
|
||||
// root user
|
||||
[reporter readMinidumpData];
|
||||
|
||||
[reporter readLogFileData];
|
||||
|
||||
// only submit a report if we have not recently crashed in the past
|
||||
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
|
||||
BOOL okayToSend = NO;
|
||||
|
@ -43,8 +43,8 @@
|
||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/tests/auto_tempdir.h"
|
||||
#include "client/mac/tests/spawn_child_process.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -84,12 +84,14 @@ public:
|
||||
AutoTempDir temp_dir;
|
||||
// Counter just to ensure that we don't hit the same port again
|
||||
static int i;
|
||||
bool filter_callback_called;
|
||||
|
||||
void SetUp() {
|
||||
sprintf(mach_port_name,
|
||||
"com.google.breakpad.ServerTest.%d.%d", getpid(),
|
||||
CrashGenerationServerTest::i++);
|
||||
"com.google.breakpad.ServerTest.%d.%d", getpid(),
|
||||
CrashGenerationServerTest::i++);
|
||||
child_pid = (pid_t)-1;
|
||||
filter_callback_called = false;
|
||||
}
|
||||
};
|
||||
int CrashGenerationServerTest::i = 0;
|
||||
@ -97,12 +99,14 @@ int CrashGenerationServerTest::i = 0;
|
||||
// Test that starting and stopping a server works
|
||||
TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // generate dumps
|
||||
""); // dump path
|
||||
NULL, // filter callback
|
||||
NULL, // filter context
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // generate dumps
|
||||
""); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
ASSERT_TRUE(server.Stop());
|
||||
}
|
||||
@ -111,12 +115,14 @@ TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
||||
// Test without actually dumping
|
||||
TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // don't generate dumps
|
||||
temp_dir.path); // dump path
|
||||
NULL, // filter callback
|
||||
NULL, // filter context
|
||||
NULL, // dump callback
|
||||
NULL, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
false, // don't generate dumps
|
||||
temp_dir.path()); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
@ -133,7 +139,7 @@ TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
EXPECT_TRUE(server.Stop());
|
||||
// check that no minidump was written
|
||||
string pattern = temp_dir.path + "/*";
|
||||
string pattern = temp_dir.path() + "/*";
|
||||
glob_t dirContents;
|
||||
ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
|
||||
EXPECT_EQ(GLOB_NOMATCH, ret);
|
||||
@ -142,7 +148,7 @@ TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
||||
}
|
||||
|
||||
void dumpCallback(void *context, const ClientInfo &client_info,
|
||||
const std::string &file_path) {
|
||||
const std::string &file_path) {
|
||||
if (context) {
|
||||
CrashGenerationServerTest* self =
|
||||
reinterpret_cast<CrashGenerationServerTest*>(context);
|
||||
@ -161,12 +167,14 @@ void *RequestDump(void *context) {
|
||||
// Test that actually writing a minidump works
|
||||
TEST_F(CrashGenerationServerTest, testRequestDump) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path); // dump path
|
||||
NULL, // filter callback
|
||||
NULL, // filter context
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path()); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
@ -209,12 +217,14 @@ static void Crasher() {
|
||||
// the parent.
|
||||
TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path); // dump path
|
||||
NULL, // filter callback
|
||||
NULL, // filter context
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path()); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
@ -270,12 +280,14 @@ TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
||||
// produces a valid minidump.
|
||||
TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path); // dump path
|
||||
NULL, // filter callback
|
||||
NULL, // filter context
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path()); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
// Spawn a child process
|
||||
@ -342,4 +354,45 @@ const u_int32_t kExpectedContext =
|
||||
}
|
||||
#endif
|
||||
|
||||
bool filter_callback(void* context) {
|
||||
CrashGenerationServerTest* self =
|
||||
reinterpret_cast<CrashGenerationServerTest*>(context);
|
||||
self->filter_callback_called = true;
|
||||
// veto dump generation
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test that a filter callback can veto minidump writing.
|
||||
TEST_F(CrashGenerationServerTest, testFilter) {
|
||||
CrashGenerationServer server(mach_port_name,
|
||||
filter_callback, // filter callback
|
||||
this, // filter context
|
||||
dumpCallback, // dump callback
|
||||
this, // dump context
|
||||
NULL, // exit callback
|
||||
NULL, // exit context
|
||||
true, // generate dumps
|
||||
temp_dir.path()); // dump path
|
||||
ASSERT_TRUE(server.Start());
|
||||
|
||||
pid_t pid = fork();
|
||||
ASSERT_NE(-1, pid);
|
||||
if (pid == 0) {
|
||||
// Instantiate an OOP exception handler.
|
||||
ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name);
|
||||
Crasher();
|
||||
// not reached
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int ret;
|
||||
ASSERT_EQ(pid, waitpid(pid, &ret, 0));
|
||||
EXPECT_FALSE(WIFEXITED(ret));
|
||||
EXPECT_TRUE(server.Stop());
|
||||
|
||||
// check that no minidump was written
|
||||
EXPECT_TRUE(last_dump_name.empty());
|
||||
EXPECT_TRUE(filter_callback_called);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -36,8 +36,8 @@
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "client/mac/tests/auto_tempdir.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -64,6 +64,7 @@ using testing::Test;
|
||||
|
||||
class ExceptionHandlerTest : public Test {
|
||||
public:
|
||||
void InProcessCrash(bool aborting);
|
||||
AutoTempDir tempDir;
|
||||
string lastDumpName;
|
||||
};
|
||||
@ -75,8 +76,13 @@ static void Crasher() {
|
||||
fprintf(stdout, "A = %d", *a);
|
||||
}
|
||||
|
||||
static void SoonToCrash() {
|
||||
Crasher();
|
||||
static void AbortCrasher() {
|
||||
fprintf(stdout, "Going to crash...\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
static void SoonToCrash(void(*crasher)()) {
|
||||
crasher();
|
||||
}
|
||||
|
||||
static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
@ -94,7 +100,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
void ExceptionHandlerTest::InProcessCrash(bool aborting) {
|
||||
// Give the child process a pipe to report back on.
|
||||
int fds[2];
|
||||
ASSERT_EQ(0, pipe(fds));
|
||||
@ -103,9 +109,9 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
if (pid == 0) {
|
||||
// In the child process.
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// crash
|
||||
SoonToCrash();
|
||||
SoonToCrash(aborting ? &AbortCrasher : &Crasher);
|
||||
// not reached
|
||||
exit(1);
|
||||
}
|
||||
@ -128,6 +134,16 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
InProcessCrash(false);
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
TEST_F(ExceptionHandlerTest, InProcessAbort) {
|
||||
InProcessCrash(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
|
||||
void *context, bool success) {
|
||||
ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
|
||||
@ -141,7 +157,8 @@ static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, WriteMinidump) {
|
||||
ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
|
||||
NULL);
|
||||
ASSERT_TRUE(eh.WriteMinidump());
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
@ -159,7 +176,8 @@ TEST_F(ExceptionHandlerTest, WriteMinidump) {
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
|
||||
ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true,
|
||||
NULL);
|
||||
ASSERT_TRUE(eh.WriteMinidump(true));
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
@ -227,10 +245,10 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) {
|
||||
|
||||
// Write a minidump of the child process.
|
||||
bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
|
||||
child_thread,
|
||||
tempDir.path,
|
||||
DumpNameMDCallback,
|
||||
this);
|
||||
child_thread,
|
||||
tempDir.path(),
|
||||
DumpNameMDCallback,
|
||||
this);
|
||||
ASSERT_EQ(true, result);
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
@ -267,7 +285,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// Get some executable memory.
|
||||
char* memory =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
@ -329,20 +347,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
|
||||
ASSERT_TRUE(context);
|
||||
|
||||
u_int64_t instruction_pointer;
|
||||
switch (context->GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
instruction_pointer = context->GetContextX86()->eip;
|
||||
break;
|
||||
case MD_CONTEXT_AMD64:
|
||||
instruction_pointer = context->GetContextAMD64()->rip;
|
||||
break;
|
||||
case MD_CONTEXT_ARM:
|
||||
instruction_pointer = context->GetContextARM()->iregs[15];
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
|
||||
break;
|
||||
}
|
||||
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||
|
||||
MinidumpMemoryRegion* region =
|
||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||
@ -379,7 +384,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// Get some executable memory.
|
||||
char* memory =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
@ -441,20 +446,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
||||
ASSERT_TRUE(context);
|
||||
|
||||
u_int64_t instruction_pointer;
|
||||
switch (context->GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
instruction_pointer = context->GetContextX86()->eip;
|
||||
break;
|
||||
case MD_CONTEXT_AMD64:
|
||||
instruction_pointer = context->GetContextAMD64()->rip;
|
||||
break;
|
||||
case MD_CONTEXT_ARM:
|
||||
instruction_pointer = context->GetContextARM()->iregs[15];
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
|
||||
break;
|
||||
}
|
||||
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||
|
||||
MinidumpMemoryRegion* region =
|
||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||
@ -491,7 +483,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// Get some executable memory.
|
||||
char* memory =
|
||||
reinterpret_cast<char*>(mmap(NULL,
|
||||
@ -553,20 +545,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
||||
ASSERT_TRUE(context);
|
||||
|
||||
u_int64_t instruction_pointer;
|
||||
switch (context->GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
instruction_pointer = context->GetContextX86()->eip;
|
||||
break;
|
||||
case MD_CONTEXT_AMD64:
|
||||
instruction_pointer = context->GetContextAMD64()->rip;
|
||||
break;
|
||||
case MD_CONTEXT_ARM:
|
||||
instruction_pointer = context->GetContextARM()->iregs[15];
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
|
||||
break;
|
||||
}
|
||||
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||
|
||||
MinidumpMemoryRegion* region =
|
||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||
@ -594,7 +573,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// Try calling a NULL pointer.
|
||||
typedef void (*void_function)(void);
|
||||
void_function memory_function =
|
||||
@ -651,7 +630,7 @@ TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
|
||||
// Run an extra thread so >2 memory regions will be written.
|
||||
pthread_t junk_thread;
|
||||
|
@ -41,9 +41,9 @@
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "client/mac/tests/auto_tempdir.h"
|
||||
#include "client/mac/tests/spawn_child_process.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -88,8 +88,8 @@ static void *Junk(void* data) {
|
||||
|
||||
TEST_F(MinidumpGeneratorTest, InProcess) {
|
||||
MinidumpGenerator generator;
|
||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
||||
NULL);
|
||||
string dump_filename =
|
||||
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||
|
||||
// Run an extra thread since MinidumpGenerator assumes there
|
||||
// are 2 or more threads.
|
||||
@ -179,8 +179,8 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
||||
|
||||
// Write a minidump of the child process.
|
||||
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
||||
NULL);
|
||||
string dump_filename =
|
||||
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
@ -258,8 +258,8 @@ TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
|
||||
|
||||
// Write a minidump of the child process.
|
||||
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
||||
NULL);
|
||||
string dump_filename =
|
||||
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
||||
|
||||
// Ensure that minidump file exists and is > 0 bytes.
|
||||
|
@ -48,11 +48,16 @@ namespace google_breakpad {
|
||||
|
||||
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
|
||||
|
||||
MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) {
|
||||
MinidumpFileWriter::MinidumpFileWriter()
|
||||
: file_(-1),
|
||||
close_file_when_destroyed_(true),
|
||||
position_(0),
|
||||
size_(0) {
|
||||
}
|
||||
|
||||
MinidumpFileWriter::~MinidumpFileWriter() {
|
||||
Close();
|
||||
if (close_file_when_destroyed_)
|
||||
Close();
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Open(const char *path) {
|
||||
@ -66,6 +71,12 @@ bool MinidumpFileWriter::Open(const char *path) {
|
||||
return file_ != -1;
|
||||
}
|
||||
|
||||
void MinidumpFileWriter::SetFile(const int file) {
|
||||
assert(file_ == -1);
|
||||
file_ = file;
|
||||
close_file_when_destroyed_ = false;
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Close() {
|
||||
bool result = true;
|
||||
|
||||
|
@ -55,6 +55,16 @@ template<typename MDType> class TypedMDRVA;
|
||||
// header->get()->signature = MD_HEADER_SIGNATURE;
|
||||
// :
|
||||
// writer.Close();
|
||||
//
|
||||
// An alternative is to use SetFile and provide a file descriptor:
|
||||
// MinidumpFileWriter writer;
|
||||
// writer.SetFile(minidump_fd);
|
||||
// TypedMDRVA<MDRawHeader> header(&writer_);
|
||||
// header.Allocate();
|
||||
// header->get()->signature = MD_HEADER_SIGNATURE;
|
||||
// :
|
||||
// writer.Close();
|
||||
|
||||
class MinidumpFileWriter {
|
||||
public:
|
||||
// Invalid MDRVA (Minidump Relative Virtual Address)
|
||||
@ -66,11 +76,19 @@ public:
|
||||
|
||||
// Open |path| as the destination of the minidump data. Any existing file
|
||||
// will be overwritten.
|
||||
// Return true on success, or false on failure
|
||||
// Return true on success, or false on failure.
|
||||
bool Open(const char *path);
|
||||
|
||||
// Close the current file
|
||||
// Return true on success, or false on failure
|
||||
// Sets the file descriptor |file| as the destination of the minidump data.
|
||||
// Can be used as an alternative to Open() when a file descriptor is
|
||||
// available.
|
||||
// Note that |fd| is not closed when the instance of MinidumpFileWriter is
|
||||
// destroyed.
|
||||
void SetFile(const int file);
|
||||
|
||||
// Close the current file (that was either created when Open was called, or
|
||||
// specified with SetFile).
|
||||
// Return true on success, or false on failure.
|
||||
bool Close();
|
||||
|
||||
// Copy the contents of |str| to a MDString and write it to the file.
|
||||
@ -106,9 +124,12 @@ public:
|
||||
// unable to allocate the bytes.
|
||||
MDRVA Allocate(size_t size);
|
||||
|
||||
// The file descriptor for the output file
|
||||
// The file descriptor for the output file.
|
||||
int file_;
|
||||
|
||||
// Whether |file_| should be closed when the instance is destroyed.
|
||||
bool close_file_when_destroyed_;
|
||||
|
||||
// Current position in buffer
|
||||
MDRVA position_;
|
||||
|
||||
@ -151,7 +172,8 @@ class UntypedMDRVA {
|
||||
|
||||
// Return size and position
|
||||
inline MDLocationDescriptor location() const {
|
||||
MDLocationDescriptor location = { static_cast<int>(size_), position_ };
|
||||
MDLocationDescriptor location = { static_cast<u_int32_t>(size_),
|
||||
position_ };
|
||||
return location;
|
||||
}
|
||||
|
||||
|
41
3rdparty/google-breakpad/common/basictypes.h
vendored
Normal file
41
3rdparty/google-breakpad/common/basictypes.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2011 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 COMMON_BASICTYPES_H_
|
||||
#define COMMON_BASICTYPES_H_
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#ifndef DISALLOW_COPY_AND_ASSIGN
|
||||
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&); \
|
||||
void operator=(const TypeName&)
|
||||
#endif // DISALLOW_COPY_AND_ASSIGN
|
||||
|
||||
#endif // COMMON_BASICTYPES_H_
|
@ -45,6 +45,8 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A buffer holding a series of bytes.
|
||||
@ -164,7 +166,7 @@ class ByteCursor {
|
||||
// byte buffer does not contain a terminating zero, clear this cursor's
|
||||
// complete_ flag, and set STR to the empty string. Return a reference to
|
||||
// this cursor.
|
||||
ByteCursor &CString(std::string *str) {
|
||||
ByteCursor &CString(string *str) {
|
||||
const uint8_t *end
|
||||
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
|
||||
if (end) {
|
||||
@ -191,7 +193,7 @@ class ByteCursor {
|
||||
//
|
||||
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
|
||||
// cursor by LIMIT bytes.
|
||||
ByteCursor &CString(std::string *str, size_t limit) {
|
||||
ByteCursor &CString(string *str, size_t limit) {
|
||||
if (CheckAvailable(limit)) {
|
||||
const uint8_t *end
|
||||
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/cfi_assembler.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using dwarf2reader::ByteReader;
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
@ -47,7 +48,6 @@ using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
using testing::Test;
|
||||
|
||||
struct ReaderFixture {
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/test_assembler.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -49,7 +50,6 @@ using dwarf2reader::DwarfPointerEncoding;
|
||||
using google_breakpad::test_assembler::Endianness;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::string;
|
||||
|
||||
class CFISection: public Section {
|
||||
public:
|
||||
|
@ -31,10 +31,13 @@
|
||||
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
|
||||
// See dwarf2diehandler.h for details.
|
||||
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
DIEDispatcher::~DIEDispatcher() {
|
||||
@ -183,4 +186,14 @@ void DIEDispatcher::ProcessAttributeString(uint64 offset,
|
||||
current.handler_->ProcessAttributeString(attr, form, data);
|
||||
}
|
||||
|
||||
void DIEDispatcher::ProcessAttributeSignature(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signature) {
|
||||
HandlerStack ¤t = die_handlers_.top();
|
||||
// This had better be an attribute of the DIE we were meant to handle.
|
||||
assert(offset == current.offset_);
|
||||
current.handler_->ProcessAttributeSignature(attr, form, signature);
|
||||
}
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
@ -157,10 +157,12 @@
|
||||
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
@ -209,6 +211,9 @@ class DIEHandler {
|
||||
virtual void ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string& data) { }
|
||||
virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signture) { }
|
||||
|
||||
// Once we have reported all the DIE's attributes' values, we call
|
||||
// this member function. If it returns false, we skip all the DIE's
|
||||
@ -314,6 +319,10 @@ class DIEDispatcher: public Dwarf2Handler {
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data);
|
||||
void ProcessAttributeSignature(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signature);
|
||||
void EndDIE(uint64 offset);
|
||||
|
||||
private:
|
||||
@ -347,7 +356,7 @@ class DIEDispatcher: public Dwarf2Handler {
|
||||
// - When we decide to ignore a subtree, we only push an entry on
|
||||
// the stack for the root of the tree being ignored, rather than
|
||||
// pushing lots of stack entries with handler_ set to NULL.
|
||||
stack<HandlerStack> die_handlers_;
|
||||
std::stack<HandlerStack> die_handlers_;
|
||||
|
||||
// The root handler. We don't push it on die_handlers_ until we
|
||||
// actually get the StartDIE call for the root.
|
||||
|
@ -32,9 +32,15 @@
|
||||
|
||||
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using std::make_pair;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::ContainerEq;
|
||||
@ -65,6 +71,8 @@ class MockDIEHandler: public DIEHandler {
|
||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||
MOCK_METHOD3(ProcessAttributeString,
|
||||
void(DwarfAttribute, DwarfForm, const string &));
|
||||
MOCK_METHOD3(ProcessAttributeSignature,
|
||||
void(DwarfAttribute, DwarfForm, uint64));
|
||||
MOCK_METHOD0(EndAttributes, bool());
|
||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||
const AttributeList &));
|
||||
@ -83,6 +91,8 @@ class MockRootDIEHandler: public RootDIEHandler {
|
||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||
MOCK_METHOD3(ProcessAttributeString,
|
||||
void(DwarfAttribute, DwarfForm, const string &));
|
||||
MOCK_METHOD3(ProcessAttributeSignature,
|
||||
void(DwarfAttribute, DwarfForm, uint64));
|
||||
MOCK_METHOD0(EndAttributes, bool());
|
||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||
const AttributeList &));
|
||||
@ -238,6 +248,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
|
||||
(DwarfForm) 0x15762fec,
|
||||
StrEq(str)))
|
||||
.WillOnce(Return());
|
||||
EXPECT_CALL(mock_root_handler,
|
||||
ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
|
||||
(DwarfForm) 0x4159f138,
|
||||
0x94682463613e6a5fULL))
|
||||
.WillOnce(Return());
|
||||
EXPECT_CALL(mock_root_handler, EndAttributes())
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
|
||||
@ -279,6 +294,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
|
||||
(DwarfAttribute) 0x310ed065,
|
||||
(DwarfForm) 0x15762fec,
|
||||
str);
|
||||
die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
|
||||
(DwarfAttribute) 0x58790d72,
|
||||
(DwarfForm) 0x4159f138,
|
||||
0x94682463613e6a5fULL);
|
||||
|
||||
// Finish the root DIE (and thus the CU).
|
||||
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
|
||||
|
@ -143,7 +143,13 @@ enum DwarfForm {
|
||||
DW_FORM_ref4 = 0x13,
|
||||
DW_FORM_ref8 = 0x14,
|
||||
DW_FORM_ref_udata = 0x15,
|
||||
DW_FORM_indirect = 0x16
|
||||
DW_FORM_indirect = 0x16,
|
||||
|
||||
// Added in DWARF 4:
|
||||
DW_FORM_sec_offset = 0x17,
|
||||
DW_FORM_exprloc = 0x18,
|
||||
DW_FORM_flag_present = 0x19,
|
||||
DW_FORM_ref_sig8 = 0x20
|
||||
};
|
||||
|
||||
// Attribute names and codes
|
||||
|
@ -39,12 +39,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/line_state_machine.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
@ -74,7 +77,7 @@ void CompilationUnit::ReadAbbrevs() {
|
||||
iter = sections_.find("__debug_abbrev");
|
||||
assert(iter != sections_.end());
|
||||
|
||||
abbrevs_ = new vector<Abbrev>;
|
||||
abbrevs_ = new std::vector<Abbrev>;
|
||||
abbrevs_->resize(1);
|
||||
|
||||
// The only way to check whether we are reading over the end of the
|
||||
@ -121,7 +124,7 @@ void CompilationUnit::ReadAbbrevs() {
|
||||
const enum DwarfAttribute name =
|
||||
static_cast<enum DwarfAttribute>(nametemp);
|
||||
const enum DwarfForm form = static_cast<enum DwarfForm>(formtemp);
|
||||
abbrev.attributes.push_back(make_pair(name, form));
|
||||
abbrev.attributes.push_back(std::make_pair(name, form));
|
||||
}
|
||||
assert(abbrev.number == abbrevs_->size());
|
||||
abbrevs_->push_back(abbrev);
|
||||
@ -150,41 +153,35 @@ const char* CompilationUnit::SkipAttribute(const char* start,
|
||||
&len));
|
||||
start += len;
|
||||
return SkipAttribute(start, form);
|
||||
break;
|
||||
|
||||
case DW_FORM_flag_present:
|
||||
return start;
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_flag:
|
||||
case DW_FORM_ref1:
|
||||
return start + 1;
|
||||
break;
|
||||
case DW_FORM_ref2:
|
||||
case DW_FORM_data2:
|
||||
return start + 2;
|
||||
break;
|
||||
case DW_FORM_ref4:
|
||||
case DW_FORM_data4:
|
||||
return start + 4;
|
||||
break;
|
||||
case DW_FORM_ref8:
|
||||
case DW_FORM_data8:
|
||||
case DW_FORM_ref_sig8:
|
||||
return start + 8;
|
||||
break;
|
||||
case DW_FORM_string:
|
||||
return start + strlen(start) + 1;
|
||||
break;
|
||||
case DW_FORM_udata:
|
||||
case DW_FORM_ref_udata:
|
||||
reader_->ReadUnsignedLEB128(start, &len);
|
||||
return start + len;
|
||||
break;
|
||||
|
||||
case DW_FORM_sdata:
|
||||
reader_->ReadSignedLEB128(start, &len);
|
||||
return start + len;
|
||||
break;
|
||||
case DW_FORM_addr:
|
||||
return start + reader_->AddressSize();
|
||||
break;
|
||||
case DW_FORM_ref_addr:
|
||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||
// offset size.
|
||||
@ -194,27 +191,21 @@ const char* CompilationUnit::SkipAttribute(const char* start,
|
||||
} else if (header_.version == 3) {
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
break;
|
||||
|
||||
case DW_FORM_block1:
|
||||
return start + 1 + reader_->ReadOneByte(start);
|
||||
break;
|
||||
case DW_FORM_block2:
|
||||
return start + 2 + reader_->ReadTwoBytes(start);
|
||||
break;
|
||||
case DW_FORM_block4:
|
||||
return start + 4 + reader_->ReadFourBytes(start);
|
||||
break;
|
||||
case DW_FORM_block: {
|
||||
case DW_FORM_block:
|
||||
case DW_FORM_exprloc: {
|
||||
uint64 size = reader_->ReadUnsignedLEB128(start, &len);
|
||||
return start + size + len;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_strp:
|
||||
return start + reader_->OffsetSize();
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr,"Unhandled form type");
|
||||
case DW_FORM_sec_offset:
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
fprintf(stderr,"Unhandled form type");
|
||||
return NULL;
|
||||
@ -326,85 +317,78 @@ const char* CompilationUnit::ProcessAttribute(
|
||||
&len));
|
||||
start += len;
|
||||
return ProcessAttribute(dieoffset, start, attr, form);
|
||||
break;
|
||||
|
||||
case DW_FORM_flag_present:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1);
|
||||
return start;
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_flag:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOneByte(start));
|
||||
return start + 1;
|
||||
break;
|
||||
case DW_FORM_data2:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadTwoBytes(start));
|
||||
return start + 2;
|
||||
break;
|
||||
case DW_FORM_data4:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadFourBytes(start));
|
||||
return start + 4;
|
||||
break;
|
||||
case DW_FORM_data8:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadEightBytes(start));
|
||||
return start + 8;
|
||||
break;
|
||||
case DW_FORM_string: {
|
||||
const char* str = start;
|
||||
handler_->ProcessAttributeString(dieoffset, attr, form,
|
||||
str);
|
||||
return start + strlen(str) + 1;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_udata:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadUnsignedLEB128(start,
|
||||
&len));
|
||||
return start + len;
|
||||
break;
|
||||
|
||||
case DW_FORM_sdata:
|
||||
handler_->ProcessAttributeSigned(dieoffset, attr, form,
|
||||
reader_->ReadSignedLEB128(start, &len));
|
||||
return start + len;
|
||||
break;
|
||||
case DW_FORM_addr:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadAddress(start));
|
||||
return start + reader_->AddressSize();
|
||||
break;
|
||||
case DW_FORM_sec_offset:
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOffset(start));
|
||||
return start + reader_->OffsetSize();
|
||||
|
||||
case DW_FORM_ref1:
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadOneByte(start)
|
||||
+ offset_from_section_start_);
|
||||
return start + 1;
|
||||
break;
|
||||
case DW_FORM_ref2:
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadTwoBytes(start)
|
||||
+ offset_from_section_start_);
|
||||
return start + 2;
|
||||
break;
|
||||
case DW_FORM_ref4:
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadFourBytes(start)
|
||||
+ offset_from_section_start_);
|
||||
return start + 4;
|
||||
break;
|
||||
case DW_FORM_ref8:
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadEightBytes(start)
|
||||
+ offset_from_section_start_);
|
||||
return start + 8;
|
||||
break;
|
||||
case DW_FORM_ref_udata:
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadUnsignedLEB128(start,
|
||||
&len)
|
||||
+ offset_from_section_start_);
|
||||
return start + len;
|
||||
break;
|
||||
case DW_FORM_ref_addr:
|
||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||
// offset size.
|
||||
@ -419,35 +403,36 @@ const char* CompilationUnit::ProcessAttribute(
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
break;
|
||||
case DW_FORM_ref_sig8:
|
||||
handler_->ProcessAttributeSignature(dieoffset, attr, form,
|
||||
reader_->ReadEightBytes(start));
|
||||
return start + 8;
|
||||
|
||||
case DW_FORM_block1: {
|
||||
uint64 datalen = reader_->ReadOneByte(start);
|
||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1,
|
||||
datalen);
|
||||
datalen);
|
||||
return start + 1 + datalen;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_block2: {
|
||||
uint64 datalen = reader_->ReadTwoBytes(start);
|
||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2,
|
||||
datalen);
|
||||
datalen);
|
||||
return start + 2 + datalen;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_block4: {
|
||||
uint64 datalen = reader_->ReadFourBytes(start);
|
||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4,
|
||||
datalen);
|
||||
datalen);
|
||||
return start + 4 + datalen;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_block: {
|
||||
case DW_FORM_block:
|
||||
case DW_FORM_exprloc: {
|
||||
uint64 datalen = reader_->ReadUnsignedLEB128(start, &len);
|
||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len,
|
||||
datalen);
|
||||
datalen);
|
||||
return start + datalen + len;
|
||||
}
|
||||
break;
|
||||
case DW_FORM_strp: {
|
||||
assert(string_buffer_ != NULL);
|
||||
|
||||
@ -459,11 +444,8 @@ const char* CompilationUnit::ProcessAttribute(
|
||||
str);
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unhandled form type");
|
||||
}
|
||||
fprintf(stderr, "Unhandled form type");
|
||||
fprintf(stderr, "Unhandled form type\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -493,7 +475,7 @@ void CompilationUnit::ProcessDIEs() {
|
||||
else
|
||||
lengthstart += 4;
|
||||
|
||||
stack<uint64> die_stack;
|
||||
std::stack<uint64> die_stack;
|
||||
|
||||
while (dieptr < (lengthstart + header_.length)) {
|
||||
// We give the user the absolute offset from the beginning of
|
||||
@ -583,7 +565,7 @@ void LineInfo::ReadHeader() {
|
||||
header_.opcode_base = reader_->ReadOneByte(lineptr);
|
||||
lineptr += 1;
|
||||
|
||||
header_.std_opcode_lengths = new vector<unsigned char>;
|
||||
header_.std_opcode_lengths = new std::vector<unsigned char>;
|
||||
header_.std_opcode_lengths->resize(header_.opcode_base + 1);
|
||||
(*header_.std_opcode_lengths)[0] = 0;
|
||||
for (int i = 1; i < header_.opcode_base; i++) {
|
||||
@ -1096,7 +1078,7 @@ class CallFrameInfo::RuleMap {
|
||||
|
||||
private:
|
||||
// A map from register numbers to Rules.
|
||||
typedef map<int, Rule *> RuleByNumber;
|
||||
typedef std::map<int, Rule *> RuleByNumber;
|
||||
|
||||
// Remove all register rules and clear cfa_rule_.
|
||||
void Clear();
|
||||
@ -1341,7 +1323,7 @@ class CallFrameInfo::State {
|
||||
|
||||
// A stack of saved states, for DW_CFA_remember_state and
|
||||
// DW_CFA_restore_state.
|
||||
stack<RuleMap> saved_rules_;
|
||||
std::stack<RuleMap> saved_rules_;
|
||||
};
|
||||
|
||||
bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
|
||||
@ -1877,20 +1859,14 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
cie->version = reader_->ReadOneByte(cursor);
|
||||
cursor++;
|
||||
|
||||
// If we don't recognize the version, we can't parse any more fields
|
||||
// of the CIE. For DWARF CFI, we handle versions 1 through 3 (there
|
||||
// was never a version 2 of CFI data). For .eh_frame, we handle only
|
||||
// version 1.
|
||||
if (eh_frame_) {
|
||||
if (cie->version != 1) {
|
||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (cie->version < 1 || cie->version > 3) {
|
||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||
return false;
|
||||
}
|
||||
// If we don't recognize the version, we can't parse any more fields of the
|
||||
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
|
||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
|
||||
// the difference between those versions seems to be the same as for
|
||||
// .debug_frame.
|
||||
if (cie->version < 1 || cie->version > 3) {
|
||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *augmentation_start = cursor;
|
||||
@ -1898,7 +1874,8 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
memchr(augmentation_start, '\0', cie->end - augmentation_start);
|
||||
if (! augmentation_end) return ReportIncomplete(cie);
|
||||
cursor = static_cast<const char *>(augmentation_end);
|
||||
cie->augmentation = string(augmentation_start, cursor - augmentation_start);
|
||||
cie->augmentation = string(augmentation_start,
|
||||
cursor - augmentation_start);
|
||||
// Skip the terminating '\0'.
|
||||
cursor++;
|
||||
|
||||
|
@ -49,8 +49,7 @@
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/dwarf/types.h"
|
||||
|
||||
using namespace std;
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
struct LineStateMachine;
|
||||
@ -59,8 +58,9 @@ class LineInfoHandler;
|
||||
|
||||
// This maps from a string naming a section to a pair containing a
|
||||
// the data for the section, and the size of the section.
|
||||
typedef map<string, pair<const char*, uint64> > SectionMap;
|
||||
typedef list<pair<enum DwarfAttribute, enum DwarfForm> > AttributeList;
|
||||
typedef std::map<string, std::pair<const char*, uint64> > SectionMap;
|
||||
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
|
||||
AttributeList;
|
||||
typedef AttributeList::iterator AttributeIterator;
|
||||
typedef AttributeList::const_iterator ConstAttributeIterator;
|
||||
|
||||
@ -75,7 +75,7 @@ struct LineInfoHeader {
|
||||
uint8 opcode_base;
|
||||
// Use a pointer so that signalsafe_addr2line is able to use this structure
|
||||
// without heap allocation problem.
|
||||
vector<unsigned char> *std_opcode_lengths;
|
||||
std::vector<unsigned char> *std_opcode_lengths;
|
||||
};
|
||||
|
||||
class LineInfo {
|
||||
@ -313,7 +313,7 @@ class CompilationUnit {
|
||||
// Set of DWARF2/3 abbreviations for this compilation unit. Indexed
|
||||
// by abbreviation number, which means that abbrevs_[0] is not
|
||||
// valid.
|
||||
vector<Abbrev>* abbrevs_;
|
||||
std::vector<Abbrev>* abbrevs_;
|
||||
|
||||
// String section buffer and length, if we have a string section.
|
||||
// This is here to avoid doing a section lookup for strings in
|
||||
@ -394,6 +394,15 @@ class Dwarf2Handler {
|
||||
enum DwarfForm form,
|
||||
const string& data) { }
|
||||
|
||||
// Called when we have an attribute whose value is the 64-bit signature
|
||||
// of a type unit in the .debug_types section. OFFSET is the offset of
|
||||
// the DIE whose attribute we're reporting. ATTR and FORM are the
|
||||
// attribute's name and form. SIGNATURE is the type unit's signature.
|
||||
virtual void ProcessAttributeSignature(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signature) { }
|
||||
|
||||
// Called when finished processing the DIE at OFFSET.
|
||||
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
||||
// before ends of the previous DIE, as we process children before
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// The '.eh_frame' format, used by the Linux C++ ABI for exception
|
||||
@ -61,6 +62,7 @@ extern "C" {
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/cfi_assembler.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
using google_breakpad::CFISection;
|
||||
|
@ -39,14 +39,13 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/dwarf/functioninfo.h"
|
||||
|
||||
#include "common/dwarf/bytereader.h"
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
|
||||
vector<string>* dirs,
|
||||
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
|
||||
std::vector<string>* dirs,
|
||||
LineMap* linemap):linemap_(linemap),
|
||||
files_(files),
|
||||
dirs_(dirs) {
|
||||
@ -95,8 +94,10 @@ void CULineInfoHandler::DefineFile(const string& name,
|
||||
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
|
||||
uint32 line_num, uint32 column_num) {
|
||||
if (file_num < files_->size()) {
|
||||
linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(),
|
||||
line_num)));
|
||||
linemap_->insert(
|
||||
std::make_pair(address,
|
||||
std::make_pair(files_->at(file_num).name.c_str(),
|
||||
line_num)));
|
||||
|
||||
if(address < files_->at(file_num).lowpc) {
|
||||
files_->at(file_num).lowpc = address;
|
||||
@ -130,7 +131,8 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag,
|
||||
current_function_info_->name = "";
|
||||
current_function_info_->line = 0;
|
||||
current_function_info_->file = "";
|
||||
offset_to_funcinfo_->insert(make_pair(offset, current_function_info_));
|
||||
offset_to_funcinfo_->insert(std::make_pair(offset,
|
||||
current_function_info_));
|
||||
};
|
||||
// FALLTHROUGH
|
||||
case DW_TAG_compile_unit:
|
||||
@ -164,9 +166,9 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
|
||||
assert(iter != sections_.end());
|
||||
|
||||
// this should be a scoped_ptr but we dont' use boost :-(
|
||||
auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
|
||||
iter->second.second - data,
|
||||
reader_, linehandler_));
|
||||
std::auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
|
||||
iter->second.second - data,
|
||||
reader_, linehandler_));
|
||||
lireader->Start();
|
||||
} else if (current_function_info_) {
|
||||
switch (attr) {
|
||||
@ -219,8 +221,8 @@ void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
|
||||
|
||||
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
|
||||
if (current_function_info_ && current_function_info_->lowpc)
|
||||
address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc,
|
||||
current_function_info_));
|
||||
address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
|
||||
current_function_info_));
|
||||
}
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
|
||||
namespace dwarf2reader {
|
||||
@ -66,8 +67,8 @@ struct SourceFileInfo {
|
||||
uint64 lowpc;
|
||||
};
|
||||
|
||||
typedef map<uint64, FunctionInfo*> FunctionMap;
|
||||
typedef map<uint64, pair<string, uint32> > LineMap;
|
||||
typedef std::map<uint64, FunctionInfo*> FunctionMap;
|
||||
typedef std::map<uint64, std::pair<string, uint32> > LineMap;
|
||||
|
||||
// This class is a basic line info handler that fills in the dirs,
|
||||
// file, and linemap passed into it with the data produced from the
|
||||
@ -76,8 +77,8 @@ class CULineInfoHandler: public LineInfoHandler {
|
||||
public:
|
||||
|
||||
//
|
||||
CULineInfoHandler(vector<SourceFileInfo>* files,
|
||||
vector<string>* dirs,
|
||||
CULineInfoHandler(std::vector<SourceFileInfo>* files,
|
||||
std::vector<string>* dirs,
|
||||
LineMap* linemap);
|
||||
virtual ~CULineInfoHandler() { }
|
||||
|
||||
@ -102,14 +103,14 @@ class CULineInfoHandler: public LineInfoHandler {
|
||||
|
||||
private:
|
||||
LineMap* linemap_;
|
||||
vector<SourceFileInfo>* files_;
|
||||
vector<string>* dirs_;
|
||||
std::vector<SourceFileInfo>* files_;
|
||||
std::vector<string>* dirs_;
|
||||
};
|
||||
|
||||
class CUFunctionInfoHandler: public Dwarf2Handler {
|
||||
public:
|
||||
CUFunctionInfoHandler(vector<SourceFileInfo>* files,
|
||||
vector<string>* dirs,
|
||||
CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
|
||||
std::vector<string>* dirs,
|
||||
LineMap* linemap,
|
||||
FunctionMap* offset_to_funcinfo,
|
||||
FunctionMap* address_to_funcinfo,
|
||||
@ -172,8 +173,8 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
|
||||
virtual void EndDIE(uint64 offset);
|
||||
|
||||
private:
|
||||
vector<SourceFileInfo>* files_;
|
||||
vector<string>* dirs_;
|
||||
std::vector<SourceFileInfo>* files_;
|
||||
std::vector<string>* dirs_;
|
||||
LineMap* linemap_;
|
||||
FunctionMap* offset_to_funcinfo_;
|
||||
FunctionMap* address_to_funcinfo_;
|
||||
|
@ -33,6 +33,8 @@
|
||||
#ifndef _COMMON_DWARF_TYPES_H__
|
||||
#define _COMMON_DWARF_TYPES_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef signed char int8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
|
@ -84,12 +84,22 @@ vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
|
||||
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||
}
|
||||
|
||||
// Per ARM IHI 0040A, section 3.1
|
||||
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
|
||||
static const char *const names[] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
||||
"fps", "cpsr"
|
||||
"fps", "cpsr", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
|
||||
"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
|
||||
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
|
||||
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
|
||||
};
|
||||
|
||||
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||
@ -132,7 +142,8 @@ string DwarfCFIToModule::RegisterName(int i) {
|
||||
if (reg == return_address_)
|
||||
return ra_name_;
|
||||
|
||||
if (0 <= reg && reg < register_names_.size())
|
||||
// Ensure that a non-empty name exists for this register value.
|
||||
if (reg < register_names_.size() && !register_names_[reg].empty())
|
||||
return register_names_[reg];
|
||||
|
||||
reporter_->UnnamedRegister(entry_offset_, reg);
|
||||
|
@ -48,13 +48,13 @@
|
||||
|
||||
#include "common/module.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::CallFrameInfo;
|
||||
using google_breakpad::Module;
|
||||
using std::set;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// A class that accepts parsed call frame information from the DWARF
|
||||
|
@ -39,7 +39,9 @@
|
||||
#include "common/dwarf_cu_to_module.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cxxabi.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
@ -72,6 +74,9 @@ using std::vector;
|
||||
// A Specification holds information gathered from a declaration DIE that
|
||||
// we may need if we find a DW_AT_specification link pointing to it.
|
||||
struct DwarfCUToModule::Specification {
|
||||
// The qualified name that can be found by demangling DW_AT_MIPS_linkage_name.
|
||||
string qualified_name;
|
||||
|
||||
// The name of the enclosing scope, or the empty string if there is none.
|
||||
string enclosing_name;
|
||||
|
||||
@ -97,11 +102,19 @@ struct DwarfCUToModule::FilePrivate {
|
||||
// our data structures, insert it into this set, and then use the string
|
||||
// from the set.
|
||||
//
|
||||
// Because std::string uses reference counting internally, simply using
|
||||
// strings from this set, even if passed by value, assigned, or held
|
||||
// directly in structures and containers (map<string, ...>, for example),
|
||||
// causes those strings to share a single instance of each distinct piece
|
||||
// of text.
|
||||
// In some STL implementations, strings are reference-counted internally,
|
||||
// meaning that simply using strings from this set, even if passed by
|
||||
// value, assigned, or held directly in structures and containers
|
||||
// (map<string, ...>, for example), causes those strings to share a
|
||||
// single instance of each distinct piece of text. GNU's libstdc++ uses
|
||||
// reference counts, and I believe MSVC did as well, at some point.
|
||||
// However, C++ '11 implementations are moving away from reference
|
||||
// counting.
|
||||
//
|
||||
// In other implementations, string assignments copy the string's text,
|
||||
// so this set will actually hold yet another copy of the string (although
|
||||
// everything will still work). To improve memory consumption portably,
|
||||
// we will probably need to use pointers to strings held in this set.
|
||||
set<string> common_strings;
|
||||
|
||||
// A map from offsets of DIEs within the .debug_info section to
|
||||
@ -217,6 +230,14 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
DIEContext *parent_context_;
|
||||
uint64 offset_;
|
||||
|
||||
// Place the name in the global set of strings. Even though this looks
|
||||
// like a copy, all the major std::string implementations use reference
|
||||
// counting internally, so the effect is to have all the data structures
|
||||
// share copies of strings whenever possible.
|
||||
// FIXME: Should this return something like a string_ref to avoid the
|
||||
// assumption about how strings are implemented?
|
||||
string AddStringToPool(const string &str);
|
||||
|
||||
// If this DIE has a DW_AT_declaration attribute, this is its value.
|
||||
// It is false on DIEs with no DW_AT_declaration attribute.
|
||||
bool declaration_;
|
||||
@ -229,6 +250,11 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
// The value of the DW_AT_name attribute, or the empty string if the
|
||||
// DIE has no such attribute.
|
||||
string name_attribute_;
|
||||
|
||||
// The demangled value of the DW_AT_MIPS_linkage_name attribute, or the empty
|
||||
// string if the DIE has no such attribute or its content could not be
|
||||
// demangled.
|
||||
string demangled_name_;
|
||||
};
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
|
||||
@ -272,20 +298,26 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
|
||||
}
|
||||
}
|
||||
|
||||
string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
|
||||
pair<set<string>::iterator, bool> result =
|
||||
cu_context_->file_context->file_private->common_strings.insert(str);
|
||||
return *result.first;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_name: {
|
||||
// Place the name in our global set of strings, and then use the
|
||||
// string from the set. Even though the assignment looks like a copy,
|
||||
// all the major std::string implementations use reference counting
|
||||
// internally, so the effect is to have all our data structures share
|
||||
// copies of strings whenever possible.
|
||||
pair<set<string>::iterator, bool> result =
|
||||
cu_context_->file_context->file_private->common_strings.insert(data);
|
||||
name_attribute_ = *result.first;
|
||||
case dwarf2reader::DW_AT_name:
|
||||
name_attribute_ = AddStringToPool(data);
|
||||
break;
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name: {
|
||||
char* demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, NULL);
|
||||
if (demangled) {
|
||||
demangled_name_ = AddStringToPool(demangled);
|
||||
free(reinterpret_cast<void*>(demangled));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
@ -293,33 +325,54 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
}
|
||||
|
||||
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||
// Find our unqualified name. If the DIE has its own DW_AT_name
|
||||
// attribute, then use that; otherwise, check our specification.
|
||||
const string *unqualified_name;
|
||||
if (name_attribute_.empty() && specification_)
|
||||
unqualified_name = &specification_->unqualified_name;
|
||||
else
|
||||
unqualified_name = &name_attribute_;
|
||||
// Use the demangled name, if one is available. Demangled names are
|
||||
// preferable to those inferred from the DWARF structure because they
|
||||
// include argument types.
|
||||
const string *qualified_name = NULL;
|
||||
if (!demangled_name_.empty()) {
|
||||
// Found it is this DIE.
|
||||
qualified_name = &demangled_name_;
|
||||
} else if (specification_ && !specification_->qualified_name.empty()) {
|
||||
// Found it on the specification.
|
||||
qualified_name = &specification_->qualified_name;
|
||||
}
|
||||
|
||||
// Find the name of our enclosing context. If we have a
|
||||
// specification, it's the specification's enclosing context that
|
||||
// counts; otherwise, use this DIE's context.
|
||||
const string *unqualified_name;
|
||||
const string *enclosing_name;
|
||||
if (specification_)
|
||||
enclosing_name = &specification_->enclosing_name;
|
||||
else
|
||||
enclosing_name = &parent_context_->name;
|
||||
if (!qualified_name) {
|
||||
// Find our unqualified name. If the DIE has its own DW_AT_name
|
||||
// attribute, then use that; otherwise, check our specification.
|
||||
if (name_attribute_.empty() && specification_)
|
||||
unqualified_name = &specification_->unqualified_name;
|
||||
else
|
||||
unqualified_name = &name_attribute_;
|
||||
|
||||
// Find the name of our enclosing context. If we have a
|
||||
// specification, it's the specification's enclosing context that
|
||||
// counts; otherwise, use this DIE's context.
|
||||
if (specification_)
|
||||
enclosing_name = &specification_->enclosing_name;
|
||||
else
|
||||
enclosing_name = &parent_context_->name;
|
||||
}
|
||||
|
||||
// If this DIE was marked as a declaration, record its names in the
|
||||
// specification table.
|
||||
if (declaration_) {
|
||||
FileContext *file_context = cu_context_->file_context;
|
||||
Specification spec;
|
||||
spec.enclosing_name = *enclosing_name;
|
||||
spec.unqualified_name = *unqualified_name;
|
||||
if (qualified_name)
|
||||
spec.qualified_name = *qualified_name;
|
||||
else {
|
||||
spec.enclosing_name = *enclosing_name;
|
||||
spec.unqualified_name = *unqualified_name;
|
||||
}
|
||||
file_context->file_private->specifications[offset_] = spec;
|
||||
}
|
||||
|
||||
if (qualified_name)
|
||||
return *qualified_name;
|
||||
|
||||
// Combine the enclosing name and unqualified name to produce our
|
||||
// own fully-qualified name.
|
||||
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||
@ -439,7 +492,11 @@ void DwarfCUToModule::FuncHandler::Finish() {
|
||||
func->address = low_pc_;
|
||||
func->size = high_pc_ - low_pc_;
|
||||
func->parameter_size = 0;
|
||||
cu_context_->functions.push_back(func);
|
||||
if (func->address) {
|
||||
// If the function address is zero this is a sign that this function
|
||||
// description is just empty debug data and should just be discarded.
|
||||
cu_context_->functions.push_back(func);
|
||||
}
|
||||
} else if (inline_) {
|
||||
AbstractOrigin origin(name_);
|
||||
cu_context_->file_context->file_private->origins[offset_] = origin;
|
||||
@ -469,7 +526,7 @@ bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
const AttributeList &/*attrs*/) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, &child_context_, offset);
|
||||
@ -552,7 +609,7 @@ void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: function at offset 0x%" PRIx64 " has no name\n",
|
||||
fprintf(stderr, "%s: warning: function at offset 0x%llx has no name\n",
|
||||
filename_.c_str(), offset);
|
||||
}
|
||||
|
||||
@ -611,7 +668,7 @@ bool DwarfCUToModule::EndAttributes() {
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
const AttributeList &/*attrs*/) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, child_context_, offset);
|
||||
@ -922,7 +979,7 @@ bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList& attrs) {
|
||||
const AttributeList& /*attrs*/) {
|
||||
// We don't deal with partial compilation units (the only other tag
|
||||
// likely to be used for root DIE).
|
||||
return tag == dwarf2reader::DW_TAG_compile_unit;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
@ -58,9 +59,10 @@ using dwarf2reader::DwarfTag;
|
||||
// Populate a google_breakpad::Module with DWARF debugging information.
|
||||
//
|
||||
// An instance of this class can be provided as a handler to a
|
||||
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the
|
||||
// results of parsing to populate a google_breakpad::Module with
|
||||
// source file, function, and source line information.
|
||||
// dwarf2reader::DIEDispatcher, which can in turn be a handler for a
|
||||
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results
|
||||
// of parsing to populate a google_breakpad::Module with source file,
|
||||
// function, and source line information.
|
||||
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||
struct FilePrivate;
|
||||
public:
|
||||
|
@ -32,7 +32,12 @@
|
||||
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
|
||||
// See dwarf_line_to_module.h for details.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/dwarf_line_to_module.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// Trying to support Windows paths in a reasonable way adds a lot of
|
||||
// variations to test; it would be better to just put off dealing with
|
||||
@ -45,7 +50,8 @@ static bool PathIsAbsolute(const string &path) {
|
||||
|
||||
// If PATH is an absolute path, return PATH. If PATH is a relative path,
|
||||
// treat it as relative to BASE and return the combined path.
|
||||
static string ExpandPath(const string &path, const string &base) {
|
||||
static string ExpandPath(const string &path,
|
||||
const string &base) {
|
||||
if (PathIsAbsolute(path))
|
||||
return path;
|
||||
return base + "/" + path;
|
||||
@ -68,7 +74,7 @@ void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
||||
else if (file_num > highest_file_number_)
|
||||
highest_file_number_ = file_num;
|
||||
|
||||
std::string full_name;
|
||||
string full_name;
|
||||
if (dir_num != 0) {
|
||||
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
||||
if (directory_it != directories_.end()) {
|
||||
|
@ -38,8 +38,11 @@
|
||||
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/module.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
@ -127,8 +130,8 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
||||
|
||||
~DwarfLineToModule() { }
|
||||
|
||||
void DefineDir(const std::string &name, uint32 dir_num);
|
||||
void DefineFile(const std::string &name, int32 file_num,
|
||||
void DefineDir(const string &name, uint32 dir_num);
|
||||
void DefineFile(const string &name, int32 file_num,
|
||||
uint32 dir_num, uint64 mod_time,
|
||||
uint64 length);
|
||||
void AddLine(uint64 address, uint64 length,
|
||||
@ -136,7 +139,7 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
||||
|
||||
private:
|
||||
|
||||
typedef std::map<uint32, std::string> DirectoryTable;
|
||||
typedef std::map<uint32, string> DirectoryTable;
|
||||
typedef std::map<uint32, Module::File *> FileTable;
|
||||
|
||||
// The module we're contributing debugging info to. Owned by our
|
||||
|
2
3rdparty/google-breakpad/common/language.cc
vendored
2
3rdparty/google-breakpad/common/language.cc
vendored
@ -49,7 +49,7 @@ class CPPLanguage: public Language {
|
||||
}
|
||||
};
|
||||
|
||||
const CPPLanguage CPPLanguageSingleton;
|
||||
CPPLanguage CPPLanguageSingleton;
|
||||
|
||||
// Java language-specific operations.
|
||||
class JavaLanguage: public Language {
|
||||
|
8
3rdparty/google-breakpad/common/language.h
vendored
8
3rdparty/google-breakpad/common/language.h
vendored
@ -40,9 +40,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using std::string;
|
||||
namespace google_breakpad {
|
||||
|
||||
// An abstract base class for language-specific operations. We choose
|
||||
// an instance of a subclass of this when we find the CU's language.
|
||||
@ -50,6 +50,10 @@ using std::string;
|
||||
// language.
|
||||
class Language {
|
||||
public:
|
||||
// A base class destructor should be either public and virtual,
|
||||
// or protected and nonvirtual.
|
||||
virtual ~Language() {}
|
||||
|
||||
// Return true if this language has functions to which we can assign
|
||||
// line numbers. (Debugging info for assembly language, for example,
|
||||
// can have source location information, but does not have functions
|
||||
|
839
3rdparty/google-breakpad/common/linux/dump_symbols.cc
vendored
Normal file
839
3rdparty/google-breakpad/common/linux/dump_symbols.cc
vendored
Normal file
@ -0,0 +1,839 @@
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
|
||||
// Find all the debugging info in a file and dump it as a Breakpad symbol file.
|
||||
|
||||
#include "common/linux/dump_symbols.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/dwarf_cfi_to_module.h"
|
||||
#include "common/dwarf_cu_to_module.h"
|
||||
#include "common/dwarf_line_to_module.h"
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/elfutils-inl.h"
|
||||
#include "common/linux/elf_symbols_to_module.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/module.h"
|
||||
#include "common/stabs_reader.h"
|
||||
#include "common/stabs_to_module.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
using google_breakpad::DwarfCFIToModule;
|
||||
using google_breakpad::DwarfCUToModule;
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
using google_breakpad::ElfClass;
|
||||
using google_breakpad::ElfClass32;
|
||||
using google_breakpad::ElfClass64;
|
||||
using google_breakpad::FindElfSectionByName;
|
||||
using google_breakpad::GetOffset;
|
||||
using google_breakpad::IsValidElf;
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::StabsToModule;
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
// Wrapper class to make sure opened file is closed.
|
||||
//
|
||||
class FDWrapper {
|
||||
public:
|
||||
explicit FDWrapper(int fd) :
|
||||
fd_(fd) {}
|
||||
~FDWrapper() {
|
||||
if (fd_ != -1)
|
||||
close(fd_);
|
||||
}
|
||||
int get() {
|
||||
return fd_;
|
||||
}
|
||||
int release() {
|
||||
int fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
//
|
||||
// MmapWrapper
|
||||
//
|
||||
// Wrapper class to make sure mapped regions are unmapped.
|
||||
//
|
||||
class MmapWrapper {
|
||||
public:
|
||||
MmapWrapper() : is_set_(false) {}
|
||||
~MmapWrapper() {
|
||||
assert(is_set_);
|
||||
if (base_ != NULL) {
|
||||
assert(size_ > 0);
|
||||
munmap(base_, size_);
|
||||
}
|
||||
}
|
||||
void set(void *mapped_address, size_t mapped_size) {
|
||||
is_set_ = true;
|
||||
base_ = mapped_address;
|
||||
size_ = mapped_size;
|
||||
}
|
||||
void release() {
|
||||
assert(is_set_);
|
||||
base_ = NULL;
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_set_;
|
||||
void *base_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
// Find the preferred loading address of the binary.
|
||||
template<typename ElfClass>
|
||||
typename ElfClass::Addr GetLoadingAddress(
|
||||
const typename ElfClass::Phdr* program_headers,
|
||||
int nheader) {
|
||||
typedef typename ElfClass::Phdr Phdr;
|
||||
|
||||
for (int i = 0; i < nheader; ++i) {
|
||||
const Phdr& header = program_headers[i];
|
||||
// For executable, it is the PT_LOAD segment with offset to zero.
|
||||
if (header.p_type == PT_LOAD &&
|
||||
header.p_offset == 0)
|
||||
return header.p_vaddr;
|
||||
}
|
||||
// For other types of ELF, return 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool LoadStabs(const typename ElfClass::Ehdr* elf_header,
|
||||
const typename ElfClass::Shdr* stab_section,
|
||||
const typename ElfClass::Shdr* stabstr_section,
|
||||
const bool big_endian,
|
||||
Module* module) {
|
||||
// A callback object to handle data from the STABS reader.
|
||||
StabsToModule handler(module);
|
||||
// Find the addresses of the STABS data, and create a STABS reader object.
|
||||
// On Linux, STABS entries always have 32-bit values, regardless of the
|
||||
// address size of the architecture whose code they're describing, and
|
||||
// the strings are always "unitized".
|
||||
const uint8_t* stabs =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header, stab_section->sh_offset);
|
||||
const uint8_t* stabstr =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header, stabstr_section->sh_offset);
|
||||
google_breakpad::StabsReader reader(stabs, stab_section->sh_size,
|
||||
stabstr, stabstr_section->sh_size,
|
||||
big_endian, 4, true, &handler);
|
||||
// Read the STABS data, and do post-processing.
|
||||
if (!reader.Process())
|
||||
return false;
|
||||
handler.Finalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
// A line-to-module loader that accepts line number info parsed by
|
||||
// dwarf2reader::LineInfo and populates a Module and a line vector
|
||||
// with the results.
|
||||
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
||||
public:
|
||||
// Create a line-to-module converter using BYTE_READER.
|
||||
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||
: byte_reader_(byte_reader) { }
|
||||
void operator()(const char *program, uint64 length,
|
||||
Module *module, std::vector<Module::Line> *lines) {
|
||||
DwarfLineToModule handler(module, lines);
|
||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||
parser.Start();
|
||||
}
|
||||
private:
|
||||
dwarf2reader::ByteReader *byte_reader_;
|
||||
};
|
||||
|
||||
template<typename ElfClass>
|
||||
bool LoadDwarf(const string& dwarf_filename,
|
||||
const typename ElfClass::Ehdr* elf_header,
|
||||
const bool big_endian,
|
||||
Module* module) {
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
const dwarf2reader::Endianness endianness = big_endian ?
|
||||
dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
|
||||
// Construct a context for this file.
|
||||
DwarfCUToModule::FileContext file_context(dwarf_filename, module);
|
||||
|
||||
// Build a map of the ELF file's sections.
|
||||
const Shdr* sections =
|
||||
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||
int num_sections = elf_header->e_shnum;
|
||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||
for (int i = 0; i < num_sections; i++) {
|
||||
const Shdr* section = §ions[i];
|
||||
string name = GetOffset<ElfClass, char>(elf_header,
|
||||
section_names->sh_offset) +
|
||||
section->sh_name;
|
||||
const char* contents = GetOffset<ElfClass, char>(elf_header,
|
||||
section->sh_offset);
|
||||
uint64 length = section->sh_size;
|
||||
file_context.section_map[name] = std::make_pair(contents, length);
|
||||
}
|
||||
|
||||
// Parse all the compilation units in the .debug_info section.
|
||||
DumperLineToModule line_to_module(&byte_reader);
|
||||
std::pair<const char *, uint64> debug_info_section
|
||||
= file_context.section_map[".debug_info"];
|
||||
// This should never have been called if the file doesn't have a
|
||||
// .debug_info section.
|
||||
assert(debug_info_section.first);
|
||||
uint64 debug_info_length = debug_info_section.second;
|
||||
for (uint64 offset = 0; offset < debug_info_length;) {
|
||||
// Make a handler for the root DIE that populates MODULE with the
|
||||
// data that was found.
|
||||
DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
|
||||
DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter);
|
||||
// Make a Dwarf2Handler that drives the DIEHandler.
|
||||
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
|
||||
// Make a DWARF parser for the compilation unit at OFFSET.
|
||||
dwarf2reader::CompilationUnit reader(file_context.section_map,
|
||||
offset,
|
||||
&byte_reader,
|
||||
&die_dispatcher);
|
||||
// Process the entire compilation unit; get the offset of the next.
|
||||
offset += reader.Start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill REGISTER_NAMES with the register names appropriate to the
|
||||
// machine architecture given in HEADER, indexed by the register
|
||||
// numbers used in DWARF call frame information. Return true on
|
||||
// success, or false if HEADER's machine architecture is not
|
||||
// supported.
|
||||
template<typename ElfClass>
|
||||
bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
|
||||
std::vector<string>* register_names) {
|
||||
switch (elf_header->e_machine) {
|
||||
case EM_386:
|
||||
*register_names = DwarfCFIToModule::RegisterNames::I386();
|
||||
return true;
|
||||
case EM_ARM:
|
||||
*register_names = DwarfCFIToModule::RegisterNames::ARM();
|
||||
return true;
|
||||
case EM_X86_64:
|
||||
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool LoadDwarfCFI(const string& dwarf_filename,
|
||||
const typename ElfClass::Ehdr* elf_header,
|
||||
const char* section_name,
|
||||
const typename ElfClass::Shdr* section,
|
||||
const bool eh_frame,
|
||||
const typename ElfClass::Shdr* got_section,
|
||||
const typename ElfClass::Shdr* text_section,
|
||||
const bool big_endian,
|
||||
Module* module) {
|
||||
// Find the appropriate set of register names for this file's
|
||||
// architecture.
|
||||
std::vector<string> register_names;
|
||||
if (!DwarfCFIRegisterNames<ElfClass>(elf_header, ®ister_names)) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
|
||||
" cannot convert DWARF call frame information\n",
|
||||
dwarf_filename.c_str(), elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
const dwarf2reader::Endianness endianness = big_endian ?
|
||||
dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
|
||||
|
||||
// Find the call frame information and its size.
|
||||
const char* cfi =
|
||||
GetOffset<ElfClass, char>(elf_header, section->sh_offset);
|
||||
size_t cfi_size = section->sh_size;
|
||||
|
||||
// Plug together the parser, handler, and their entourages.
|
||||
DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
|
||||
DwarfCFIToModule handler(module, register_names, &module_reporter);
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
|
||||
byte_reader.SetAddressSize(ElfClass::kAddrSize);
|
||||
|
||||
// Provide the base addresses for .eh_frame encoded pointers, if
|
||||
// possible.
|
||||
byte_reader.SetCFIDataBase(section->sh_addr, cfi);
|
||||
if (got_section)
|
||||
byte_reader.SetDataBase(got_section->sh_addr);
|
||||
if (text_section)
|
||||
byte_reader.SetTextBase(text_section->sh_addr);
|
||||
|
||||
dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
|
||||
section_name);
|
||||
dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
|
||||
&byte_reader, &handler, &dwarf_reporter,
|
||||
eh_frame);
|
||||
parser.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
|
||||
void** elf_header) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
if (obj_fd < 0) {
|
||||
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FDWrapper obj_fd_wrapper(obj_fd);
|
||||
struct stat st;
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
|
||||
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
void *obj_base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||
if (obj_base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
map_wrapper->set(obj_base, st.st_size);
|
||||
*elf_header = obj_base;
|
||||
if (!IsValidElf(*elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the endianness of ELF_HEADER. If it's invalid, return false.
|
||||
template<typename ElfClass>
|
||||
bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
|
||||
bool* big_endian) {
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
|
||||
*big_endian = false;
|
||||
return true;
|
||||
}
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
|
||||
*big_endian = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
fprintf(stderr, "bad data encoding in ELF header: %d\n",
|
||||
elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||
// wrong, return an empty string.
|
||||
template<typename ElfClass>
|
||||
string ReadDebugLink(const char* debuglink,
|
||||
size_t debuglink_size,
|
||||
const string& obj_file,
|
||||
const string& debug_dir) {
|
||||
size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32.
|
||||
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes.
|
||||
|
||||
// Sanity check.
|
||||
if (debuglink_len != debuglink_size) {
|
||||
fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
|
||||
"%zx %zx\n", debuglink_len, debuglink_size);
|
||||
return "";
|
||||
}
|
||||
|
||||
string debuglink_path = debug_dir + "/" + debuglink;
|
||||
int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||
if (debuglink_fd < 0) {
|
||||
fprintf(stderr, "Failed to open debug ELF file '%s' for '%s': %s\n",
|
||||
debuglink_path.c_str(), obj_file.c_str(), strerror(errno));
|
||||
return "";
|
||||
}
|
||||
FDWrapper debuglink_fd_wrapper(debuglink_fd);
|
||||
// TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink
|
||||
// section.
|
||||
|
||||
return debuglink_path;
|
||||
}
|
||||
|
||||
//
|
||||
// LoadSymbolsInfo
|
||||
//
|
||||
// Holds the state between the two calls to LoadSymbols() in case it's necessary
|
||||
// to follow the .gnu_debuglink section and load debug information from a
|
||||
// different file.
|
||||
//
|
||||
template<typename ElfClass>
|
||||
class LoadSymbolsInfo {
|
||||
public:
|
||||
typedef typename ElfClass::Addr Addr;
|
||||
|
||||
explicit LoadSymbolsInfo(const string &dbg_dir) :
|
||||
debug_dir_(dbg_dir),
|
||||
has_loading_addr_(false) {}
|
||||
|
||||
// Keeps track of which sections have been loaded so sections don't
|
||||
// accidentally get loaded twice from two different files.
|
||||
void LoadedSection(const string §ion) {
|
||||
if (loaded_sections_.count(section) == 0) {
|
||||
loaded_sections_.insert(section);
|
||||
} else {
|
||||
fprintf(stderr, "Section %s has already been loaded.\n",
|
||||
section.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// The ELF file and linked debug file are expected to have the same preferred
|
||||
// loading address.
|
||||
void set_loading_addr(Addr addr, const string &filename) {
|
||||
if (!has_loading_addr_) {
|
||||
loading_addr_ = addr;
|
||||
loaded_file_ = filename;
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr != loading_addr_) {
|
||||
fprintf(stderr,
|
||||
"ELF file '%s' and debug ELF file '%s' "
|
||||
"have different load addresses.\n",
|
||||
loaded_file_.c_str(), filename.c_str());
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Setters and getters
|
||||
const string &debug_dir() const {
|
||||
return debug_dir_;
|
||||
}
|
||||
|
||||
string debuglink_file() const {
|
||||
return debuglink_file_;
|
||||
}
|
||||
void set_debuglink_file(string file) {
|
||||
debuglink_file_ = file;
|
||||
}
|
||||
|
||||
private:
|
||||
const string &debug_dir_; // Directory with the debug ELF file.
|
||||
|
||||
string debuglink_file_; // Full path to the debug ELF file.
|
||||
|
||||
bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
|
||||
|
||||
Addr loading_addr_; // Saves the preferred loading address from the
|
||||
// first call to LoadSymbols().
|
||||
|
||||
string loaded_file_; // Name of the file loaded from the first call to
|
||||
// LoadSymbols().
|
||||
|
||||
std::set<string> loaded_sections_; // Tracks the Loaded ELF sections
|
||||
// between calls to LoadSymbols().
|
||||
};
|
||||
|
||||
template<typename ElfClass>
|
||||
bool LoadSymbols(const string& obj_file,
|
||||
const bool big_endian,
|
||||
const typename ElfClass::Ehdr* elf_header,
|
||||
const bool read_gnu_debug_link,
|
||||
LoadSymbolsInfo<ElfClass>* info,
|
||||
Module* module) {
|
||||
typedef typename ElfClass::Addr Addr;
|
||||
typedef typename ElfClass::Phdr Phdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
Addr loading_addr = GetLoadingAddress<ElfClass>(
|
||||
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
|
||||
elf_header->e_phnum);
|
||||
module->SetLoadAddress(loading_addr);
|
||||
info->set_loading_addr(loading_addr, obj_file);
|
||||
|
||||
const Shdr* sections =
|
||||
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||
const char* names =
|
||||
GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
|
||||
const char *names_end = names + section_names->sh_size;
|
||||
bool found_debug_info_section = false;
|
||||
bool found_usable_info = false;
|
||||
|
||||
// Look for STABS debugging information, and load it if present.
|
||||
const Shdr* stab_section =
|
||||
FindElfSectionByName<ElfClass>(".stab", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (stab_section) {
|
||||
const Shdr* stabstr_section = stab_section->sh_link + sections;
|
||||
if (stabstr_section) {
|
||||
found_debug_info_section = true;
|
||||
found_usable_info = true;
|
||||
info->LoadedSection(".stab");
|
||||
if (!LoadStabs<ElfClass>(elf_header, stab_section, stabstr_section,
|
||||
big_endian, module)) {
|
||||
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
|
||||
" debugging information\n", obj_file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for DWARF debugging information, and load it if present.
|
||||
const Shdr* dwarf_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_section) {
|
||||
found_debug_info_section = true;
|
||||
found_usable_info = true;
|
||||
info->LoadedSection(".debug_info");
|
||||
if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian, module))
|
||||
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
|
||||
"DWARF debugging information\n", obj_file.c_str());
|
||||
}
|
||||
|
||||
// Dwarf Call Frame Information (CFI) is actually independent from
|
||||
// the other DWARF debugging information, and can be used alone.
|
||||
const Shdr* dwarf_cfi_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_cfi_section) {
|
||||
// Ignore the return value of this function; even without call frame
|
||||
// information, the other debugging information could be perfectly
|
||||
// useful.
|
||||
info->LoadedSection(".debug_frame");
|
||||
bool result =
|
||||
LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame",
|
||||
dwarf_cfi_section, false, 0, 0, big_endian,
|
||||
module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
|
||||
// Linux C++ exception handling information can also provide
|
||||
// unwinding data.
|
||||
const Shdr* eh_frame_section =
|
||||
FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (eh_frame_section) {
|
||||
// Pointers in .eh_frame data may be relative to the base addresses of
|
||||
// certain sections. Provide those sections if present.
|
||||
const Shdr* got_section =
|
||||
FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
const Shdr* text_section =
|
||||
FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
info->LoadedSection(".eh_frame");
|
||||
// As above, ignore the return value of this function.
|
||||
bool result =
|
||||
LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
|
||||
eh_frame_section, true,
|
||||
got_section, text_section, big_endian, module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
|
||||
if (!found_debug_info_section) {
|
||||
fprintf(stderr, "%s: file contains no debugging information"
|
||||
" (no \".stab\" or \".debug_info\" sections)\n",
|
||||
obj_file.c_str());
|
||||
|
||||
// Failed, but maybe there's a .gnu_debuglink section?
|
||||
if (read_gnu_debug_link) {
|
||||
const Shdr* gnu_debuglink_section
|
||||
= FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS,
|
||||
sections, names,
|
||||
names_end, elf_header->e_shnum);
|
||||
if (gnu_debuglink_section) {
|
||||
if (!info->debug_dir().empty()) {
|
||||
const char* debuglink_contents =
|
||||
GetOffset<ElfClass, char>(elf_header,
|
||||
gnu_debuglink_section->sh_offset);
|
||||
string debuglink_file
|
||||
= ReadDebugLink<ElfClass>(debuglink_contents,
|
||||
gnu_debuglink_section->sh_size,
|
||||
obj_file, info->debug_dir());
|
||||
info->set_debuglink_file(debuglink_file);
|
||||
} else {
|
||||
fprintf(stderr, ".gnu_debuglink section found in '%s', "
|
||||
"but no debug path specified.\n", obj_file.c_str());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
|
||||
obj_file.c_str());
|
||||
}
|
||||
} else {
|
||||
// The caller doesn't want to consult .gnu_debuglink.
|
||||
// See if there are export symbols available.
|
||||
const Shdr* dynsym_section =
|
||||
FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
const Shdr* dynstr_section =
|
||||
FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (dynsym_section && dynstr_section) {
|
||||
info->LoadedSection(".dynsym");
|
||||
|
||||
const uint8_t* dynsyms =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header, dynsym_section->sh_offset);
|
||||
const uint8_t* dynstrs =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header, dynstr_section->sh_offset);
|
||||
bool result =
|
||||
ELFSymbolsToModule(dynsyms,
|
||||
dynsym_section->sh_size,
|
||||
dynstrs,
|
||||
dynstr_section->sh_size,
|
||||
big_endian,
|
||||
ElfClass::kAddrSize,
|
||||
module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
|
||||
// Return true if some usable information was found, since
|
||||
// the caller doesn't want to use .gnu_debuglink.
|
||||
return found_usable_info;
|
||||
}
|
||||
|
||||
// No debug info was found, let the user try again with .gnu_debuglink
|
||||
// if present.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the breakpad symbol file identifier for the architecture of
|
||||
// ELF_HEADER.
|
||||
template<typename ElfClass>
|
||||
const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
|
||||
typedef typename ElfClass::Half Half;
|
||||
Half arch = elf_header->e_machine;
|
||||
switch (arch) {
|
||||
case EM_386: return "x86";
|
||||
case EM_ARM: return "arm";
|
||||
case EM_MIPS: return "mips";
|
||||
case EM_PPC64: return "ppc64";
|
||||
case EM_PPC: return "ppc";
|
||||
case EM_S390: return "s390";
|
||||
case EM_SPARC: return "sparc";
|
||||
case EM_SPARCV9: return "sparcv9";
|
||||
case EM_X86_64: return "x86_64";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the Elf file identifier in IDENTIFIER as a UUID with the
|
||||
// dashes removed.
|
||||
string FormatIdentifier(unsigned char identifier[16]) {
|
||||
char identifier_str[40];
|
||||
google_breakpad::FileID::ConvertIdentifierToString(
|
||||
identifier,
|
||||
identifier_str,
|
||||
sizeof(identifier_str));
|
||||
string id_no_dash;
|
||||
for (int i = 0; identifier_str[i] != '\0'; ++i)
|
||||
if (identifier_str[i] != '-')
|
||||
id_no_dash += identifier_str[i];
|
||||
// Add an extra "0" by the end. PDB files on Windows have an 'age'
|
||||
// number appended to the end of the file identifier; this isn't
|
||||
// really used or necessary on other platforms, but be consistent.
|
||||
id_no_dash += '0';
|
||||
return id_no_dash;
|
||||
}
|
||||
|
||||
// Return the non-directory portion of FILENAME: the portion after the
|
||||
// last slash, or the whole filename if there are no slashes.
|
||||
string BaseFileName(const string &filename) {
|
||||
// Lots of copies! basename's behavior is less than ideal.
|
||||
char *c_filename = strdup(filename.c_str());
|
||||
string base = basename(c_filename);
|
||||
free(c_filename);
|
||||
return base;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool WriteSymbolFileElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
const string& obj_filename,
|
||||
const string& debug_dir,
|
||||
bool cfi,
|
||||
std::ostream& sym_stream) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
unsigned char identifier[16];
|
||||
if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header,
|
||||
identifier)) {
|
||||
fprintf(stderr, "%s: unable to generate file identifier\n",
|
||||
obj_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *architecture = ElfArchitecture<ElfClass>(elf_header);
|
||||
if (!architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
obj_filename.c_str(), elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Figure out what endianness this file is.
|
||||
bool big_endian;
|
||||
if (!ElfEndianness<ElfClass>(elf_header, &big_endian))
|
||||
return false;
|
||||
|
||||
string name = BaseFileName(obj_filename);
|
||||
string os = "Linux";
|
||||
string id = FormatIdentifier(identifier);
|
||||
|
||||
LoadSymbolsInfo<ElfClass> info(debug_dir);
|
||||
Module module(name, os, architecture, id);
|
||||
if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
|
||||
!debug_dir.empty(), &info, &module)) {
|
||||
const string debuglink_file = info.debuglink_file();
|
||||
if (debuglink_file.empty())
|
||||
return false;
|
||||
|
||||
// Load debuglink ELF file.
|
||||
fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
|
||||
MmapWrapper debug_map_wrapper;
|
||||
Ehdr* debug_elf_header = NULL;
|
||||
if (!LoadELF(debuglink_file, &debug_map_wrapper,
|
||||
reinterpret_cast<void**>(&debug_elf_header)))
|
||||
return false;
|
||||
// Sanity checks to make sure everything matches up.
|
||||
const char *debug_architecture =
|
||||
ElfArchitecture<ElfClass>(debug_elf_header);
|
||||
if (!debug_architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
debuglink_file.c_str(), debug_elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
if (strcmp(architecture, debug_architecture)) {
|
||||
fprintf(stderr, "%s with ELF machine architecture %s does not match "
|
||||
"%s with ELF architecture %s\n",
|
||||
debuglink_file.c_str(), debug_architecture,
|
||||
obj_filename.c_str(), architecture);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug_big_endian;
|
||||
if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
|
||||
return false;
|
||||
if (debug_big_endian != big_endian) {
|
||||
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||
obj_filename.c_str(), debuglink_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
|
||||
debug_elf_header, false, &info, &module)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!module.Write(sym_stream, cfi))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Not explicitly exported, but not static so it can be used in unit tests.
|
||||
bool WriteSymbolFileInternal(const uint8_t* obj_file,
|
||||
const string& obj_filename,
|
||||
const string& debug_dir,
|
||||
bool cfi,
|
||||
std::ostream& sym_stream) {
|
||||
|
||||
if (!IsValidElf(obj_file)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
int elfclass = ElfClass(obj_file);
|
||||
if (elfclass == ELFCLASS32) {
|
||||
return WriteSymbolFileElfClass<ElfClass32>(
|
||||
reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dir,
|
||||
cfi, sym_stream);
|
||||
}
|
||||
if (elfclass == ELFCLASS64) {
|
||||
return WriteSymbolFileElfClass<ElfClass64>(
|
||||
reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dir,
|
||||
cfi, sym_stream);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WriteSymbolFile(const string &obj_file,
|
||||
const string &debug_dir,
|
||||
bool cfi,
|
||||
std::ostream &sym_stream) {
|
||||
MmapWrapper map_wrapper;
|
||||
void* elf_header = NULL;
|
||||
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||
return false;
|
||||
|
||||
return WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(elf_header),
|
||||
obj_file, debug_dir, cfi, sym_stream);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
58
3rdparty/google-breakpad/common/linux/dump_symbols.h
vendored
Normal file
58
3rdparty/google-breakpad/common/linux/dump_symbols.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// dump_symbols.h: Read debugging information from an ELF file, and write
|
||||
// it out as a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
#define COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and write it to SYM_STREAM in the Breakpad symbol
|
||||
// file format.
|
||||
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
|
||||
// then look for the debug file in DEBUG_DIR.
|
||||
// If CFI is set to false, then omit the CFI section.
|
||||
bool WriteSymbolFile(const string &obj_file,
|
||||
const string &debug_dir,
|
||||
bool cfi,
|
||||
std::ostream &sym_stream);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DUMP_SYMBOLS_H__
|
@ -44,4 +44,4 @@
|
||||
__eintr_result__;\
|
||||
})
|
||||
|
||||
#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_
|
||||
#endif // COMMON_LINUX_EINTR_WRAPPER_H_
|
||||
|
168
3rdparty/google-breakpad/common/linux/elf_symbols_to_module.cc
vendored
Normal file
168
3rdparty/google-breakpad/common/linux/elf_symbols_to_module.cc
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
#include "common/linux/elf_symbols_to_module.h"
|
||||
|
||||
#include <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ELFSymbolIterator {
|
||||
public:
|
||||
// The contents of an ELF symbol, adjusted for the host's endianness,
|
||||
// word size, and so on. Corresponds to the data in Elf32_Sym / Elf64_Sym.
|
||||
struct Symbol {
|
||||
// True if this iterator has reached the end of the symbol array. When
|
||||
// this is set, the other members of this structure are not valid.
|
||||
bool at_end;
|
||||
|
||||
// The number of this symbol within the list.
|
||||
size_t index;
|
||||
|
||||
// The current symbol's name offset. This is the offset within the
|
||||
// string table.
|
||||
size_t name_offset;
|
||||
|
||||
// The current symbol's value, size, info and shndx fields.
|
||||
uint64_t value;
|
||||
uint64_t size;
|
||||
unsigned char info;
|
||||
uint16_t shndx;
|
||||
};
|
||||
|
||||
// Create an ELFSymbolIterator walking the symbols in BUFFER. Treat the
|
||||
// symbols as big-endian if BIG_ENDIAN is true, as little-endian
|
||||
// otherwise. Assume each symbol has a 'value' field whose size is
|
||||
// VALUE_SIZE.
|
||||
//
|
||||
ELFSymbolIterator(const ByteBuffer *buffer, bool big_endian,
|
||||
size_t value_size)
|
||||
: value_size_(value_size), cursor_(buffer, big_endian) {
|
||||
// Actually, weird sizes could be handled just fine, but they're
|
||||
// probably mistakes --- expressed in bits, say.
|
||||
assert(value_size == 4 || value_size == 8);
|
||||
symbol_.index = 0;
|
||||
Fetch();
|
||||
}
|
||||
|
||||
// Move to the next symbol. This function's behavior is undefined if
|
||||
// at_end() is true when it is called.
|
||||
ELFSymbolIterator &operator++() { Fetch(); symbol_.index++; return *this; }
|
||||
|
||||
// Dereferencing this iterator produces a reference to an Symbol structure
|
||||
// that holds the current symbol's values. The symbol is owned by this
|
||||
// SymbolIterator, and will be invalidated at the next call to operator++.
|
||||
const Symbol &operator*() const { return symbol_; }
|
||||
const Symbol *operator->() const { return &symbol_; }
|
||||
|
||||
private:
|
||||
// Read the symbol at cursor_, and set symbol_ appropriately.
|
||||
void Fetch() {
|
||||
// Elf32_Sym and Elf64_Sym have different layouts.
|
||||
unsigned char other;
|
||||
if (value_size_ == 4) {
|
||||
// Elf32_Sym
|
||||
cursor_
|
||||
.Read(4, false, &symbol_.name_offset)
|
||||
.Read(4, false, &symbol_.value)
|
||||
.Read(4, false, &symbol_.size)
|
||||
.Read(1, false, &symbol_.info)
|
||||
.Read(1, false, &other)
|
||||
.Read(2, false, &symbol_.shndx);
|
||||
} else {
|
||||
// Elf64_Sym
|
||||
cursor_
|
||||
.Read(4, false, &symbol_.name_offset)
|
||||
.Read(1, false, &symbol_.info)
|
||||
.Read(1, false, &other)
|
||||
.Read(2, false, &symbol_.shndx)
|
||||
.Read(8, false, &symbol_.value)
|
||||
.Read(8, false, &symbol_.size);
|
||||
}
|
||||
symbol_.at_end = !cursor_;
|
||||
}
|
||||
|
||||
// The size of symbols' value field, in bytes.
|
||||
size_t value_size_;
|
||||
|
||||
// A byte cursor traversing buffer_.
|
||||
ByteCursor cursor_;
|
||||
|
||||
// Values for the symbol this iterator refers to.
|
||||
Symbol symbol_;
|
||||
};
|
||||
|
||||
const char *SymbolString(ptrdiff_t offset, ByteBuffer& strings) {
|
||||
if (offset < 0 || (size_t) offset >= strings.Size()) {
|
||||
// Return the null string.
|
||||
offset = 0;
|
||||
}
|
||||
return reinterpret_cast<const char *>(strings.start + offset);
|
||||
}
|
||||
|
||||
bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
size_t symtab_size,
|
||||
const uint8_t *string_section,
|
||||
size_t string_size,
|
||||
const bool big_endian,
|
||||
size_t value_size,
|
||||
Module *module) {
|
||||
ByteBuffer symbols(symtab_section, symtab_size);
|
||||
// Ensure that the string section is null-terminated.
|
||||
if (string_section[string_size - 1] != '\0') {
|
||||
const void* null_terminator = memrchr(string_section, '\0', string_size);
|
||||
string_size = reinterpret_cast<const uint8_t*>(null_terminator)
|
||||
- string_section;
|
||||
}
|
||||
ByteBuffer strings(string_section, string_size);
|
||||
|
||||
// The iterator walking the symbol table.
|
||||
ELFSymbolIterator iterator(&symbols, big_endian, value_size);
|
||||
|
||||
while(!iterator->at_end) {
|
||||
if (ELF32_ST_TYPE(iterator->info) == STT_FUNC &&
|
||||
iterator->shndx != SHN_UNDEF) {
|
||||
Module::Extern *ext = new Module::Extern;
|
||||
ext->name = SymbolString(iterator->name_offset, strings);
|
||||
ext->address = iterator->value;
|
||||
module->AddExtern(ext);
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
58
3rdparty/google-breakpad/common/linux/elf_symbols_to_module.h
vendored
Normal file
58
3rdparty/google-breakpad/common/linux/elf_symbols_to_module.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// elf_symbols_to_module.h: Exposes ELFSymbolsToModule, a function
|
||||
// for reading ELF symbol tables and inserting exported symbol names
|
||||
// into a google_breakpad::Module as Extern definitions.
|
||||
|
||||
#ifndef BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
||||
#define BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class Module;
|
||||
|
||||
bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
size_t symtab_size,
|
||||
const uint8_t *string_section,
|
||||
size_t string_size,
|
||||
const bool big_endian,
|
||||
size_t value_size,
|
||||
Module *module);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
74
3rdparty/google-breakpad/common/linux/elfutils-inl.h
vendored
Normal file
74
3rdparty/google-breakpad/common/linux/elfutils-inl.h
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2012, 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 COMMON_LINUX_ELFUTILS_INL_H__
|
||||
#define COMMON_LINUX_ELFUTILS_INL_H__
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "elfutils.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
template<typename ElfClass, typename T>
|
||||
const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
|
||||
typename ElfClass::Off offset) {
|
||||
return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
|
||||
offset);
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
const typename ElfClass::Shdr* FindElfSectionByName(
|
||||
const char* name,
|
||||
typename ElfClass::Word section_type,
|
||||
const typename ElfClass::Shdr* sections,
|
||||
const char* section_names,
|
||||
const char* names_end,
|
||||
int nsection) {
|
||||
assert(name != NULL);
|
||||
assert(sections != NULL);
|
||||
assert(nsection > 0);
|
||||
|
||||
int name_len = my_strlen(name);
|
||||
if (name_len == 0)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < nsection; ++i) {
|
||||
const char* section_name = section_names + sections[i].sh_name;
|
||||
if (sections[i].sh_type == section_type &&
|
||||
names_end - section_name >= name_len + 1 &&
|
||||
my_strcmp(name, section_name) == 0) {
|
||||
return sections + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_ELFUTILS_INL_H__
|
129
3rdparty/google-breakpad/common/linux/elfutils.cc
vendored
Normal file
129
3rdparty/google-breakpad/common/linux/elfutils.cc
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
#include "common/linux/elfutils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/elfutils-inl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename ElfClass>
|
||||
void FindElfClassSection(const char *elf_base,
|
||||
const char *section_name,
|
||||
typename ElfClass::Word section_type,
|
||||
const void **section_start,
|
||||
int *section_size) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
assert(elf_base);
|
||||
assert(section_start);
|
||||
assert(section_size);
|
||||
|
||||
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
|
||||
|
||||
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
|
||||
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
||||
|
||||
const Shdr* sections =
|
||||
GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
|
||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||
const char* names =
|
||||
GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
|
||||
const char *names_end = names + section_names->sh_size;
|
||||
|
||||
const Shdr* section =
|
||||
FindElfSectionByName<ElfClass>(section_name, section_type,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
|
||||
if (section != NULL && section->sh_size > 0) {
|
||||
*section_start = elf_base + section->sh_offset;
|
||||
*section_size = section->sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsValidElf(const void* elf_base) {
|
||||
return my_strncmp(reinterpret_cast<const char*>(elf_base),
|
||||
ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
|
||||
int ElfClass(const void* elf_base) {
|
||||
const ElfW(Ehdr)* elf_header =
|
||||
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
|
||||
|
||||
return elf_header->e_ident[EI_CLASS];
|
||||
}
|
||||
|
||||
bool FindElfSection(const void *elf_mapped_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
int *section_size,
|
||||
int *elfclass) {
|
||||
assert(elf_mapped_base);
|
||||
assert(section_start);
|
||||
assert(section_size);
|
||||
|
||||
*section_start = NULL;
|
||||
*section_size = 0;
|
||||
|
||||
if (!IsValidElf(elf_mapped_base))
|
||||
return false;
|
||||
|
||||
int cls = ElfClass(elf_mapped_base);
|
||||
if (elfclass) {
|
||||
*elfclass = cls;
|
||||
}
|
||||
|
||||
const char* elf_base =
|
||||
static_cast<const char*>(elf_mapped_base);
|
||||
|
||||
if (cls == ELFCLASS32) {
|
||||
FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
|
||||
section_start, section_size);
|
||||
return *section_start != NULL;
|
||||
} else if (cls == ELFCLASS64) {
|
||||
FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
|
||||
section_start, section_size);
|
||||
return *section_start != NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
107
3rdparty/google-breakpad/common/linux/elfutils.h
vendored
Normal file
107
3rdparty/google-breakpad/common/linux/elfutils.h
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
//
|
||||
// elfutils.h: Utilities for dealing with ELF files.
|
||||
//
|
||||
|
||||
#ifndef COMMON_LINUX_ELFUTILS_H__
|
||||
#define COMMON_LINUX_ELFUTILS_H__
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Traits classes so consumers can write templatized code to deal
|
||||
// with specific ELF bits.
|
||||
struct ElfClass32 {
|
||||
typedef Elf32_Addr Addr;
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
typedef Elf32_Nhdr Nhdr;
|
||||
typedef Elf32_Phdr Phdr;
|
||||
typedef Elf32_Shdr Shdr;
|
||||
typedef Elf32_Half Half;
|
||||
typedef Elf32_Off Off;
|
||||
typedef Elf32_Word Word;
|
||||
static const int kClass = ELFCLASS32;
|
||||
static const size_t kAddrSize = sizeof(Elf32_Addr);
|
||||
};
|
||||
|
||||
struct ElfClass64 {
|
||||
typedef Elf64_Addr Addr;
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
typedef Elf64_Nhdr Nhdr;
|
||||
typedef Elf64_Phdr Phdr;
|
||||
typedef Elf64_Shdr Shdr;
|
||||
typedef Elf64_Half Half;
|
||||
typedef Elf64_Off Off;
|
||||
typedef Elf64_Word Word;
|
||||
static const int kClass = ELFCLASS64;
|
||||
static const size_t kAddrSize = sizeof(Elf64_Addr);
|
||||
};
|
||||
|
||||
bool IsValidElf(const void* elf_header);
|
||||
int ElfClass(const void* elf_base);
|
||||
|
||||
// Attempt to find a section named |section_name| of type |section_type|
|
||||
// in the ELF binary data at |elf_mapped_base|. On success, returns true
|
||||
// and sets |*section_start| to point to the start of the section data,
|
||||
// and |*section_size| to the size of the section's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file class.
|
||||
bool FindElfSection(const void *elf_mapped_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
int *section_size,
|
||||
int *elfclass);
|
||||
|
||||
// Internal helper method, exposed for convenience for callers
|
||||
// that already have more info.
|
||||
template<typename ElfClass>
|
||||
const typename ElfClass::Shdr*
|
||||
FindElfSectionByName(const char* name,
|
||||
typename ElfClass::Word section_type,
|
||||
const typename ElfClass::Shdr* sections,
|
||||
const char* section_names,
|
||||
const char* names_end,
|
||||
int nsection);
|
||||
|
||||
// Convert an offset from an Elf header into a pointer to the mapped
|
||||
// address in the current process. Takes an extra template parameter
|
||||
// to specify the return type to avoid having to dynamic_cast the
|
||||
// result.
|
||||
template<typename ElfClass, typename T>
|
||||
const T*
|
||||
GetOffset(const typename ElfClass::Ehdr* elf_header,
|
||||
typename ElfClass::Off offset);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_ELFUTILS_H__
|
192
3rdparty/google-breakpad/common/linux/file_id.cc
vendored
192
3rdparty/google-breakpad/common/linux/file_id.cc
vendored
@ -36,149 +36,111 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include "client/linux/android_link.h"
|
||||
#else
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#ifndef NT_GNU_BUILD_ID
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
#endif
|
||||
|
||||
FileID::FileID(const char* path) {
|
||||
strncpy(path_, path, sizeof(path_));
|
||||
}
|
||||
|
||||
struct ElfClass32 {
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
typedef Elf32_Shdr Shdr;
|
||||
static const int kClass = ELFCLASS32;
|
||||
};
|
||||
|
||||
struct ElfClass64 {
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
typedef Elf64_Shdr Shdr;
|
||||
static const int kClass = ELFCLASS64;
|
||||
};
|
||||
|
||||
// These three functions are also used inside the crashed process, so be safe
|
||||
// These six functions are also used inside the crashed process, so be safe
|
||||
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
||||
|
||||
template<typename ElfClass>
|
||||
static void FindElfClassTextSection(const char *elf_base,
|
||||
const void **text_start,
|
||||
int *text_size) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
static bool ElfClassBuildIDNoteIdentifier(const void *section,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
typedef typename ElfClass::Nhdr Nhdr;
|
||||
|
||||
assert(elf_base);
|
||||
assert(text_start);
|
||||
assert(text_size);
|
||||
|
||||
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
|
||||
|
||||
const char* text_section_name = ".text";
|
||||
int name_len = my_strlen(text_section_name);
|
||||
|
||||
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
|
||||
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
||||
|
||||
const Shdr* sections =
|
||||
reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
|
||||
const Shdr* string_section = sections + elf_header->e_shstrndx;
|
||||
|
||||
const Shdr* text_section = NULL;
|
||||
for (int i = 0; i < elf_header->e_shnum; ++i) {
|
||||
if (sections[i].sh_type == SHT_PROGBITS) {
|
||||
const char* section_name = (char*)(elf_base +
|
||||
string_section->sh_offset +
|
||||
sections[i].sh_name);
|
||||
if (!my_strncmp(section_name, text_section_name, name_len)) {
|
||||
text_section = §ions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (text_section != NULL && text_section->sh_size > 0) {
|
||||
*text_start = elf_base + text_section->sh_offset;
|
||||
*text_size = text_section->sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
static bool FindElfTextSection(const void *elf_mapped_base,
|
||||
const void **text_start,
|
||||
int *text_size) {
|
||||
assert(elf_mapped_base);
|
||||
assert(text_start);
|
||||
assert(text_size);
|
||||
|
||||
const char* elf_base =
|
||||
static_cast<const char*>(elf_mapped_base);
|
||||
const ElfW(Ehdr)* elf_header =
|
||||
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
|
||||
if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
|
||||
return false;
|
||||
|
||||
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
FindElfClassTextSection<ElfClass32>(elf_base, text_start, text_size);
|
||||
} else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
FindElfClassTextSection<ElfClass64>(elf_base, text_start, text_size);
|
||||
} else {
|
||||
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
|
||||
if (note_header->n_type != NT_GNU_BUILD_ID ||
|
||||
note_header->n_descsz == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* build_id = reinterpret_cast<const char*>(section) +
|
||||
sizeof(Nhdr) + note_header->n_namesz;
|
||||
// Copy as many bits of the build ID as will fit
|
||||
// into the GUID space.
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
memcpy(identifier, build_id,
|
||||
std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool FileID::ElfFileIdentifierFromMappedFile(void* base,
|
||||
uint8_t identifier[kMDGUIDSize])
|
||||
{
|
||||
const void* text_section = NULL;
|
||||
int text_size = 0;
|
||||
bool success = false;
|
||||
if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
||||
const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
|
||||
while (ptr < ptr_end) {
|
||||
for (unsigned i = 0; i < kMDGUIDSize; i++)
|
||||
identifier[i] ^= ptr[i];
|
||||
ptr += kMDGUIDSize;
|
||||
}
|
||||
success = true;
|
||||
// Attempt to locate a .note.gnu.build-id section in an ELF binary
|
||||
// and copy as many bytes of it as will fit into |identifier|.
|
||||
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
void* note_section;
|
||||
int note_size, elfclass;
|
||||
if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
|
||||
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||
note_size == 0) {
|
||||
return false;
|
||||
}
|
||||
return success;
|
||||
|
||||
if (elfclass == ELFCLASS32) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
|
||||
} else if (elfclass == ELFCLASS64) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to locate the .text section of an ELF binary and generate
|
||||
// a simple hash by XORing the first page worth of bytes into |identifier|.
|
||||
static bool HashElfTextSection(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
void* text_section;
|
||||
int text_size;
|
||||
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
||||
(const void**)&text_section, &text_size, NULL) ||
|
||||
text_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
||||
const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
|
||||
while (ptr < ptr_end) {
|
||||
for (unsigned i = 0; i < kMDGUIDSize; i++)
|
||||
identifier[i] ^= ptr[i];
|
||||
ptr += kMDGUIDSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
// Look for a build id note first.
|
||||
if (FindElfBuildIDNote(base, identifier))
|
||||
return true;
|
||||
|
||||
// Fall back on hashing the first page of the text section.
|
||||
return HashElfTextSection(base, identifier);
|
||||
}
|
||||
|
||||
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
||||
int fd = open(path_, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
void* base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (base == MAP_FAILED)
|
||||
MemoryMappedFile mapped_file(path_);
|
||||
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||
return false;
|
||||
|
||||
bool success = ElfFileIdentifierFromMappedFile(base, identifier);
|
||||
munmap(base, st.st_size);
|
||||
return success;
|
||||
return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -49,14 +49,15 @@ class FileID {
|
||||
// Load the identifier for the elf file path specified in the constructor into
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
// The current implementation will XOR the first 4096 bytes of the
|
||||
// .text section to generate an identifier.
|
||||
// The current implementation will look for a .note.gnu.build-id
|
||||
// section and use that as the file id, otherwise it falls back to
|
||||
// XORing the first 4096 bytes of the .text section to generate an identifier.
|
||||
bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Load the identifier for the elf file mapped into memory at |base| into
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
static bool ElfFileIdentifierFromMappedFile(void* base,
|
||||
static bool ElfFileIdentifierFromMappedFile(const void* base,
|
||||
uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Convert the |identifier| data to a NULL terminated string. The string will
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
@ -45,10 +46,6 @@
|
||||
//
|
||||
class GUIDGenerator {
|
||||
public:
|
||||
GUIDGenerator() {
|
||||
srandom(time(NULL));
|
||||
}
|
||||
|
||||
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
|
||||
return ((u_int32_t) bytes[0]
|
||||
| ((u_int32_t) bytes[1] << 8)
|
||||
@ -63,7 +60,8 @@ class GUIDGenerator {
|
||||
bytes[3] = (n >> 24) & 0xff;
|
||||
}
|
||||
|
||||
bool CreateGUID(GUID *guid) const {
|
||||
static bool CreateGUID(GUID *guid) {
|
||||
InitOnce();
|
||||
guid->data1 = random();
|
||||
guid->data2 = (u_int16_t)(random());
|
||||
guid->data3 = (u_int16_t)(random());
|
||||
@ -71,13 +69,23 @@ class GUIDGenerator {
|
||||
UInt32ToBytes(&guid->data4[4], random());
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void InitOnce() {
|
||||
pthread_once(&once_control, &InitOnceImpl);
|
||||
}
|
||||
|
||||
static void InitOnceImpl() {
|
||||
srandom(time(NULL));
|
||||
}
|
||||
|
||||
static pthread_once_t once_control;
|
||||
};
|
||||
|
||||
// Guid generator.
|
||||
const GUIDGenerator kGuidGenerator;
|
||||
pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
|
||||
|
||||
bool CreateGUID(GUID *guid) {
|
||||
return kGuidGenerator.CreateGUID(guid);
|
||||
return GUIDGenerator::CreateGUID(guid);
|
||||
}
|
||||
|
||||
// Parse guid to string.
|
||||
|
40
3rdparty/google-breakpad/common/linux/ignore_ret.h
vendored
Normal file
40
3rdparty/google-breakpad/common/linux/ignore_ret.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2012 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 COMMON_LINUX_IGNORE_RET_H_
|
||||
#define COMMON_LINUX_IGNORE_RET_H_
|
||||
|
||||
// Some compilers are prone to warn about unused return values. In cases where
|
||||
// either a) the call cannot fail, or b) there is nothing that can be done when
|
||||
// the call fails, IGNORE_RET() can be used to mark the return code as ignored.
|
||||
// This avoids spurious compiler warnings.
|
||||
|
||||
#define IGNORE_RET(x) do { if (x); } while (0)
|
||||
|
||||
#endif // COMMON_LINUX_IGNORE_RET_H_
|
227
3rdparty/google-breakpad/common/linux/linux_libc_support.cc
vendored
Normal file
227
3rdparty/google-breakpad/common/linux/linux_libc_support.cc
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
// This source file provides replacements for libc functions that we need. If
|
||||
// we call the libc functions directly we risk crashing in the dynamic linker
|
||||
// as it tries to resolve uncached PLT entries.
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
size_t my_strlen(const char* s) {
|
||||
size_t len = 0;
|
||||
while (*s++) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
int my_strcmp(const char* a, const char* b) {
|
||||
for (;;) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
int my_strncmp(const char* a, const char* b, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse a non-negative integer.
|
||||
// result: (output) the resulting non-negative integer
|
||||
// s: a NUL terminated string
|
||||
// Return true iff successful.
|
||||
bool my_strtoui(int* result, const char* s) {
|
||||
if (*s == 0)
|
||||
return false;
|
||||
int r = 0;
|
||||
for (;; s++) {
|
||||
if (*s == 0)
|
||||
break;
|
||||
const int old_r = r;
|
||||
r *= 10;
|
||||
if (*s < '0' || *s > '9')
|
||||
return false;
|
||||
r += *s - '0';
|
||||
if (r < old_r)
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the length of the given unsigned integer when expressed in base 10.
|
||||
unsigned my_uint_len(uintmax_t i) {
|
||||
if (!i)
|
||||
return 1;
|
||||
|
||||
int len = 0;
|
||||
while (i) {
|
||||
len++;
|
||||
i /= 10;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Convert an unsigned integer to a string
|
||||
// output: (output) the resulting string is written here. This buffer must be
|
||||
// large enough to hold the resulting string. Call |my_uint_len| to get the
|
||||
// required length.
|
||||
// i: the unsigned integer to serialise.
|
||||
// i_len: the length of the integer in base 10 (see |my_uint_len|).
|
||||
void my_uitos(char* output, uintmax_t i, unsigned i_len) {
|
||||
for (unsigned index = i_len; index; --index, i /= 10)
|
||||
output[index - 1] = '0' + (i % 10);
|
||||
}
|
||||
|
||||
const char* my_strchr(const char* haystack, char needle) {
|
||||
while (*haystack && *haystack != needle)
|
||||
haystack++;
|
||||
if (*haystack == needle)
|
||||
return haystack;
|
||||
return (const char*) 0;
|
||||
}
|
||||
|
||||
const char* my_strrchr(const char* haystack, char needle) {
|
||||
const char* ret = NULL;
|
||||
while (*haystack) {
|
||||
if (*haystack == needle)
|
||||
ret = haystack;
|
||||
haystack++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Read a hex value
|
||||
// result: (output) the resulting value
|
||||
// s: a string
|
||||
// Returns a pointer to the first invalid charactor.
|
||||
const char* my_read_hex_ptr(uintptr_t* result, const char* s) {
|
||||
uintptr_t r = 0;
|
||||
|
||||
for (;; ++s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
r <<= 4;
|
||||
r += *s - '0';
|
||||
} else if (*s >= 'a' && *s <= 'f') {
|
||||
r <<= 4;
|
||||
r += (*s - 'a') + 10;
|
||||
} else if (*s >= 'A' && *s <= 'F') {
|
||||
r <<= 4;
|
||||
r += (*s - 'A') + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*result = r;
|
||||
return s;
|
||||
}
|
||||
|
||||
const char* my_read_decimal_ptr(uintptr_t* result, const char* s) {
|
||||
uintptr_t r = 0;
|
||||
|
||||
for (;; ++s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
r *= 10;
|
||||
r += *s - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*result = r;
|
||||
return s;
|
||||
}
|
||||
|
||||
void my_memset(void* ip, char c, size_t len) {
|
||||
char* p = (char *) ip;
|
||||
while (len--)
|
||||
*p++ = c;
|
||||
}
|
||||
|
||||
size_t my_strlcpy(char* s1, const char* s2, size_t len) {
|
||||
size_t pos1 = 0;
|
||||
size_t pos2 = 0;
|
||||
|
||||
while (s2[pos2] != '\0') {
|
||||
if (pos1 + 1 < len) {
|
||||
s1[pos1] = s2[pos2];
|
||||
pos1++;
|
||||
}
|
||||
pos2++;
|
||||
}
|
||||
if (len > 0)
|
||||
s1[pos1] = '\0';
|
||||
|
||||
return pos2;
|
||||
}
|
||||
|
||||
size_t my_strlcat(char* s1, const char* s2, size_t len) {
|
||||
size_t pos1 = 0;
|
||||
|
||||
while (pos1 < len && s1[pos1] != '\0')
|
||||
pos1++;
|
||||
|
||||
if (pos1 == len)
|
||||
return pos1;
|
||||
|
||||
return pos1 + my_strlcpy(s1 + pos1, s2, len - pos1);
|
||||
}
|
||||
|
||||
int my_isspace(int ch) {
|
||||
// Matches the C locale.
|
||||
const char spaces[] = " \t\f\n\r\t\v";
|
||||
for (size_t i = 0; i < sizeof(spaces); i++) {
|
||||
if (ch == spaces[i])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // extern "C"
|
@ -40,138 +40,54 @@
|
||||
|
||||
extern "C" {
|
||||
|
||||
static inline size_t
|
||||
my_strlen(const char* s) {
|
||||
size_t len = 0;
|
||||
while (*s++) len++;
|
||||
return len;
|
||||
}
|
||||
extern size_t my_strlen(const char* s);
|
||||
|
||||
static inline int
|
||||
my_strcmp(const char* a, const char* b) {
|
||||
for (;;) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
extern int my_strcmp(const char* a, const char* b);
|
||||
|
||||
static inline int
|
||||
my_strncmp(const char* a, const char* b, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
extern int my_strncmp(const char* a, const char* b, size_t len);
|
||||
|
||||
// Parse a non-negative integer.
|
||||
// result: (output) the resulting non-negative integer
|
||||
// s: a NUL terminated string
|
||||
// Return true iff successful.
|
||||
static inline bool
|
||||
my_strtoui(int* result, const char* s) {
|
||||
if (*s == 0)
|
||||
return false;
|
||||
int r = 0;
|
||||
for (;; s++) {
|
||||
if (*s == 0)
|
||||
break;
|
||||
const int old_r = r;
|
||||
r *= 10;
|
||||
if (*s < '0' || *s > '9')
|
||||
return false;
|
||||
r += *s - '0';
|
||||
if (r < old_r)
|
||||
return false;
|
||||
}
|
||||
extern bool my_strtoui(int* result, const char* s);
|
||||
|
||||
*result = r;
|
||||
return true;
|
||||
}
|
||||
// Return the length of the given unsigned integer when expressed in base 10.
|
||||
extern unsigned my_uint_len(uintmax_t i);
|
||||
|
||||
// Return the length of the given, non-negative integer when expressed in base
|
||||
// 10.
|
||||
static inline unsigned
|
||||
my_int_len(int i) {
|
||||
if (!i)
|
||||
return 1;
|
||||
|
||||
int len = 0;
|
||||
while (i) {
|
||||
len++;
|
||||
i /= 10;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Convert a non-negative integer to a string
|
||||
// Convert an unsigned integer to a string
|
||||
// output: (output) the resulting string is written here. This buffer must be
|
||||
// large enough to hold the resulting string. Call |my_int_len| to get the
|
||||
// large enough to hold the resulting string. Call |my_uint_len| to get the
|
||||
// required length.
|
||||
// i: the non-negative integer to serialise.
|
||||
// i_len: the length of the integer in base 10 (see |my_int_len|).
|
||||
static inline void
|
||||
my_itos(char* output, int i, unsigned i_len) {
|
||||
for (unsigned index = i_len; index; --index, i /= 10)
|
||||
output[index - 1] = '0' + (i % 10);
|
||||
}
|
||||
// i: the unsigned integer to serialise.
|
||||
// i_len: the length of the integer in base 10 (see |my_uint_len|).
|
||||
extern void my_uitos(char* output, uintmax_t i, unsigned i_len);
|
||||
|
||||
static inline const char*
|
||||
my_strchr(const char* haystack, char needle) {
|
||||
while (*haystack && *haystack != needle)
|
||||
haystack++;
|
||||
if (*haystack == needle)
|
||||
return haystack;
|
||||
return (const char*) 0;
|
||||
}
|
||||
extern const char* my_strchr(const char* haystack, char needle);
|
||||
|
||||
extern const char* my_strrchr(const char* haystack, char needle);
|
||||
|
||||
// Read a hex value
|
||||
// result: (output) the resulting value
|
||||
// s: a string
|
||||
// Returns a pointer to the first invalid charactor.
|
||||
static inline const char*
|
||||
my_read_hex_ptr(uintptr_t* result, const char* s) {
|
||||
uintptr_t r = 0;
|
||||
extern const char* my_read_hex_ptr(uintptr_t* result, const char* s);
|
||||
|
||||
for (;; ++s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
r <<= 4;
|
||||
r += *s - '0';
|
||||
} else if (*s >= 'a' && *s <= 'f') {
|
||||
r <<= 4;
|
||||
r += (*s - 'a') + 10;
|
||||
} else if (*s >= 'A' && *s <= 'F') {
|
||||
r <<= 4;
|
||||
r += (*s - 'A') + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s);
|
||||
|
||||
*result = r;
|
||||
return s;
|
||||
}
|
||||
extern void my_memset(void* ip, char c, size_t len);
|
||||
|
||||
static inline void
|
||||
my_memset(void* ip, char c, size_t len) {
|
||||
char* p = (char *) ip;
|
||||
while (len--)
|
||||
*p++ = c;
|
||||
}
|
||||
// The following are considered safe to use in a compromised environment.
|
||||
// Besides, this gives the compiler an opportunity to optimize their calls.
|
||||
#define my_memcpy memcpy
|
||||
#define my_memmove memmove
|
||||
#define my_memcmp memcmp
|
||||
|
||||
extern size_t my_strlcpy(char* s1, const char* s2, size_t len);
|
||||
|
||||
extern size_t my_strlcat(char* s1, const char* s2, size_t len);
|
||||
|
||||
extern int my_isspace(int ch);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
|
105
3rdparty/google-breakpad/common/linux/memory_mapped_file.cc
vendored
Normal file
105
3rdparty/google-breakpad/common/linux/memory_mapped_file.cc
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile.
|
||||
// See memory_mapped_file.h for details.
|
||||
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/memory_range.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
MemoryMappedFile::MemoryMappedFile() {}
|
||||
|
||||
MemoryMappedFile::MemoryMappedFile(const char* path) {
|
||||
Map(path);
|
||||
}
|
||||
|
||||
MemoryMappedFile::~MemoryMappedFile() {
|
||||
Unmap();
|
||||
}
|
||||
|
||||
bool MemoryMappedFile::Map(const char* path) {
|
||||
Unmap();
|
||||
|
||||
int fd = sys_open(path, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
struct kernel_stat st;
|
||||
if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
|
||||
#else
|
||||
struct kernel_stat64 st;
|
||||
if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) {
|
||||
#endif
|
||||
sys_close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the file size is zero, simply use an empty MemoryRange and return
|
||||
// true. Don't bother to call mmap() even though mmap() can handle an
|
||||
// empty file on some platforms.
|
||||
if (st.st_size == 0) {
|
||||
sys_close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__)
|
||||
void* data = sys_mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
#else
|
||||
void* data = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
#endif
|
||||
sys_close(fd);
|
||||
if (data == MAP_FAILED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
content_.Set(data, st.st_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MemoryMappedFile::Unmap() {
|
||||
if (content_.data()) {
|
||||
sys_munmap(const_cast<u_int8_t*>(content_.data()), content_.length());
|
||||
content_.Set(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
86
3rdparty/google-breakpad/common/linux/memory_mapped_file.h
vendored
Normal file
86
3rdparty/google-breakpad/common/linux/memory_mapped_file.h
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile
|
||||
// class, which maps a file into memory for read-only access.
|
||||
|
||||
#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_
|
||||
#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_
|
||||
|
||||
#include "common/basictypes.h"
|
||||
#include "common/memory_range.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// A utility class for mapping a file into memory for read-only access of
|
||||
// the file content. Its implementation avoids calling into libc functions
|
||||
// by directly making system calls for open, close, mmap, and munmap.
|
||||
class MemoryMappedFile {
|
||||
public:
|
||||
MemoryMappedFile();
|
||||
|
||||
// Constructor that calls Map() to map a file at |path| into memory.
|
||||
// If Map() fails, the object behaves as if it is default constructed.
|
||||
explicit MemoryMappedFile(const char* path);
|
||||
|
||||
~MemoryMappedFile();
|
||||
|
||||
// Maps a file at |path| into memory, which can then be accessed via
|
||||
// content() as a MemoryRange object or via data(), and returns true on
|
||||
// success. Mapping an empty file will succeed but with data() and size()
|
||||
// returning NULL and 0, respectively. An existing mapping is unmapped
|
||||
// before a new mapping is created.
|
||||
bool Map(const char* path);
|
||||
|
||||
// Unmaps the memory for the mapped file. It's a no-op if no file is
|
||||
// mapped.
|
||||
void Unmap();
|
||||
|
||||
// Returns a MemoryRange object that covers the memory for the mapped
|
||||
// file. The MemoryRange object is empty if no file is mapped.
|
||||
const MemoryRange& content() const { return content_; }
|
||||
|
||||
// Returns a pointer to the beginning of the memory for the mapped file.
|
||||
// or NULL if no file is mapped or the mapped file is empty.
|
||||
const void* data() const { return content_.data(); }
|
||||
|
||||
// Returns the size in bytes of the mapped file, or zero if no file
|
||||
// is mapped.
|
||||
size_t size() const { return content_.length(); }
|
||||
|
||||
private:
|
||||
// Mapped file content as a MemoryRange object.
|
||||
MemoryRange content_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_
|
53
3rdparty/google-breakpad/common/linux/safe_readlink.cc
vendored
Normal file
53
3rdparty/google-breakpad/common/linux/safe_readlink.cc
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// safe_readlink.cc: Implement google_breakpad::SafeReadLink.
|
||||
// See safe_readlink.h for details.
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) {
|
||||
// sys_readlink() does not add a NULL byte to |buffer|. In order to return
|
||||
// a NULL-terminated string in |buffer|, |buffer_size| should be at least
|
||||
// one byte longer than the expected path length. Also, sys_readlink()
|
||||
// returns the actual path length on success, which does not count the
|
||||
// NULL byte, so |result_size| should be less than |buffer_size|.
|
||||
ssize_t result_size = sys_readlink(path, buffer, buffer_size);
|
||||
if (result_size >= 0 && static_cast<size_t>(result_size) < buffer_size) {
|
||||
buffer[result_size] = '\0';
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
65
3rdparty/google-breakpad/common/linux/safe_readlink.h
vendored
Normal file
65
3rdparty/google-breakpad/common/linux/safe_readlink.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2011, 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.
|
||||
|
||||
// safe_readlink.h: Define the google_breakpad::SafeReadLink function,
|
||||
// which wraps sys_readlink and gurantees the result is NULL-terminated.
|
||||
|
||||
#ifndef COMMON_LINUX_SAFE_READLINK_H_
|
||||
#define COMMON_LINUX_SAFE_READLINK_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// This function wraps sys_readlink() and performs the same functionalty,
|
||||
// but guarantees |buffer| is NULL-terminated if sys_readlink() returns
|
||||
// no error. It takes the same arguments as sys_readlink(), but unlike
|
||||
// sys_readlink(), it returns true on success.
|
||||
//
|
||||
// |buffer_size| specifies the size of |buffer| in bytes. As this function
|
||||
// always NULL-terminates |buffer| on success, |buffer_size| should be
|
||||
// at least one byte longer than the expected path length (e.g. PATH_MAX,
|
||||
// which is typically defined as the maximum length of a path name
|
||||
// including the NULL byte).
|
||||
//
|
||||
// The implementation of this function calls sys_readlink() instead of
|
||||
// readlink(), it can thus be used in the context where calling to libc
|
||||
// functions is discouraged.
|
||||
bool SafeReadLink(const char* path, char* buffer, size_t buffer_size);
|
||||
|
||||
// Same as the three-argument version of SafeReadLink() but deduces the
|
||||
// size of |buffer| if it is a char array of known size.
|
||||
template <size_t N>
|
||||
bool SafeReadLink(const char* path, char (&buffer)[N]) {
|
||||
return SafeReadLink(path, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_SAFE_READLINK_H_
|
@ -29,8 +29,6 @@
|
||||
|
||||
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
|
||||
SDKROOT = macosx10.5
|
||||
SDKROOT[arch=i386] = macosx10.4
|
||||
SDKROOT[arch=ppc] = macosx10.4
|
||||
|
||||
GCC_VERSION = 4.2
|
||||
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0
|
||||
|
265
3rdparty/google-breakpad/common/mac/GTMDefines.h
vendored
265
3rdparty/google-breakpad/common/mac/GTMDefines.h
vendored
@ -21,6 +21,14 @@
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#include <Foundation/NSObjCRuntime.h>
|
||||
#endif // __OBJC__
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include <Availability.h>
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
|
||||
#ifndef MAC_OS_X_VERSION_10_5
|
||||
#define MAC_OS_X_VERSION_10_5 1050
|
||||
@ -28,6 +36,29 @@
|
||||
#ifndef MAC_OS_X_VERSION_10_6
|
||||
#define MAC_OS_X_VERSION_10_6 1060
|
||||
#endif
|
||||
#ifndef MAC_OS_X_VERSION_10_7
|
||||
#define MAC_OS_X_VERSION_10_7 1070
|
||||
#endif
|
||||
|
||||
// Not all __IPHONE_X macros defined in past SDKs
|
||||
#ifndef __IPHONE_3_0
|
||||
#define __IPHONE_3_0 30000
|
||||
#endif
|
||||
#ifndef __IPHONE_3_1
|
||||
#define __IPHONE_3_1 30100
|
||||
#endif
|
||||
#ifndef __IPHONE_3_2
|
||||
#define __IPHONE_3_2 30200
|
||||
#endif
|
||||
#ifndef __IPHONE_4_0
|
||||
#define __IPHONE_4_0 40000
|
||||
#endif
|
||||
#ifndef __IPHONE_4_3
|
||||
#define __IPHONE_4_3 40300
|
||||
#endif
|
||||
#ifndef __IPHONE_5_0
|
||||
#define __IPHONE_5_0 50000
|
||||
#endif
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CPP symbols that can be overridden in a prefix to control how the toolbox
|
||||
@ -47,7 +78,7 @@
|
||||
// a few different actual definitions, so we're based off of the foundation
|
||||
// one.
|
||||
#if !defined(GTM_INLINE)
|
||||
#if defined (__GNUC__) && (__GNUC__ == 4)
|
||||
#if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
|
||||
#define GTM_INLINE static __inline__ __attribute__((always_inline))
|
||||
#else
|
||||
#define GTM_INLINE static __inline__
|
||||
@ -59,8 +90,12 @@
|
||||
#if !defined (GTM_EXTERN)
|
||||
#if defined __cplusplus
|
||||
#define GTM_EXTERN extern "C"
|
||||
#define GTM_EXTERN_C_BEGIN extern "C" {
|
||||
#define GTM_EXTERN_C_END }
|
||||
#else
|
||||
#define GTM_EXTERN extern
|
||||
#define GTM_EXTERN_C_BEGIN
|
||||
#define GTM_EXTERN_C_END
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -70,6 +105,12 @@
|
||||
#define GTM_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
// Give ourselves a consistent way of declaring something as unused. This
|
||||
// doesn't use __unused because that is only supported in gcc 4.2 and greater.
|
||||
#if !defined (GTM_UNUSED)
|
||||
#define GTM_UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
// _GTMDevLog & _GTMDevAssert
|
||||
//
|
||||
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
|
||||
@ -100,11 +141,6 @@
|
||||
|
||||
#endif // _GTMDevLog
|
||||
|
||||
// Declared here so that it can easily be used for logging tracking if
|
||||
// necessary. See GTMUnitTestDevLog.h for details.
|
||||
@class NSString;
|
||||
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
|
||||
#ifndef _GTMDevAssert
|
||||
// we directly invoke the NSAssert handler so we can pass on the varargs
|
||||
// (NSAssert doesn't have a macro we can use that takes varargs)
|
||||
@ -145,28 +181,6 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
|
||||
#endif // _GTMCompileAssert
|
||||
|
||||
// Macro to allow fast enumeration when building for 10.5 or later, and
|
||||
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
|
||||
// does keys, so pick the right thing, nothing is done on the FastEnumeration
|
||||
// side to be sure you're getting what you wanted.
|
||||
#ifndef GTM_FOREACH_OBJECT
|
||||
#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (element in collection)
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (element in collection)
|
||||
#else
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// CPP symbols defined based on the project settings so the GTM code has
|
||||
// simple things to test against w/o scattering the knowledge of project
|
||||
@ -183,11 +197,26 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
#else
|
||||
#define GTM_IPHONE_DEVICE 1
|
||||
#endif // TARGET_IPHONE_SIMULATOR
|
||||
// By default, GTM has provided it's own unittesting support, define this
|
||||
// to use the support provided by Xcode, especially for the Xcode4 support
|
||||
// for unittesting.
|
||||
#ifndef GTM_IPHONE_USE_SENTEST
|
||||
#define GTM_IPHONE_USE_SENTEST 0
|
||||
#endif
|
||||
#else
|
||||
// For MacOS specific stuff
|
||||
#define GTM_MACOS_SDK 1
|
||||
#endif
|
||||
|
||||
// Some of our own availability macros
|
||||
#if GTM_MACOS_SDK
|
||||
#define GTM_AVAILABLE_ONLY_ON_IPHONE UNAVAILABLE_ATTRIBUTE
|
||||
#define GTM_AVAILABLE_ONLY_ON_MACOS
|
||||
#else
|
||||
#define GTM_AVAILABLE_ONLY_ON_IPHONE
|
||||
#define GTM_AVAILABLE_ONLY_ON_MACOS UNAVAILABLE_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
// Provide a symbol to include/exclude extra code for GC support. (This mainly
|
||||
// just controls the inclusion of finalize methods).
|
||||
#ifndef GTM_SUPPORT_GC
|
||||
@ -197,7 +226,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
#else
|
||||
// We can't find a symbol to tell if GC is supported/required, so best we
|
||||
// do on Mac targets is include it if we're on 10.5 or later.
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
|
||||
#define GTM_SUPPORT_GC 0
|
||||
#else
|
||||
#define GTM_SUPPORT_GC 1
|
||||
@ -207,7 +236,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
|
||||
// To simplify support for 64bit (and Leopard in general), we provide the type
|
||||
// defines for non Leopard SDKs
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
// NSInteger/NSUInteger and Max/Mins
|
||||
#ifndef NSINTEGER_DEFINED
|
||||
#if __LP64__ || NS_BUILD_32_LIKE_64
|
||||
@ -238,4 +267,178 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
||||
#endif /* !defined(__LP64__) || !__LP64__ */
|
||||
#define CGFLOAT_DEFINED 1
|
||||
#endif // CGFLOAT_DEFINED
|
||||
#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
|
||||
|
||||
// Some support for advanced clang static analysis functionality
|
||||
// See http://clang-analyzer.llvm.org/annotations.html
|
||||
#ifndef __has_feature // Optional.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#ifndef NS_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_retained)
|
||||
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
|
||||
#else
|
||||
#define NS_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_RETURNS_NOT_RETAINED
|
||||
#if __has_feature(attribute_ns_returns_not_retained)
|
||||
#define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained))
|
||||
#else
|
||||
#define NS_RETURNS_NOT_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_retained)
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
#else
|
||||
#define CF_RETURNS_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_RETURNS_NOT_RETAINED
|
||||
#if __has_feature(attribute_cf_returns_not_retained)
|
||||
#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
|
||||
#else
|
||||
#define CF_RETURNS_NOT_RETAINED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_CONSUMED
|
||||
#if __has_feature(attribute_ns_consumed)
|
||||
#define NS_CONSUMED __attribute__((ns_consumed))
|
||||
#else
|
||||
#define NS_CONSUMED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef CF_CONSUMED
|
||||
#if __has_feature(attribute_cf_consumed)
|
||||
#define CF_CONSUMED __attribute__((cf_consumed))
|
||||
#else
|
||||
#define CF_CONSUMED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NS_CONSUMES_SELF
|
||||
#if __has_feature(attribute_ns_consumes_self)
|
||||
#define NS_CONSUMES_SELF __attribute__((ns_consumes_self))
|
||||
#else
|
||||
#define NS_CONSUMES_SELF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef NS_FORMAT_ARGUMENT
|
||||
#define NS_FORMAT_ARGUMENT(A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef NS_FORMAT_FUNCTION
|
||||
#define NS_FORMAT_FUNCTION(F,A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef CF_FORMAT_ARGUMENT
|
||||
#define CF_FORMAT_ARGUMENT(A)
|
||||
#endif
|
||||
|
||||
// Defined on 10.6 and above.
|
||||
#ifndef CF_FORMAT_FUNCTION
|
||||
#define CF_FORMAT_FUNCTION(F,A)
|
||||
#endif
|
||||
|
||||
#ifndef GTM_NONNULL
|
||||
#define GTM_NONNULL(x) __attribute__((nonnull(x)))
|
||||
#endif
|
||||
|
||||
// Invalidates the initializer from which it's called.
|
||||
#ifndef GTMInvalidateInitializer
|
||||
#if __has_feature(objc_arc)
|
||||
#define GTMInvalidateInitializer() \
|
||||
do { \
|
||||
[self class]; /* Avoid warning of dead store to |self|. */ \
|
||||
_GTMDevAssert(NO, @"Invalid initializer."); \
|
||||
return nil; \
|
||||
} while (0)
|
||||
#else
|
||||
#define GTMInvalidateInitializer() \
|
||||
do { \
|
||||
[self release]; \
|
||||
_GTMDevAssert(NO, @"Invalid initializer."); \
|
||||
return nil; \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
// Declared here so that it can easily be used for logging tracking if
|
||||
// necessary. See GTMUnitTestDevLog.h for details.
|
||||
@class NSString;
|
||||
GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...) NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
// Macro to allow you to create NSStrings out of other macros.
|
||||
// #define FOO foo
|
||||
// NSString *fooString = GTM_NSSTRINGIFY(FOO);
|
||||
#if !defined (GTM_NSSTRINGIFY)
|
||||
#define GTM_NSSTRINGIFY_INNER(x) @#x
|
||||
#define GTM_NSSTRINGIFY(x) GTM_NSSTRINGIFY_INNER(x)
|
||||
#endif
|
||||
|
||||
// Macro to allow fast enumeration when building for 10.5 or later, and
|
||||
// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration
|
||||
// does keys, so pick the right thing, nothing is done on the FastEnumeration
|
||||
// side to be sure you're getting what you wanted.
|
||||
#ifndef GTM_FOREACH_OBJECT
|
||||
#if TARGET_OS_IPHONE || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5)
|
||||
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
|
||||
for (element in enumeration)
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
for (element in collection)
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
for (element in collection)
|
||||
#else
|
||||
#define GTM_FOREACH_ENUMEREE(element, enumeration) \
|
||||
for (NSEnumerator *_ ## element ## _enum = enumeration; \
|
||||
(element = [_ ## element ## _enum nextObject]) != nil; )
|
||||
#define GTM_FOREACH_OBJECT(element, collection) \
|
||||
GTM_FOREACH_ENUMEREE(element, [collection objectEnumerator])
|
||||
#define GTM_FOREACH_KEY(element, collection) \
|
||||
GTM_FOREACH_ENUMEREE(element, [collection keyEnumerator])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// To simplify support for both Leopard and Snow Leopard we declare
|
||||
// the Snow Leopard protocols that we need here.
|
||||
#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
#define GTM_10_6_PROTOCOLS_DEFINED 1
|
||||
@protocol NSConnectionDelegate
|
||||
@end
|
||||
@protocol NSAnimationDelegate
|
||||
@end
|
||||
@protocol NSImageDelegate
|
||||
@end
|
||||
@protocol NSTabViewDelegate
|
||||
@end
|
||||
#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
|
||||
|
||||
// GTM_SEL_STRING is for specifying selector (usually property) names to KVC
|
||||
// or KVO methods.
|
||||
// In debug it will generate warnings for undeclared selectors if
|
||||
// -Wunknown-selector is turned on.
|
||||
// In release it will have no runtime overhead.
|
||||
#ifndef GTM_SEL_STRING
|
||||
#ifdef DEBUG
|
||||
#define GTM_SEL_STRING(selName) NSStringFromSelector(@selector(selName))
|
||||
#else
|
||||
#define GTM_SEL_STRING(selName) @#selName
|
||||
#endif // DEBUG
|
||||
#endif // GTM_SEL_STRING
|
||||
|
||||
#endif // __OBJC__
|
||||
|
78
3rdparty/google-breakpad/common/mac/GTMLogger.h
vendored
78
3rdparty/google-breakpad/common/mac/GTMLogger.h
vendored
@ -55,12 +55,6 @@
|
||||
// Predeclaration of used protocols that are declared later in this file.
|
||||
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
|
||||
#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b)))
|
||||
#else
|
||||
#define CHECK_FORMAT_NSSTRING(a, b)
|
||||
#endif
|
||||
|
||||
// GTMLogger
|
||||
//
|
||||
// GTMLogger is the primary user-facing class for an object-oriented logging
|
||||
@ -244,6 +238,10 @@
|
||||
// Same as +standardLogger, but logs to stderr.
|
||||
+ (id)standardLoggerWithStderr;
|
||||
|
||||
// Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
|
||||
// stderr, everything else goes to stdout.
|
||||
+ (id)standardLoggerWithStdoutAndStderr;
|
||||
|
||||
// Returns a new standard GTMLogger instance with a log writer that will
|
||||
// write to the file at |path|, and will use the GTMLogStandardFormatter and
|
||||
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
|
||||
@ -274,13 +272,13 @@
|
||||
//
|
||||
|
||||
// Logs a message at the debug level (kGTMLoggerLevelDebug).
|
||||
- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the info level (kGTMLoggerLevelInfo).
|
||||
- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the error level (kGTMLoggerLevelError).
|
||||
- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
// Logs a message at the assert level (kGTMLoggerLevelAssert).
|
||||
- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2);
|
||||
- (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
|
||||
|
||||
|
||||
//
|
||||
@ -310,16 +308,19 @@
|
||||
// enable the logging of function names.
|
||||
@interface GTMLogger (GTMLoggerMacroHelpers)
|
||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
|
||||
CHECK_FORMAT_NSSTRING(2, 3);
|
||||
NS_FORMAT_FUNCTION(2, 3);
|
||||
@end // GTMLoggerMacroHelpers
|
||||
|
||||
|
||||
// The convenience macros are only defined if they haven't already been defined.
|
||||
#ifndef GTMLoggerInfo
|
||||
|
||||
// Convenience macros that log to the shared GTMLogger instance. These macros
|
||||
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
|
||||
// calls will be compiled out of non-Debug builds.
|
||||
@ -339,6 +340,8 @@
|
||||
#define GTMLoggerDebug(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#endif // !defined(GTMLoggerInfo)
|
||||
|
||||
// Log levels.
|
||||
typedef enum {
|
||||
kGTMLoggerLevelUnknown,
|
||||
@ -407,7 +410,7 @@ typedef enum {
|
||||
- (NSString *)stringForFunc:(NSString *)func
|
||||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level;
|
||||
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
|
||||
@end // GTMLogFormatter
|
||||
|
||||
|
||||
@ -415,6 +418,10 @@ typedef enum {
|
||||
// printf) would. It does not do anything fancy, nor does it add any data of its
|
||||
// own.
|
||||
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
|
||||
|
||||
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
|
||||
- (NSString *)prettyNameForFunc:(NSString *)func;
|
||||
|
||||
@end // GTMLogBasicFormatter
|
||||
|
||||
|
||||
@ -450,9 +457,48 @@ typedef enum {
|
||||
@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
|
||||
@end // GTMLogLevelFilter
|
||||
|
||||
|
||||
// A simple log filter that does NOT filter anything out;
|
||||
// -filterAllowsMessage:level will always return YES. This can be a convenient
|
||||
// way to enable debug-level logging in release builds (if you so desire).
|
||||
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
|
||||
@end // GTMLogNoFilter
|
||||
|
||||
|
||||
// Base class for custom level filters. Not for direct use, use the minimum
|
||||
// or maximum level subclasses below.
|
||||
@interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
|
||||
@private
|
||||
NSIndexSet *allowedLevels_;
|
||||
}
|
||||
@end
|
||||
|
||||
// A log filter that allows you to set a minimum log level. Messages below this
|
||||
// level will be filtered.
|
||||
@interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
|
||||
|
||||
// Designated initializer, logs at levels < |level| will be filtered.
|
||||
- (id)initWithMinimumLevel:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
// A log filter that allows you to set a maximum log level. Messages whose level
|
||||
// exceeds this level will be filtered. This is really only useful if you have
|
||||
// a composite GTMLogger that is sending the other messages elsewhere.
|
||||
@interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
|
||||
|
||||
// Designated initializer, logs at levels > |level| will be filtered.
|
||||
- (id)initWithMaximumLevel:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
// For subclasses only
|
||||
@interface GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
|
||||
|
||||
@end
|
||||
|
||||
|
345
3rdparty/google-breakpad/common/mac/GTMLogger.m
vendored
345
3rdparty/google-breakpad/common/mac/GTMLogger.m
vendored
@ -24,23 +24,15 @@
|
||||
#import <pthread.h>
|
||||
|
||||
|
||||
// Define a trivial assertion macro to avoid dependencies
|
||||
#ifdef DEBUG
|
||||
#define GTMLOGGER_ASSERT(expr) assert(expr)
|
||||
#else
|
||||
#define GTMLOGGER_ASSERT(expr)
|
||||
#endif
|
||||
|
||||
|
||||
@interface GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
|
||||
// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
|
||||
// -Wmissing-format-attribute
|
||||
// when the function is anything more complex than foo(NSString *fmt, ...).
|
||||
// You see the error inside the function when you turn ... into va_args and
|
||||
// attempt to call another function (like vsprintf for example).
|
||||
// So we just shut off the warning for this file. We reenable it at the end.
|
||||
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
|
||||
#endif // !__clang__
|
||||
|
||||
// Reference to the shared GTMLogger instance. This is not a singleton, it's
|
||||
// just an easy reference to one shared instance.
|
||||
@ -56,7 +48,6 @@ static GTMLogger *gSharedLogger = nil;
|
||||
if (gSharedLogger == nil) {
|
||||
gSharedLogger = [[self standardLogger] retain];
|
||||
}
|
||||
GTMLOGGER_ASSERT(gSharedLogger != nil);
|
||||
}
|
||||
return [[gSharedLogger retain] autorelease];
|
||||
}
|
||||
@ -69,24 +60,85 @@ static GTMLogger *gSharedLogger = nil;
|
||||
}
|
||||
|
||||
+ (id)standardLogger {
|
||||
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
|
||||
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
|
||||
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
|
||||
return [self loggerWithWriter:writer formatter:fr filter:filter];
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
|
||||
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init]
|
||||
autorelease];
|
||||
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
|
||||
return [[[self alloc] initWithWriter:writer
|
||||
formatter:fr
|
||||
filter:filter] autorelease];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithStderr {
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
|
||||
return me;
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
|
||||
return me;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithStdoutAndStderr {
|
||||
// We're going to take advantage of the GTMLogger to GTMLogWriter adaptor
|
||||
// and create a composite logger that an outer "standard" logger can use
|
||||
// as a writer. Our inner loggers should apply no formatting since the main
|
||||
// logger does that and we want the caller to be able to change formatters
|
||||
// or add writers without knowing the inner structure of our composite.
|
||||
|
||||
// Don't trust NSFileHandle not to throw
|
||||
@try {
|
||||
GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init]
|
||||
autorelease];
|
||||
GTMLogger *stdoutLogger =
|
||||
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogMaximumLevelFilter alloc]
|
||||
initWithMaximumLevel:kGTMLoggerLevelInfo]
|
||||
autorelease]];
|
||||
GTMLogger *stderrLogger =
|
||||
[self loggerWithWriter:[NSFileHandle fileHandleWithStandardError]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogMininumLevelFilter alloc]
|
||||
initWithMinimumLevel:kGTMLoggerLevelError]
|
||||
autorelease]];
|
||||
GTMLogger *compositeWriter =
|
||||
[self loggerWithWriter:[NSArray arrayWithObjects:
|
||||
stdoutLogger, stderrLogger, nil]
|
||||
formatter:formatter
|
||||
filter:[[[GTMLogNoFilter alloc] init] autorelease]];
|
||||
GTMLogger *outerLogger = [self standardLogger];
|
||||
[outerLogger setWriter:compositeWriter];
|
||||
return outerLogger;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)standardLoggerWithPath:(NSString *)path {
|
||||
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
|
||||
if (fh == nil) return nil;
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:fh];
|
||||
return me;
|
||||
@try {
|
||||
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
|
||||
if (fh == nil) return nil;
|
||||
id me = [self standardLogger];
|
||||
[me setWriter:fh];
|
||||
return me;
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
|
||||
@ -112,69 +164,85 @@ static GTMLogger *gSharedLogger = nil;
|
||||
[self setWriter:writer];
|
||||
[self setFormatter:formatter];
|
||||
[self setFilter:filter];
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
[writer_ release];
|
||||
[formatter_ release];
|
||||
[filter_ release];
|
||||
// Unlikely, but |writer_| may be an NSFileHandle, which can throw
|
||||
@try {
|
||||
[formatter_ release];
|
||||
[filter_ release];
|
||||
[writer_ release];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id<GTMLogWriter>)writer {
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
return [[writer_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setWriter:(id<GTMLogWriter>)writer {
|
||||
@synchronized(self) {
|
||||
[writer_ autorelease];
|
||||
if (writer == nil)
|
||||
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
||||
else
|
||||
writer_ = nil;
|
||||
if (writer == nil) {
|
||||
// Try to use stdout, but don't trust NSFileHandle
|
||||
@try {
|
||||
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |writer_| nil
|
||||
}
|
||||
} else {
|
||||
writer_ = [writer retain];
|
||||
}
|
||||
}
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
}
|
||||
|
||||
- (id<GTMLogFormatter>)formatter {
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
return [[formatter_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
|
||||
@synchronized(self) {
|
||||
[formatter_ autorelease];
|
||||
if (formatter == nil)
|
||||
formatter_ = [[GTMLogBasicFormatter alloc] init];
|
||||
else
|
||||
formatter_ = nil;
|
||||
if (formatter == nil) {
|
||||
@try {
|
||||
formatter_ = [[GTMLogBasicFormatter alloc] init];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |formatter_| nil
|
||||
}
|
||||
} else {
|
||||
formatter_ = [formatter retain];
|
||||
}
|
||||
}
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
}
|
||||
|
||||
- (id<GTMLogFilter>)filter {
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
return [[filter_ retain] autorelease];
|
||||
}
|
||||
|
||||
- (void)setFilter:(id<GTMLogFilter>)filter {
|
||||
@synchronized(self) {
|
||||
[filter_ autorelease];
|
||||
if (filter == nil)
|
||||
filter_ = [[GTMLogNoFilter alloc] init];
|
||||
else
|
||||
filter_ = nil;
|
||||
if (filter == nil) {
|
||||
@try {
|
||||
filter_ = [[GTMLogNoFilter alloc] init];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Leave |filter_| nil
|
||||
}
|
||||
} else {
|
||||
filter_ = [filter retain];
|
||||
}
|
||||
}
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
}
|
||||
|
||||
- (void)logDebug:(NSString *)fmt, ... {
|
||||
@ -207,7 +275,6 @@ static GTMLogger *gSharedLogger = nil;
|
||||
|
||||
@end // GTMLogger
|
||||
|
||||
|
||||
@implementation GTMLogger (GTMLoggerMacroHelpers)
|
||||
|
||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
|
||||
@ -240,24 +307,26 @@ static GTMLogger *gSharedLogger = nil;
|
||||
|
||||
@end // GTMLoggerMacroHelpers
|
||||
|
||||
|
||||
@implementation GTMLogger (PrivateMethods)
|
||||
|
||||
- (void)logInternalFunc:(const char *)func
|
||||
format:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
||||
GTMLOGGER_ASSERT(filter_ != nil);
|
||||
GTMLOGGER_ASSERT(writer_ != nil);
|
||||
|
||||
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
|
||||
NSString *msg = [formatter_ stringForFunc:fname
|
||||
withFormat:fmt
|
||||
valist:args
|
||||
level:level];
|
||||
if (msg && [filter_ filterAllowsMessage:msg level:level])
|
||||
[writer_ logMessage:msg level:level];
|
||||
// Primary point where logging happens, logging should never throw, catch
|
||||
// everything.
|
||||
@try {
|
||||
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
|
||||
NSString *msg = [formatter_ stringForFunc:fname
|
||||
withFormat:fmt
|
||||
valist:args
|
||||
level:level];
|
||||
if (msg && [filter_ filterAllowsMessage:msg level:level])
|
||||
[writer_ logMessage:msg level:level];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
@end // PrivateMethods
|
||||
@ -278,8 +347,16 @@ static GTMLogger *gSharedLogger = nil;
|
||||
|
||||
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||
@synchronized(self) {
|
||||
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
|
||||
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
// Closed pipes should not generate exceptions in our caller. Catch here
|
||||
// as well [GTMLogger logInternalFunc:...] so that an exception in this
|
||||
// writer does not prevent other writers from having a chance.
|
||||
@try {
|
||||
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
|
||||
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
@catch (id e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,19 +405,32 @@ static GTMLogger *gSharedLogger = nil;
|
||||
|
||||
@implementation GTMLogBasicFormatter
|
||||
|
||||
- (NSString *)prettyNameForFunc:(NSString *)func {
|
||||
NSString *name = [func stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
NSString *function = @"(unknown)";
|
||||
if ([name length]) {
|
||||
if (// Objective C __func__ and __PRETTY_FUNCTION__
|
||||
[name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
|
||||
// C++ __PRETTY_FUNCTION__ and other preadorned formats
|
||||
[name hasSuffix:@")"]) {
|
||||
function = name;
|
||||
} else {
|
||||
// Assume C99 __func__
|
||||
function = [NSString stringWithFormat:@"%@()", name];
|
||||
}
|
||||
}
|
||||
return function;
|
||||
}
|
||||
|
||||
- (NSString *)stringForFunc:(NSString *)func
|
||||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
// Performance note: since we always have to create a new NSString from the
|
||||
// returned CFStringRef, we may want to do a quick check here to see if |fmt|
|
||||
// Performance note: We may want to do a quick check here to see if |fmt|
|
||||
// contains a '%', and if not, simply return 'fmt'.
|
||||
CFStringRef cfmsg = NULL;
|
||||
cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
|
||||
NULL, // format options
|
||||
(CFStringRef)fmt,
|
||||
args);
|
||||
return GTMCFAutorelease(cfmsg);
|
||||
if (!(fmt && args)) return nil;
|
||||
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
|
||||
}
|
||||
|
||||
@end // GTMLogBasicFormatter
|
||||
@ -355,6 +445,10 @@ static GTMLogger *gSharedLogger = nil;
|
||||
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
|
||||
pname_ = [[[NSProcessInfo processInfo] processName] copy];
|
||||
pid_ = [[NSProcessInfo processInfo] processIdentifier];
|
||||
if (!(dateFormatter_ && pname_)) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -369,15 +463,15 @@ static GTMLogger *gSharedLogger = nil;
|
||||
withFormat:(NSString *)fmt
|
||||
valist:(va_list)args
|
||||
level:(GTMLoggerLevel)level {
|
||||
GTMLOGGER_ASSERT(dateFormatter_ != nil);
|
||||
NSString *tstamp = nil;
|
||||
@synchronized (dateFormatter_) {
|
||||
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
|
||||
tstamp, pname_, pid_, pthread_self(),
|
||||
level, (func ? func : @"(no func)"),
|
||||
[super stringForFunc:func withFormat:fmt valist:args level:level]];
|
||||
tstamp, pname_, pid_, pthread_self(),
|
||||
level, [self prettyNameForFunc:func],
|
||||
// |super| has guard for nil |fmt| and |args|
|
||||
[super stringForFunc:func withFormat:fmt valist:args level:level]];
|
||||
}
|
||||
|
||||
@end // GTMLogStandardFormatter
|
||||
@ -391,14 +485,20 @@ static GTMLogger *gSharedLogger = nil;
|
||||
// COV_NF_START
|
||||
static BOOL IsVerboseLoggingEnabled(void) {
|
||||
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
|
||||
static char *env = NULL;
|
||||
if (env == NULL)
|
||||
env = getenv([kVerboseLoggingKey UTF8String]);
|
||||
|
||||
if (env && env[0]) {
|
||||
return (strtol(env, NULL, 10) != 0);
|
||||
NSString *value = [[[NSProcessInfo processInfo] environment]
|
||||
objectForKey:kVerboseLoggingKey];
|
||||
if (value) {
|
||||
// Emulate [NSString boolValue] for pre-10.5
|
||||
value = [value stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
if ([[value uppercaseString] hasPrefix:@"Y"] ||
|
||||
[[value uppercaseString] hasPrefix:@"T"] ||
|
||||
[value intValue]) {
|
||||
return YES;
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
|
||||
}
|
||||
// COV_NF_END
|
||||
@ -417,7 +517,7 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
||||
allow = NO;
|
||||
break;
|
||||
case kGTMLoggerLevelInfo:
|
||||
allow = (IsVerboseLoggingEnabled() == YES);
|
||||
allow = IsVerboseLoggingEnabled();
|
||||
break;
|
||||
case kGTMLoggerLevelError:
|
||||
allow = YES;
|
||||
@ -443,3 +543,70 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
||||
}
|
||||
|
||||
@end // GTMLogNoFilter
|
||||
|
||||
|
||||
@implementation GTMLogAllowedLevelFilter
|
||||
|
||||
// Private designated initializer
|
||||
- (id)initWithAllowedLevels:(NSIndexSet *)levels {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
allowedLevels_ = [levels retain];
|
||||
// Cap min/max level
|
||||
if (!allowedLevels_ ||
|
||||
// NSIndexSet is unsigned so only check the high bound, but need to
|
||||
// check both first and last index because NSIndexSet appears to allow
|
||||
// wraparound.
|
||||
([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) ||
|
||||
([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) {
|
||||
[self release];
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
// Allow all levels in default init
|
||||
return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(kGTMLoggerLevelUnknown,
|
||||
(kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[allowedLevels_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||
return [allowedLevels_ containsIndex:level];
|
||||
}
|
||||
|
||||
@end // GTMLogAllowedLevelFilter
|
||||
|
||||
|
||||
@implementation GTMLogMininumLevelFilter
|
||||
|
||||
- (id)initWithMinimumLevel:(GTMLoggerLevel)level {
|
||||
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(level,
|
||||
(kGTMLoggerLevelAssert - level + 1))]];
|
||||
}
|
||||
|
||||
@end // GTMLogMininumLevelFilter
|
||||
|
||||
|
||||
@implementation GTMLogMaximumLevelFilter
|
||||
|
||||
- (id)initWithMaximumLevel:(GTMLoggerLevel)level {
|
||||
return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange:
|
||||
NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]];
|
||||
}
|
||||
|
||||
@end // GTMLogMaximumLevelFilter
|
||||
|
||||
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
|
||||
// See comment at top of file.
|
||||
#pragma GCC diagnostic error "-Wmissing-format-attribute"
|
||||
#endif // !__clang__
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
// Each file is sent with a name field in addition to the filename and data
|
||||
// The data will be sent synchronously.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface HTTPMultipartUpload : NSObject {
|
||||
@protected
|
||||
|
@ -32,6 +32,8 @@
|
||||
|
||||
@interface HTTPMultipartUpload(PrivateMethods)
|
||||
- (NSString *)multipartBoundary;
|
||||
// Each of the following methods will append the starting multipart boundary,
|
||||
// but not the ending one.
|
||||
- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value;
|
||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
|
||||
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
|
||||
@ -67,11 +69,9 @@
|
||||
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
|
||||
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
|
||||
NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
|
||||
NSString *post = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
|
||||
|
||||
[data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[data appendData:contents];
|
||||
[data appendData:[post dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -182,6 +182,9 @@
|
||||
[postBody appendData:fileData];
|
||||
}
|
||||
|
||||
NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
|
||||
[postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
[req setHTTPBody:postBody];
|
||||
[req setHTTPMethod:@"POST"];
|
||||
|
||||
@ -193,6 +196,7 @@
|
||||
error:error];
|
||||
|
||||
[response_ retain];
|
||||
[req release];
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -139,12 +139,6 @@ class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
|
||||
return disposition;
|
||||
}
|
||||
|
||||
// We're just a simple wrapper for mach_msg_port_descriptor_t
|
||||
// and have the same memory layout
|
||||
operator mach_msg_port_descriptor_t&() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For convenience
|
||||
operator mach_port_t() const {
|
||||
return GetMachPort();
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#import <stdio.h>
|
||||
#import "MachIPC.h"
|
||||
#include "common/mac/bootstrap_compat.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
//==============================================================================
|
||||
@ -187,9 +188,10 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
|
||||
if (init_result_ != KERN_SUCCESS)
|
||||
return;
|
||||
|
||||
init_result_ = bootstrap_register(bootstrap_port,
|
||||
const_cast<char*>(receive_port_name),
|
||||
port_);
|
||||
init_result_ = breakpad::BootstrapRegister(
|
||||
bootstrap_port,
|
||||
const_cast<char*>(receive_port_name),
|
||||
port_);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
@ -134,7 +134,7 @@ class SimpleStringDictionary {
|
||||
// Given |key|, returns its corresponding |value|.
|
||||
// If |key| is NULL, an assert will fire or NULL will be returned. If |key|
|
||||
// is not found or is an empty string, NULL is returned.
|
||||
const char *GetValueForKey(const char *key);
|
||||
const char *GetValueForKey(const char *key) const;
|
||||
|
||||
// Stores a string |value| represented by |key|. If |key| is NULL or an empty
|
||||
// string, this will assert (or do nothing). If |value| is NULL then
|
||||
|
@ -55,13 +55,13 @@ int SimpleStringDictionary::GetCount() const {
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const char *SimpleStringDictionary::GetValueForKey(const char *key) {
|
||||
const char *SimpleStringDictionary::GetValueForKey(const char *key) const {
|
||||
assert(key);
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
|
||||
KeyValueEntry &entry = entries_[i];
|
||||
const KeyValueEntry &entry = entries_[i];
|
||||
if (entry.IsActive() && !strcmp(entry.GetKey(), key)) {
|
||||
return entry.GetValue();
|
||||
}
|
||||
|
81
3rdparty/google-breakpad/common/mac/arch_utilities.cc
vendored
Normal file
81
3rdparty/google-breakpad/common/mac/arch_utilities.cc
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
#include "common/mac/arch_utilities.h"
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef CPU_TYPE_ARM
|
||||
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
|
||||
#endif // CPU_TYPE_ARM
|
||||
|
||||
#ifndef CPU_SUBTYPE_ARM_V7
|
||||
#define CPU_SUBTYPE_ARM_V7 (static_cast<cpu_subtype_t>(9))
|
||||
#endif // CPU_SUBTYPE_ARM_V7
|
||||
|
||||
#ifndef CPU_SUBTYPE_ARM_V7S
|
||||
#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11))
|
||||
#endif // CPU_SUBTYPE_ARM_V7S
|
||||
|
||||
namespace {
|
||||
|
||||
const NXArchInfo* ArchInfo_armv7s() {
|
||||
NXArchInfo* armv7s = new NXArchInfo;
|
||||
*armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM,
|
||||
CPU_SUBTYPE_ARM_V7);
|
||||
armv7s->name = "armv7s";
|
||||
armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S;
|
||||
armv7s->description = "arm v7s";
|
||||
return armv7s;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) {
|
||||
// TODO: Remove this when the OS knows about armv7s.
|
||||
if (!strcmp("armv7s", arch_name))
|
||||
return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S);
|
||||
return NXGetArchInfoFromName(arch_name);
|
||||
}
|
||||
|
||||
const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
|
||||
cpu_subtype_t cpu_subtype) {
|
||||
// TODO: Remove this when the OS knows about armv7s.
|
||||
if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) {
|
||||
static const NXArchInfo* armv7s = ArchInfo_armv7s();
|
||||
return armv7s;
|
||||
}
|
||||
return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
47
3rdparty/google-breakpad/common/mac/arch_utilities.h
vendored
Normal file
47
3rdparty/google-breakpad/common/mac/arch_utilities.h
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
// arch_utilities.h: Utilities for architecture introspection for Mac platform.
|
||||
|
||||
#ifndef COMMON_MAC_ARCH_UTILITIES_H__
|
||||
#define COMMON_MAC_ARCH_UTILITIES_H__
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Custom implementation of |NXGetArchInfoFromName| and
|
||||
// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes.
|
||||
const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name);
|
||||
const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
|
||||
cpu_subtype_t cpu_subtype);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_MAC_ARCH_UTILITIES_H__
|
42
3rdparty/google-breakpad/common/mac/bootstrap_compat.cc
vendored
Normal file
42
3rdparty/google-breakpad/common/mac/bootstrap_compat.cc
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2012, 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.
|
||||
|
||||
#include "common/mac/bootstrap_compat.h"
|
||||
|
||||
namespace breakpad {
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
kern_return_t BootstrapRegister(mach_port_t bp,
|
||||
name_t service_name,
|
||||
mach_port_t sp) {
|
||||
return bootstrap_register(bp, service_name, sp);
|
||||
}
|
||||
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
|
||||
|
||||
} // namesapce breakpad
|
54
3rdparty/google-breakpad/common/mac/bootstrap_compat.h
vendored
Normal file
54
3rdparty/google-breakpad/common/mac/bootstrap_compat.h
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2012, 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 COMMON_MAC_BOOTSTRAP_COMPAT_H_
|
||||
#define COMMON_MAC_BOOTSTRAP_COMPAT_H_
|
||||
|
||||
#include <servers/bootstrap.h>
|
||||
|
||||
namespace breakpad {
|
||||
|
||||
// Wrapper for bootstrap_register to avoid deprecation warnings.
|
||||
//
|
||||
// In 10.6, it's possible to call bootstrap_check_in as the one-stop-shop for
|
||||
// handling what bootstrap_register is used for. In 10.5, bootstrap_check_in
|
||||
// can't check in a service whose name has not yet been registered, despite
|
||||
// bootstrap_register being marked as deprecated in that OS release. Breakpad
|
||||
// needs to register new service names, and in 10.5, calling
|
||||
// bootstrap_register is the only way to achieve that. Attempts to call
|
||||
// bootstrap_check_in for a new service name on 10.5 will result in
|
||||
// BOOTSTRAP_UNKNOWN_SERVICE being returned rather than registration of the
|
||||
// new service name.
|
||||
kern_return_t BootstrapRegister(mach_port_t bp,
|
||||
name_t service_name,
|
||||
mach_port_t sp);
|
||||
|
||||
} // namespace breakpad
|
||||
|
||||
#endif // COMMON_MAC_BOOTSTRAP_COMPAT_H_
|
11
3rdparty/google-breakpad/common/mac/dump_syms.h
vendored
11
3rdparty/google-breakpad/common/mac/dump_syms.h
vendored
@ -1,6 +1,6 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// Copyright (c) 2011, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
@ -40,6 +40,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -108,10 +109,10 @@ class DumpSymbols {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read the selected object file's debugging information, and write it
|
||||
// out to |stream|. Return true on success; if an error occurs, report it
|
||||
// and return false.
|
||||
bool WriteSymbolFile(FILE *stream);
|
||||
// Read the selected object file's debugging information, and write it out to
|
||||
// |stream|. Write the CFI section if |cfi| is true. Return true on success;
|
||||
// if an error occurs, report it and return false.
|
||||
bool WriteSymbolFile(std::ostream &stream, bool cfi);
|
||||
|
||||
private:
|
||||
// Used internally.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user