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})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
if (LINUX)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=gnu++0x")
|
||||||
set(SOURCES
|
|
||||||
client/linux/crash_generation/crash_generation_client.cc
|
set(COMMON_SOURCES
|
||||||
client/linux/handler/exception_handler.cc
|
common/convert_UTF.c
|
||||||
client/linux/minidump_writer/linux_dumper.cc
|
common/dwarf/bytereader.cc
|
||||||
client/linux/minidump_writer/minidump_writer.cc
|
common/dwarf/dwarf2diehandler.cc
|
||||||
client/minidump_file_writer.cc
|
common/dwarf/dwarf2reader.cc
|
||||||
common/convert_UTF.c
|
common/dwarf_cfi_to_module.cc
|
||||||
common/md5.c
|
common/dwarf_cu_to_module.cc
|
||||||
common/string_conversion.cc
|
common/dwarf_line_to_module.cc
|
||||||
common/linux/file_id.cc
|
common/language.cc
|
||||||
common/linux/guid_creator.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
|
list(APPEND LIBRARY_SOURCES
|
||||||
${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)
|
list(APPEND DUMP_SYMS_SOURCES
|
||||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms/build/Release/dump_syms
|
tools/linux/dump_syms/dump_syms.cc
|
||||||
COMMAND xcodebuild -target dump_syms -configuration Release -sdk macosx10.5 ARCHS=i386
|
)
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/mac/dump_syms)
|
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
|
list(APPEND COMMON_SOURCES
|
||||||
COMMAND xcodebuild -target Breakpad -configuration Release -sdk macosx10.5 ARCHS=i386
|
common/mac/arch_utilities.cc
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/client/mac)
|
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
|
list(APPEND LIBRARY_SOURCES
|
||||||
DEPENDS tools/mac/dump_syms/build/Release/dump_syms
|
client/mac/crash_generation/crash_generation_client.cc
|
||||||
client/mac/build/Release/Breakpad.Framework)
|
client/mac/Framework/Breakpad.mm
|
||||||
endif (APPLE)
|
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 "client/linux/crash_generation/crash_generation_client.h"
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/eintr_wrapper.h"
|
||||||
|
#include "common/linux/ignore_ret.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "third_party/lss/linux_syscall_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));
|
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
|
||||||
*p = fds[1];
|
*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]);
|
sys_close(fds[1]);
|
||||||
|
if (ret <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
// wait for an ACK from the server
|
// wait for an ACK from the server
|
||||||
char b;
|
char b;
|
||||||
HANDLE_EINTR(sys_read(fds[0], &b, 1));
|
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -73,19 +73,14 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
#include <sys/signal.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#if !defined(__ANDROID__)
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <sys/signal.h>
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
#endif
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
#include <ucontext.h>
|
#include <ucontext.h>
|
||||||
#endif
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -93,12 +88,14 @@
|
|||||||
|
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
|
#include "client/linux/log/log.h"
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||||
#include "common/linux/guid_creator.h"
|
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/eintr_wrapper.h"
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
|
#include "linux/sched.h"
|
||||||
|
|
||||||
#ifndef PR_SET_PTRACER
|
#ifndef PR_SET_PTRACER
|
||||||
#define PR_SET_PTRACER 0x59616d61
|
#define PR_SET_PTRACER 0x59616d61
|
||||||
#endif
|
#endif
|
||||||
@ -111,144 +108,177 @@ static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
|||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
namespace {
|
||||||
// The list of signals which we consider to be crashes. The default action for
|
// 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
|
// 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.
|
// signal after handling it and expect that it'll be fatal.
|
||||||
static const int kExceptionSignals[] = {
|
const int kExceptionSignals[] = {
|
||||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
|
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
|
// We can stack multiple exception handlers. In that case, this is the global
|
||||||
// which holds the stack.
|
// which holds the stack.
|
||||||
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||||
unsigned ExceptionHandler::handler_stack_index_ = 0;
|
|
||||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
|
||||||
PTHREAD_MUTEX_INITIALIZER;
|
PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// Runs before crashing: normal context.
|
||||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
|
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||||
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,
|
|
||||||
FilterCallback filter,
|
FilterCallback filter,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void* callback_context,
|
void* callback_context,
|
||||||
bool install_handler,
|
bool install_handler,
|
||||||
const int server_fd)
|
const int server_fd)
|
||||||
: filter_(filter),
|
: filter_(filter),
|
||||||
callback_(callback),
|
callback_(callback),
|
||||||
callback_context_(callback_context),
|
callback_context_(callback_context),
|
||||||
handler_installed_(install_handler)
|
minidump_descriptor_(descriptor),
|
||||||
{
|
crash_handler_(NULL) {
|
||||||
Init(dump_path, server_fd);
|
if (server_fd >= 0)
|
||||||
}
|
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD())
|
||||||
ExceptionHandler::~ExceptionHandler() {
|
minidump_descriptor_.UpdatePath();
|
||||||
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);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
pthread_mutex_lock(&handler_stack_mutex_);
|
||||||
if (handler_stack_ == NULL)
|
if (!handler_stack_)
|
||||||
handler_stack_ = new std::vector<ExceptionHandler *>;
|
handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||||
|
if (install_handler) {
|
||||||
|
InstallAlternateStackLocked();
|
||||||
|
InstallHandlersLocked();
|
||||||
|
}
|
||||||
handler_stack_->push_back(this);
|
handler_stack_->push_back(this);
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// Runs before crashing: normal context.
|
||||||
bool ExceptionHandler::InstallHandlers() {
|
ExceptionHandler::~ExceptionHandler() {
|
||||||
// We run the signal handlers on an alternative stack because we might have
|
pthread_mutex_lock(&handler_stack_mutex_);
|
||||||
// crashed because of a stack overflow.
|
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
|
// Runs before crashing: normal context.
|
||||||
// such a small stack.
|
// static
|
||||||
static const unsigned kSigStackSize = 8192;
|
bool ExceptionHandler::InstallHandlersLocked() {
|
||||||
|
if (handlers_installed)
|
||||||
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)
|
|
||||||
return false;
|
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;
|
struct sigaction sa;
|
||||||
memset(&sa, 0, sizeof(sa));
|
memset(&sa, 0, sizeof(sa));
|
||||||
sigemptyset(&sa.sa_mask);
|
sigemptyset(&sa.sa_mask);
|
||||||
|
|
||||||
// mask all exception signals when we're handling one of them.
|
// Mask all exception signals when we're handling one of them.
|
||||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
|
for (int i = 0; i < kNumHandledSignals; ++i)
|
||||||
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
|
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
|
||||||
|
|
||||||
sa.sa_sigaction = SignalHandler;
|
sa.sa_sigaction = SignalHandler;
|
||||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
|
||||||
|
|
||||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
|
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||||
struct sigaction* old = new struct sigaction;
|
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
|
||||||
if (sigaction(kExceptionSignals[i], &sa, old) == -1)
|
// At this point it is impractical to back out changes, and so failure to
|
||||||
return false;
|
// install a signal is intentionally ignored.
|
||||||
old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
|
}
|
||||||
}
|
}
|
||||||
|
handlers_installed = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// This function runs in a compromised context: see the top of the file.
|
||||||
void ExceptionHandler::UninstallHandlers() {
|
// Runs on the crashing thread.
|
||||||
for (unsigned i = 0; i < old_handlers_.size(); ++i) {
|
// static
|
||||||
struct sigaction *action =
|
void ExceptionHandler::RestoreHandlersLocked() {
|
||||||
reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
|
if (!handlers_installed)
|
||||||
sigaction(old_handlers_[i].first, action, NULL);
|
return;
|
||||||
delete action;
|
|
||||||
}
|
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||||
std::vector<ExceptionHandler*>::iterator handler =
|
signal(kExceptionSignals[i], SIG_DFL);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
handlers_installed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
// 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.
|
// All the exception signals are blocked at this point.
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
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_);
|
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = handler_stack_->size() - 1; i >= 0; --i) {
|
bool handled = false;
|
||||||
if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
|
for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
||||||
// successfully handled: We are in an invalid state since an exception
|
handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc);
|
||||||
// signal has been delivered. We don't call the exit handlers because
|
}
|
||||||
// they could end up corrupting on-disk state.
|
|
||||||
break;
|
// 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_);
|
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,
|
// No need to reissue the signal. It will automatically trigger again,
|
||||||
// when we return from the signal handler.
|
// 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 {
|
struct ThreadArgument {
|
||||||
pid_t pid; // the crashing process
|
pid_t pid; // the crashing process
|
||||||
|
const MinidumpDescriptor* minidump_descriptor;
|
||||||
ExceptionHandler* handler;
|
ExceptionHandler* handler;
|
||||||
const void* context; // a CrashContext structure
|
const void* context; // a CrashContext structure
|
||||||
size_t context_size;
|
size_t context_size;
|
||||||
@ -350,14 +405,23 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||||||
#endif
|
#endif
|
||||||
context.tid = syscall(__NR_gettid);
|
context.tid = syscall(__NR_gettid);
|
||||||
if (crash_handler_ != NULL) {
|
if (crash_handler_ != NULL) {
|
||||||
if (crash_handler_(&context, sizeof(context),
|
if (crash_handler_(&context, sizeof(context), callback_context_)) {
|
||||||
callback_context_)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return GenerateDump(&context);
|
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.
|
// This function may run in a compromised context: see the top of the file.
|
||||||
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||||
if (IsOutOfProcess())
|
if (IsOutOfProcess())
|
||||||
@ -374,6 +438,7 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
|||||||
|
|
||||||
ThreadArgument thread_arg;
|
ThreadArgument thread_arg;
|
||||||
thread_arg.handler = this;
|
thread_arg.handler = this;
|
||||||
|
thread_arg.minidump_descriptor = &minidump_descriptor_;
|
||||||
thread_arg.pid = getpid();
|
thread_arg.pid = getpid();
|
||||||
thread_arg.context = context;
|
thread_arg.context = context;
|
||||||
thread_arg.context_size = sizeof(*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
|
// is the write() and read() calls will fail with EBADF
|
||||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
||||||
sys_pipe failed:";
|
sys_pipe failed:";
|
||||||
sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
sys_write(2, "\n", 1);
|
logger::write("\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pid_t child = sys_clone(
|
const pid_t child = sys_clone(
|
||||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||||
&thread_arg, NULL, NULL, NULL);
|
&thread_arg, NULL, NULL, NULL);
|
||||||
|
|
||||||
int r, status;
|
int r, status;
|
||||||
// Allow the child to ptrace us
|
// Allow the child to ptrace us
|
||||||
prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
sys_prctl(PR_SET_PTRACER, child);
|
||||||
SendContinueSignalToChild();
|
SendContinueSignalToChild();
|
||||||
do {
|
do {
|
||||||
r = sys_waitpid(child, &status, __WALL);
|
r = sys_waitpid(child, &status, __WALL);
|
||||||
@ -409,17 +475,14 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
|||||||
|
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
|
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
|
||||||
sys_write(2, msg, sizeof(msg) - 1);
|
logger::write(msg, sizeof(msg) - 1);
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
sys_write(2, "\n", 1);
|
logger::write("\n", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||||
|
|
||||||
if (callback_)
|
if (callback_)
|
||||||
success = callback_(dump_path_c_, next_minidump_id_c_,
|
success = callback_(minidump_descriptor_, callback_context_, success);
|
||||||
callback_context_, success);
|
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,9 +494,9 @@ void ExceptionHandler::SendContinueSignalToChild() {
|
|||||||
if(r == -1) {
|
if(r == -1) {
|
||||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
||||||
sys_write failed:";
|
sys_write failed:";
|
||||||
sys_write(2, msg, sizeof(msg) - 1);
|
logger::write(msg, sizeof(msg) - 1);
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
sys_write(2, "\n", 1);
|
logger::write("\n", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,9 +509,9 @@ void ExceptionHandler::WaitForContinueSignal() {
|
|||||||
if(r == -1) {
|
if(r == -1) {
|
||||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
||||||
sys_read failed:";
|
sys_read failed:";
|
||||||
sys_write(2, msg, sizeof(msg) - 1);
|
logger::write(msg, sizeof(msg) - 1);
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
sys_write(2, "\n", 1);
|
logger::write("\n", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,43 +519,81 @@ void ExceptionHandler::WaitForContinueSignal() {
|
|||||||
// Runs on the cloned process.
|
// Runs on the cloned process.
|
||||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||||
size_t context_size) {
|
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,
|
crashing_process,
|
||||||
context,
|
context,
|
||||||
context_size,
|
context_size,
|
||||||
mapping_list_);
|
mapping_list_,
|
||||||
|
app_memory_list_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
|
bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void* callback_context) {
|
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();
|
return eh.WriteMinidump();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandler::WriteMinidump() {
|
bool ExceptionHandler::WriteMinidump() {
|
||||||
#if !defined(__ARM_EABI__)
|
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
|
||||||
// Allow ourselves to be dumped.
|
// 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);
|
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||||
|
|
||||||
CrashContext context;
|
CrashContext context;
|
||||||
int getcontext_result = getcontext(&context.context);
|
int getcontext_result = getcontext(&context.context);
|
||||||
if (getcontext_result)
|
if (getcontext_result)
|
||||||
return false;
|
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,
|
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
||||||
sizeof(context.float_state));
|
sizeof(context.float_state));
|
||||||
|
#endif
|
||||||
context.tid = sys_gettid();
|
context.tid = sys_gettid();
|
||||||
|
|
||||||
bool success = GenerateDump(&context);
|
// Add an exception stream to the minidump for better reporting.
|
||||||
UpdateNextID();
|
memset(&context.siginfo, 0, sizeof(context.siginfo));
|
||||||
return success;
|
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
|
#else
|
||||||
return false;
|
#error "This code has not been ported to your platform yet."
|
||||||
#endif // !defined(__ARM_EABI__)
|
#endif
|
||||||
|
|
||||||
|
return GenerateDump(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExceptionHandler::AddMappingInfo(const std::string& name,
|
void ExceptionHandler::AddMappingInfo(const string& name,
|
||||||
const u_int8_t identifier[sizeof(MDGUID)],
|
const u_int8_t identifier[sizeof(MDGUID)],
|
||||||
uintptr_t start_address,
|
uintptr_t start_address,
|
||||||
size_t mapping_size,
|
size_t mapping_size,
|
||||||
@ -501,7 +602,8 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
|
|||||||
info.start_addr = start_address;
|
info.start_addr = start_address;
|
||||||
info.size = mapping_size;
|
info.size = mapping_size;
|
||||||
info.offset = file_offset;
|
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;
|
MappingEntry mapping;
|
||||||
mapping.first = info;
|
mapping.first = info;
|
||||||
@ -509,4 +611,43 @@ void ExceptionHandler::AddMappingInfo(const std::string& name,
|
|||||||
mapping_list_.push_back(mapping);
|
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
|
} // namespace google_breakpad
|
||||||
|
@ -37,21 +37,17 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.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/crash_generation/crash_generation_client.h"
|
||||||
|
#include "client/linux/handler/minidump_descriptor.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
#include "processor/scoped_ptr.h"
|
#include "processor/scoped_ptr.h"
|
||||||
|
|
||||||
struct sigaction;
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
class ExceptionHandler;
|
|
||||||
|
|
||||||
// ExceptionHandler
|
// ExceptionHandler
|
||||||
//
|
//
|
||||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||||
@ -70,17 +66,18 @@ class ExceptionHandler;
|
|||||||
// use different minidump callbacks for different call sites.
|
// use different minidump callbacks for different call sites.
|
||||||
//
|
//
|
||||||
// In either case, a callback function is called when a minidump is written,
|
// 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
|
// which receives the full path or file descriptor of the minidump. The
|
||||||
// id to collect and write additional application state, and to launch an
|
// caller can collect and write additional application state to that minidump,
|
||||||
// external crash-reporting application.
|
// and launch an external crash-reporting application.
|
||||||
//
|
//
|
||||||
// Caller should try to make the callbacks as crash-friendly as possible,
|
// Caller should try to make the callbacks as crash-friendly as possible,
|
||||||
// it should avoid use heap memory allocation as much as possible.
|
// it should avoid use heap memory allocation as much as possible.
|
||||||
|
|
||||||
class ExceptionHandler {
|
class ExceptionHandler {
|
||||||
public:
|
public:
|
||||||
// A callback function to run before Breakpad performs any substantial
|
// A callback function to run before Breakpad performs any substantial
|
||||||
// processing of an exception. A FilterCallback is called before writing
|
// 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.
|
// callback_context when the handler was created.
|
||||||
//
|
//
|
||||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
// If a FilterCallback returns true, Breakpad will continue processing,
|
||||||
@ -90,10 +87,10 @@ class ExceptionHandler {
|
|||||||
typedef bool (*FilterCallback)(void *context);
|
typedef bool (*FilterCallback)(void *context);
|
||||||
|
|
||||||
// A callback function to run after the minidump has been written.
|
// A callback function to run after the minidump has been written.
|
||||||
// minidump_id is a unique id for the dump, so the minidump
|
// |descriptor| contains the file descriptor or file path containing the
|
||||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
// minidump. |context| is the parameter supplied by the user as
|
||||||
// by the user as callback_context when the handler was created. succeeded
|
// callback_context when the handler was created. |succeeded| indicates
|
||||||
// indicates whether a minidump file was successfully written.
|
// whether a minidump file was successfully written.
|
||||||
//
|
//
|
||||||
// If an exception occurred and the callback returns true, Breakpad will
|
// If an exception occurred and the callback returns true, Breakpad will
|
||||||
// treat the exception as fully-handled, suppressing any other handlers from
|
// 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
|
// should normally return the value of |succeeded|, or when they wish to
|
||||||
// not report an exception of handled, false. Callbacks will rarely want to
|
// not report an exception of handled, false. Callbacks will rarely want to
|
||||||
// return true directly (unless |succeeded| is true).
|
// return true directly (unless |succeeded| is true).
|
||||||
typedef bool (*MinidumpCallback)(const char *dump_path,
|
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
|
||||||
const char *minidump_id,
|
void* context,
|
||||||
void *context,
|
|
||||||
bool succeeded);
|
bool succeeded);
|
||||||
|
|
||||||
// In certain cases, a user may wish to handle the generation of the minidump
|
// In certain cases, a user may wish to handle the generation of the minidump
|
||||||
@ -120,52 +116,73 @@ class ExceptionHandler {
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
// 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
|
// Its return value determines whether or not Breakpad should write a
|
||||||
// minidump. Minidump files will be written to dump_path, and the optional
|
// minidump. The minidump content will be written to the file path or file
|
||||||
// callback is called after writing the dump file, as described above.
|
// 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
|
// If install_handler is true, then a minidump will be written whenever
|
||||||
// an unhandled exception occurs. If it is false, minidumps will only
|
// an unhandled exception occurs. If it is false, minidumps will only
|
||||||
// be written when WriteMinidump is called.
|
// be written when WriteMinidump is called.
|
||||||
ExceptionHandler(const std::string &dump_path,
|
// If |server_fd| is valid, the minidump is generated out-of-process. If it
|
||||||
FilterCallback filter, MinidumpCallback callback,
|
// is -1, in-process generation will always be used.
|
||||||
|
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||||
|
FilterCallback filter,
|
||||||
|
MinidumpCallback callback,
|
||||||
void *callback_context,
|
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,
|
bool install_handler,
|
||||||
const int server_fd);
|
const int server_fd);
|
||||||
|
|
||||||
~ExceptionHandler();
|
~ExceptionHandler();
|
||||||
|
|
||||||
// Get and set the minidump path.
|
const MinidumpDescriptor& minidump_descriptor() const {
|
||||||
std::string dump_path() const { return dump_path_; }
|
return minidump_descriptor_;
|
||||||
void set_dump_path(const std::string &dump_path) {
|
}
|
||||||
dump_path_ = dump_path;
|
|
||||||
dump_path_c_ = dump_path_.c_str();
|
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
|
||||||
UpdateNextID();
|
minidump_descriptor_ = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_crash_handler(HandlerCallback callback) {
|
void set_crash_handler(HandlerCallback callback) {
|
||||||
crash_handler_ = callback;
|
crash_handler_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes a minidump immediately. This can be used to capture the
|
// Writes a minidump immediately. This can be used to capture the execution
|
||||||
// execution state independently of a crash. Returns true on success.
|
// 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();
|
bool WriteMinidump();
|
||||||
|
|
||||||
// Convenience form of WriteMinidump which does not require an
|
// Convenience form of WriteMinidump which does not require an
|
||||||
// ExceptionHandler instance.
|
// ExceptionHandler instance.
|
||||||
static bool WriteMinidump(const std::string &dump_path,
|
static bool WriteMinidump(const string& dump_path,
|
||||||
MinidumpCallback callback,
|
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
|
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
||||||
// blob. It shouldn't be needed in any user code.
|
// 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
|
// Add information about a memory mapping. This can be used if
|
||||||
// a custom library loader is used that maps things in a way
|
// a custom library loader is used that maps things in a way
|
||||||
// that the linux dumper can't handle by reading the maps file.
|
// 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)],
|
const u_int8_t identifier[sizeof(MDGUID)],
|
||||||
uintptr_t start_address,
|
uintptr_t start_address,
|
||||||
size_t mapping_size,
|
size_t mapping_size,
|
||||||
size_t file_offset);
|
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:
|
private:
|
||||||
void Init(const std::string &dump_path,
|
// Save the old signal handlers and install new ones.
|
||||||
const int server_fd);
|
static bool InstallHandlersLocked();
|
||||||
bool InstallHandlers();
|
// Restore the old signal handlers.
|
||||||
void UninstallHandlers();
|
static void RestoreHandlersLocked();
|
||||||
|
|
||||||
void PreresolveSymbols();
|
void PreresolveSymbols();
|
||||||
bool GenerateDump(CrashContext *context);
|
bool GenerateDump(CrashContext *context);
|
||||||
void SendContinueSignalToChild();
|
void SendContinueSignalToChild();
|
||||||
void WaitForContinueSignal();
|
void WaitForContinueSignal();
|
||||||
|
|
||||||
void UpdateNextID();
|
|
||||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||||
static int ThreadEntry(void* arg);
|
static int ThreadEntry(void* arg);
|
||||||
@ -216,32 +242,16 @@ class ExceptionHandler {
|
|||||||
|
|
||||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||||
|
|
||||||
std::string dump_path_;
|
MinidumpDescriptor minidump_descriptor_;
|
||||||
std::string next_minidump_path_;
|
|
||||||
std::string next_minidump_id_;
|
|
||||||
|
|
||||||
// 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_;
|
HandlerCallback crash_handler_;
|
||||||
|
|
||||||
// The global exception handler stack. This is need becuase there may exist
|
// The global exception handler stack. This is need becuase there may exist
|
||||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||||
// registered in this stack.
|
// registered in this stack.
|
||||||
static std::vector<ExceptionHandler*> *handler_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_;
|
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
|
// 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
|
// 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
|
// 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
|
// Callers can add extra info about mappings for cases where the
|
||||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||||
MappingList mapping_list_;
|
MappingList mapping_list_;
|
||||||
|
|
||||||
|
// Callers can request additional memory regions to be included in
|
||||||
|
// the dump.
|
||||||
|
AppMemoryList app_memory_list_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // 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 <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -90,7 +91,7 @@ class DirectoryReader {
|
|||||||
reinterpret_cast<kernel_dirent*>(buf_);
|
reinterpret_cast<kernel_dirent*>(buf_);
|
||||||
|
|
||||||
buf_used_ -= dent->d_reclen;
|
buf_used_ -= dent->d_reclen;
|
||||||
memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -114,7 +115,7 @@ class LineReader {
|
|||||||
|
|
||||||
assert(buf_used_ >= len + 1);
|
assert(buf_used_ >= len + 1);
|
||||||
buf_used_ -= len + 1;
|
buf_used_ -= len + 1;
|
||||||
memmove(buf_, buf_ + len + 1, buf_used_);
|
my_memmove(buf_, buf_ + len + 1, buf_used_);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -27,6 +27,9 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// 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
|
// 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
|
// 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
|
// 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 "client/linux/minidump_writer/linux_dumper.h"
|
||||||
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
#include <link.h>
|
|
||||||
#endif
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.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 "client/linux/minidump_writer/line_reader.h"
|
||||||
#include "common/linux/file_id.h"
|
#include "common/linux/file_id.h"
|
||||||
#include "common/linux/linux_libc_support.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"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||||
static const char kDeletedSuffix[] = " (deleted)";
|
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(
|
inline static bool IsMappedFileOpenUnsafe(
|
||||||
const google_breakpad::MappingInfo& mapping) {
|
const google_breakpad::MappingInfo& mapping) {
|
||||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
// 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 {
|
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),
|
: pid_(pid),
|
||||||
threads_suspended_(false),
|
crash_address_(0),
|
||||||
|
crash_signal_(0),
|
||||||
|
crash_thread_(0),
|
||||||
threads_(&allocator_, 8),
|
threads_(&allocator_, 8),
|
||||||
mappings_(&allocator_) {
|
mappings_(&allocator_),
|
||||||
|
auxv_(&allocator_, AT_MAX + 1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxDumper::~LinuxDumper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LinuxDumper::Init() {
|
bool LinuxDumper::Init() {
|
||||||
return EnumerateThreads(&threads_) &&
|
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||||
int mapping_id,
|
bool member,
|
||||||
|
unsigned int mapping_id,
|
||||||
uint8_t identifier[sizeof(MDGUID)])
|
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));
|
my_memset(identifier, 0, sizeof(MDGUID));
|
||||||
if (IsMappedFileOpenUnsafe(mapping))
|
if (IsMappedFileOpenUnsafe(mapping))
|
||||||
return false;
|
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];
|
char filename[NAME_MAX];
|
||||||
size_t filename_len = my_strlen(mapping.name);
|
size_t filename_len = my_strlen(mapping.name);
|
||||||
assert(filename_len < NAME_MAX);
|
assert(filename_len < NAME_MAX);
|
||||||
if (filename_len >= NAME_MAX)
|
if (filename_len >= NAME_MAX)
|
||||||
return false;
|
return false;
|
||||||
memcpy(filename, mapping.name, filename_len);
|
my_memcpy(filename, mapping.name, filename_len);
|
||||||
filename[filename_len] = '\0';
|
filename[filename_len] = '\0';
|
||||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||||
|
|
||||||
int fd = sys_open(filename, O_RDONLY, 0);
|
MemoryMappedFile mapped_file(filename);
|
||||||
if (fd < 0)
|
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||||
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)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier);
|
bool success =
|
||||||
sys_munmap(base, st.st_size);
|
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||||
if (success && mapping_id != -1 && filename_modified) {
|
if (success && member && filename_modified) {
|
||||||
mappings_[mapping_id]->name[filename_len -
|
mappings_[mapping_id]->name[filename_len -
|
||||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
sizeof(kDeletedSuffix) + 1] = '\0';
|
||||||
}
|
}
|
||||||
@ -247,48 +135,50 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
bool LinuxDumper::ReadAuxv() {
|
||||||
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
|
|
||||||
char auxv_path[NAME_MAX];
|
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);
|
int fd = sys_open(auxv_path, O_RDONLY, 0);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
elf_aux_entry one_aux_entry;
|
elf_aux_entry one_aux_entry;
|
||||||
|
bool res = false;
|
||||||
while (sys_read(fd,
|
while (sys_read(fd,
|
||||||
&one_aux_entry,
|
&one_aux_entry,
|
||||||
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
|
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
|
||||||
one_aux_entry.a_type != AT_NULL) {
|
one_aux_entry.a_type != AT_NULL) {
|
||||||
if (one_aux_entry.a_type == AT_SYSINFO_EHDR) {
|
if (one_aux_entry.a_type <= AT_MAX) {
|
||||||
close(fd);
|
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
|
||||||
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
|
res = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(fd);
|
sys_close(fd);
|
||||||
return NULL;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool LinuxDumper::EnumerateMappings() {
|
||||||
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
|
|
||||||
char maps_path[NAME_MAX];
|
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_loc is the beginning of the kernel's mapping of
|
||||||
// linux-gate.so in the process. It doesn't actually show up in the
|
// 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
|
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
|
||||||
// load location and special case it's entry when creating the list
|
// aux vector entry, which gives the information necessary to special
|
||||||
// of mappings.
|
// case its entry when creating the list of mappings.
|
||||||
const void* linux_gate_loc;
|
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
||||||
linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_);
|
// 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);
|
const int fd = sys_open(maps_path, O_RDONLY, 0);
|
||||||
if (fd < 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,
|
// Merge adjacent mappings with the same name into one module,
|
||||||
// assuming they're a single library mapped by the dynamic linker
|
// assuming they're a single library mapped by the dynamic linker
|
||||||
if (name && result->size()) {
|
if (name && !mappings_.empty()) {
|
||||||
MappingInfo* module = (*result)[result->size() - 1];
|
MappingInfo* module = mappings_.back();
|
||||||
if ((start_addr == module->start_addr + module->size) &&
|
if ((start_addr == module->start_addr + module->size) &&
|
||||||
(my_strlen(name) == my_strlen(module->name)) &&
|
(my_strlen(name) == my_strlen(module->name)) &&
|
||||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
|
(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;
|
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||||
memset(module, 0, sizeof(MappingInfo));
|
my_memset(module, 0, sizeof(MappingInfo));
|
||||||
module->start_addr = start_addr;
|
module->start_addr = start_addr;
|
||||||
module->size = end_addr - start_addr;
|
module->size = end_addr - start_addr;
|
||||||
module->offset = offset;
|
module->offset = offset;
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
const unsigned l = my_strlen(name);
|
const unsigned l = my_strlen(name);
|
||||||
if (l < sizeof(module->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);
|
sys_close(fd);
|
||||||
|
|
||||||
return result->size() > 0;
|
return !mappings_.empty();
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get information about the stack, given the stack pointer. We don't try to
|
// 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;
|
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.
|
// Find the mapping which the given memory address falls in.
|
||||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||||
const uintptr_t addr = (uintptr_t) address;
|
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'.
|
// Check |path| against the /proc/pid/exe 'symlink'.
|
||||||
char exe_link[NAME_MAX];
|
char exe_link[NAME_MAX];
|
||||||
char new_path[NAME_MAX];
|
char new_path[NAME_MAX];
|
||||||
BuildProcPath(exe_link, pid_, "exe");
|
if (!BuildProcPath(exe_link, pid_, "exe"))
|
||||||
ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX);
|
return false;
|
||||||
if (new_path_len <= 0 || new_path_len == NAME_MAX)
|
if (!SafeReadLink(exe_link, new_path))
|
||||||
return false;
|
return false;
|
||||||
new_path[new_path_len] = '\0';
|
|
||||||
if (my_strcmp(path, new_path) != 0)
|
if (my_strcmp(path, new_path) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -548,7 +329,7 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(path, exe_link, NAME_MAX);
|
my_memcpy(path, exe_link, NAME_MAX);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +27,14 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// 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_
|
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||||
|
|
||||||
@ -34,9 +42,7 @@
|
|||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
@ -44,31 +50,18 @@
|
|||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64)
|
#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
|
#endif
|
||||||
|
|
||||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||||
#if defined(__i386) || defined(__ARM_EABI__)
|
#if defined(__i386) || defined(__ARM_EABI__)
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
typedef Elf32_auxv_t elf_aux_entry;
|
typedef Elf32_auxv_t elf_aux_entry;
|
||||||
#else
|
#elif defined(__x86_64)
|
||||||
// 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__)
|
|
||||||
typedef Elf64_auxv_t elf_aux_entry;
|
typedef Elf64_auxv_t elf_aux_entry;
|
||||||
#endif
|
#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
|
// 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.
|
// is the name we use for it when writing it to the minidump.
|
||||||
// This should always be less than NAME_MAX!
|
// This should always be less than NAME_MAX!
|
||||||
@ -79,10 +72,7 @@ struct ThreadInfo {
|
|||||||
pid_t tgid; // thread group id
|
pid_t tgid; // thread group id
|
||||||
pid_t ppid; // parent process
|
pid_t ppid; // parent process
|
||||||
|
|
||||||
// Even on platforms where the stack grows down, the following will point to
|
uintptr_t stack_pointer; // thread stack pointer
|
||||||
// the smallest address in the stack.
|
|
||||||
const void* stack; // pointer to the stack area
|
|
||||||
size_t stack_len; // length of the stack to copy
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64)
|
#if defined(__i386) || defined(__x86_64)
|
||||||
@ -96,12 +86,8 @@ struct ThreadInfo {
|
|||||||
|
|
||||||
#elif defined(__ARM_EABI__)
|
#elif defined(__ARM_EABI__)
|
||||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
// 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_regs regs;
|
||||||
struct user_fpregs fpregs;
|
struct user_fpregs fpregs;
|
||||||
#endif // __ANDROID__
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,21 +104,27 @@ class LinuxDumper {
|
|||||||
public:
|
public:
|
||||||
explicit LinuxDumper(pid_t pid);
|
explicit LinuxDumper(pid_t pid);
|
||||||
|
|
||||||
|
virtual ~LinuxDumper();
|
||||||
|
|
||||||
// Parse the data for |threads| and |mappings|.
|
// 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.
|
// Suspend/resume all threads in the given process.
|
||||||
bool ThreadsSuspend();
|
virtual bool ThreadsSuspend() = 0;
|
||||||
bool ThreadsResume();
|
virtual bool ThreadsResume() = 0;
|
||||||
|
|
||||||
// Read information about the given thread. Returns true on success. One must
|
// Read information about the |index|-th thread of |threads_|.
|
||||||
// have called |ThreadsSuspend| first.
|
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||||
bool ThreadInfoGet(pid_t tid, ThreadInfo* info);
|
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
|
||||||
|
|
||||||
// These are only valid after a call to |Init|.
|
// These are only valid after a call to |Init|.
|
||||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
const wasteful_vector<pid_t> &threads() { return threads_; }
|
||||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
||||||
const MappingInfo* FindMapping(const void* address) const;
|
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.
|
// 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
|
// stack: (output) the lowest address in the memory area
|
||||||
@ -142,30 +134,41 @@ class LinuxDumper {
|
|||||||
|
|
||||||
PageAllocator* allocator() { return &allocator_; }
|
PageAllocator* allocator() { return &allocator_; }
|
||||||
|
|
||||||
// memcpy from a remote process.
|
// Copy content of |length| bytes from a given process |child|,
|
||||||
static void CopyFromProcess(void* dest, pid_t child, const void* src,
|
// starting from |src|, into |dest|.
|
||||||
size_t length);
|
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
|
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||||
// character array that is overwritten, and node is the final node
|
// |path| is a character array of at least NAME_MAX bytes to return the
|
||||||
// without any slashes.
|
// result.|node| is the final node without any slashes. Returns true on
|
||||||
void BuildProcPath(char* path, pid_t pid, const char* node) const;
|
// 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.
|
// 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,
|
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||||
int mapping_id,
|
bool member,
|
||||||
|
unsigned int mapping_id,
|
||||||
uint8_t identifier[sizeof(MDGUID)]);
|
uint8_t identifier[sizeof(MDGUID)]);
|
||||||
|
|
||||||
// Utility method to find the location of where the kernel has
|
uintptr_t crash_address() const { return crash_address_; }
|
||||||
// mapped linux-gate.so in memory(shows up in /proc/pid/maps as
|
void set_crash_address(uintptr_t crash_address) {
|
||||||
// [vdso], but we can't guarantee that it's the only virtual dynamic
|
crash_address_ = crash_address;
|
||||||
// shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR
|
}
|
||||||
// is the safest way to go.)
|
|
||||||
void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const;
|
int crash_signal() const { return crash_signal_; }
|
||||||
private:
|
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
|
||||||
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const;
|
|
||||||
bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
|
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
|
// 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
|
// /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.
|
// Returns true if |path| is modified.
|
||||||
bool HandleDeletedFileInMapping(char* path) const;
|
bool HandleDeletedFileInMapping(char* path) const;
|
||||||
|
|
||||||
|
// ID of the crashed process.
|
||||||
const pid_t pid_;
|
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_;
|
mutable PageAllocator allocator_;
|
||||||
|
|
||||||
bool threads_suspended_;
|
// IDs of all the threads.
|
||||||
wasteful_vector<pid_t> threads_; // the ids of all the threads
|
wasteful_vector<pid_t> threads_;
|
||||||
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
|
|
||||||
|
// Info from /proc/<pid>/maps.
|
||||||
|
wasteful_vector<MappingInfo*> mappings_;
|
||||||
|
|
||||||
|
// Info from /proc/<pid>/auxv
|
||||||
|
wasteful_vector<elf_aux_val_t> auxv_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // 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_
|
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
class ExceptionHandler;
|
||||||
|
|
||||||
|
struct MappingEntry {
|
||||||
|
MappingInfo first;
|
||||||
|
u_int8_t second[sizeof(MDGUID)];
|
||||||
|
};
|
||||||
|
|
||||||
// A list of <MappingInfo, GUID>
|
// A list of <MappingInfo, GUID>
|
||||||
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
|
|
||||||
typedef std::list<MappingEntry> MappingList;
|
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
|
// libc functions which may. Thus, it can be used in contexts where the state
|
||||||
// of the heap may be corrupt.
|
// of the heap may be corrupt.
|
||||||
// filename: the filename to write to. This is opened O_EXCL and fails if
|
// minidump_path: the path to the file to write to. This is opened O_EXCL and
|
||||||
// open fails.
|
// fails open fails.
|
||||||
// crashing_process: the pid of the crashing process. This must be trusted.
|
// 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: a blob of data from the crashing process. See exception_handler.h
|
||||||
// blob_size: the length of |blob|, in bytes
|
// blob_size: the length of |blob|, in bytes
|
||||||
//
|
//
|
||||||
// Returns true iff successful.
|
// 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);
|
const void* blob, size_t blob_size);
|
||||||
|
|
||||||
// This overload also allows passing a list of known mappings.
|
// Alternate form of WriteMinidump() that works with processes that
|
||||||
bool WriteMinidump(const char* filename, pid_t crashing_process,
|
// 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 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
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
@ -35,9 +35,33 @@
|
|||||||
/* End PBXAggregateTarget section */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile 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 */; };
|
3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; };
|
||||||
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
|
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 */; };
|
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 */; };
|
8B3101C611F0CD9F00FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; };
|
||||||
8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||||
8B3101CA11F0CDB000FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.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 */; };
|
8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
||||||
8B3102EB11F0D78000FCF3E4 /* 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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; };
|
||||||
D24641EC12BAC6FB005170D0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.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 */; };
|
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 */; };
|
D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; };
|
||||||
D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
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; };
|
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; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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; };
|
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; };
|
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; };
|
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; };
|
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; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; 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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D246418412BAA4BA005170D0 /* Foundation.framework in Frameworks */,
|
D246418412BAA4BA005170D0 /* Foundation.framework in Frameworks */,
|
||||||
D246419C12BAA65F005170D0 /* libcrypto.dylib in Frameworks */,
|
4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -707,8 +736,8 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */,
|
D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */,
|
||||||
D2F9A540121383A1002747C1 /* libcrypto.dylib in Frameworks */,
|
|
||||||
D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */,
|
D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */,
|
||||||
|
4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -727,7 +756,6 @@
|
|||||||
files = (
|
files = (
|
||||||
8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */,
|
8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */,
|
||||||
F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */,
|
F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */,
|
||||||
8B4BDAAF12012BC5009C7060 /* libcrypto.dylib in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -746,8 +774,8 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */,
|
8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */,
|
||||||
8B4BDABE12012CEF009C7060 /* libcrypto.dylib in Frameworks */,
|
|
||||||
D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */,
|
D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */,
|
||||||
|
4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -763,8 +791,8 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */,
|
8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */,
|
||||||
8B4BDAC512012D05009C7060 /* libcrypto.dylib in Frameworks */,
|
|
||||||
D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */,
|
D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */,
|
||||||
|
4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -833,17 +861,34 @@
|
|||||||
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
|
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
8B4BDAA7120124EA009C7060 /* libcrypto.dylib */,
|
|
||||||
8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */,
|
8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */,
|
||||||
F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */,
|
F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */,
|
||||||
F92C554A0ECCF530009BE4BA /* Carbon.framework */,
|
F92C554A0ECCF530009BE4BA /* Carbon.framework */,
|
||||||
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
|
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */,
|
||||||
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
|
0867D6A5FE840307C02AAC07 /* AppKit.framework */,
|
||||||
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
|
0867D69BFE84028FC02AAC07 /* Foundation.framework */,
|
||||||
|
4DBE4769134A4F080072546A /* CoreServices.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
D244536912426EE7009BBCE0 /* processor */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -890,6 +935,7 @@
|
|||||||
D244540A12439BA0009BBCE0 /* memory_unittest.cc */,
|
D244540A12439BA0009BBCE0 /* memory_unittest.cc */,
|
||||||
F92C53870ECCE6C0009BE4BA /* convert_UTF.c */,
|
F92C53870ECCE6C0009BE4BA /* convert_UTF.c */,
|
||||||
F92C53880ECCE6C0009BE4BA /* convert_UTF.h */,
|
F92C53880ECCE6C0009BE4BA /* convert_UTF.h */,
|
||||||
|
4D72CA0D13DFAD5C006CABE3 /* md5.cc */,
|
||||||
F92C53850ECCE6AD009BE4BA /* string_conversion.cc */,
|
F92C53850ECCE6AD009BE4BA /* string_conversion.cc */,
|
||||||
F92C53860ECCE6AD009BE4BA /* string_conversion.h */,
|
F92C53860ECCE6AD009BE4BA /* string_conversion.h */,
|
||||||
F92C53840ECCE68D009BE4BA /* mac */,
|
F92C53840ECCE68D009BE4BA /* mac */,
|
||||||
@ -900,6 +946,8 @@
|
|||||||
F92C53840ECCE68D009BE4BA /* mac */ = {
|
F92C53840ECCE68D009BE4BA /* mac */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
162F64F0161C577500CD68D5 /* arch_utilities.cc */,
|
||||||
|
162F64F1161C577500CD68D5 /* arch_utilities.h */,
|
||||||
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */,
|
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */,
|
||||||
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */,
|
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */,
|
||||||
F9C77E0F0F7DDF650045F7DB /* testing */,
|
F9C77E0F0F7DDF650045F7DB /* testing */,
|
||||||
@ -911,6 +959,8 @@
|
|||||||
F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */,
|
F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */,
|
||||||
F92C53780ECCE635009BE4BA /* MachIPC.h */,
|
F92C53780ECCE635009BE4BA /* MachIPC.h */,
|
||||||
F92C53790ECCE635009BE4BA /* MachIPC.mm */,
|
F92C53790ECCE635009BE4BA /* MachIPC.mm */,
|
||||||
|
4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */,
|
||||||
|
4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */,
|
||||||
F92C537A0ECCE635009BE4BA /* macho_id.cc */,
|
F92C537A0ECCE635009BE4BA /* macho_id.cc */,
|
||||||
F92C537B0ECCE635009BE4BA /* macho_id.h */,
|
F92C537B0ECCE635009BE4BA /* macho_id.h */,
|
||||||
F92C537C0ECCE635009BE4BA /* macho_utilities.cc */,
|
F92C537C0ECCE635009BE4BA /* macho_utilities.cc */,
|
||||||
@ -928,6 +978,7 @@
|
|||||||
F92C538D0ECCE6F2009BE4BA /* client */ = {
|
F92C538D0ECCE6F2009BE4BA /* client */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
16C7C915147D45AE00776EAD /* apple */,
|
||||||
F92C53990ECCE78E009BE4BA /* mac */,
|
F92C53990ECCE78E009BE4BA /* mac */,
|
||||||
F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */,
|
F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */,
|
||||||
F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */,
|
F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */,
|
||||||
@ -953,6 +1004,8 @@
|
|||||||
F92C53B50ECCE799009BE4BA /* crash_generation */ = {
|
F92C53B50ECCE799009BE4BA /* crash_generation */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
163201D41443019E00C4DBF5 /* ConfigFile.h */,
|
||||||
|
163201D51443019E00C4DBF5 /* ConfigFile.mm */,
|
||||||
D2F9A4C4121336C7002747C1 /* client_info.h */,
|
D2F9A4C4121336C7002747C1 /* client_info.h */,
|
||||||
D2F9A4C5121336C7002747C1 /* crash_generation_client.h */,
|
D2F9A4C5121336C7002747C1 /* crash_generation_client.h */,
|
||||||
D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */,
|
D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */,
|
||||||
@ -980,6 +1033,8 @@
|
|||||||
F92C56A60ECE04B6009BE4BA /* sender */ = {
|
F92C56A60ECE04B6009BE4BA /* sender */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
16E02DB4147410D4008C604D /* uploader.mm */,
|
||||||
|
163202431443201300C4DBF5 /* uploader.h */,
|
||||||
F9B6309F100FF96B00D0F4AC /* goArrow.png */,
|
F9B6309F100FF96B00D0F4AC /* goArrow.png */,
|
||||||
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
|
F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */,
|
||||||
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
|
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
|
||||||
@ -1093,6 +1148,9 @@
|
|||||||
D2F9A4C9121336C7002747C1 /* client_info.h in Headers */,
|
D2F9A4C9121336C7002747C1 /* client_info.h in Headers */,
|
||||||
D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */,
|
D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */,
|
||||||
D2F9A4CC121336C7002747C1 /* crash_generation_server.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;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1337,6 +1395,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
||||||
compatibilityVersion = "Xcode 3.1";
|
compatibilityVersion = "Xcode 3.1";
|
||||||
|
developmentRegion = English;
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
knownRegions = (
|
knownRegions = (
|
||||||
English,
|
English,
|
||||||
@ -1615,6 +1674,8 @@
|
|||||||
F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */,
|
F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */,
|
||||||
D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */,
|
D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */,
|
||||||
D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */,
|
D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */,
|
||||||
|
163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */,
|
||||||
|
162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1637,6 +1698,8 @@
|
|||||||
D246419612BAA55A005170D0 /* macho_id.cc in Sources */,
|
D246419612BAA55A005170D0 /* macho_id.cc in Sources */,
|
||||||
D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */,
|
D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */,
|
||||||
D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */,
|
D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */,
|
||||||
|
4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */,
|
||||||
|
4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1674,6 +1737,8 @@
|
|||||||
D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */,
|
D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */,
|
||||||
D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */,
|
D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */,
|
||||||
D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */,
|
D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */,
|
||||||
|
4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */,
|
||||||
|
4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1683,6 +1748,8 @@
|
|||||||
files = (
|
files = (
|
||||||
F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */,
|
F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */,
|
||||||
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */,
|
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */,
|
||||||
|
163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */,
|
||||||
|
4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1690,6 +1757,8 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */,
|
||||||
|
162F64F5161C579B00CD68D5 /* arch_utilities.h in Sources */,
|
||||||
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */,
|
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */,
|
||||||
F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */,
|
F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */,
|
||||||
F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */,
|
F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */,
|
||||||
@ -1698,11 +1767,13 @@
|
|||||||
F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */,
|
F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */,
|
||||||
F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */,
|
F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */,
|
||||||
F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */,
|
F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */,
|
||||||
|
4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */,
|
||||||
F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */,
|
F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */,
|
||||||
F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */,
|
F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */,
|
||||||
F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */,
|
F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */,
|
||||||
F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */,
|
F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */,
|
||||||
F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */,
|
F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */,
|
||||||
|
4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1713,6 +1784,7 @@
|
|||||||
F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */,
|
F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */,
|
||||||
F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */,
|
F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */,
|
||||||
F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */,
|
F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */,
|
||||||
|
16E02DB8147410F0008C604D /* uploader.mm in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1740,6 +1812,8 @@
|
|||||||
F93803D60F8083B7004D428B /* macho_walker.cc in Sources */,
|
F93803D60F8083B7004D428B /* macho_walker.cc in Sources */,
|
||||||
F93803D70F8083B7004D428B /* string_utilities.cc in Sources */,
|
F93803D70F8083B7004D428B /* string_utilities.cc in Sources */,
|
||||||
D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */,
|
D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */,
|
||||||
|
4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */,
|
||||||
|
4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -1779,6 +1853,8 @@
|
|||||||
F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */,
|
F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */,
|
||||||
D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */,
|
D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */,
|
||||||
D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */,
|
D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */,
|
||||||
|
4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */,
|
||||||
|
4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -2044,9 +2120,6 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
|
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
|
||||||
SDKROOT = macosx10.5;
|
SDKROOT = macosx10.5;
|
||||||
"SDKROOT[arch=i386]" = macosx10.4;
|
|
||||||
"SDKROOT[arch=ppc]" = macosx10.4;
|
|
||||||
"SDKROOT[arch=x86_64]" = macosx10.6;
|
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@ -2080,6 +2153,7 @@
|
|||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
GCC_ENABLE_FIX_AND_CONTINUE = YES;
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
|
HEADER_SEARCH_PATHS = ../..;
|
||||||
INSTALL_PATH = /usr/local/bin;
|
INSTALL_PATH = /usr/local/bin;
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = minidump_generator_test_helper;
|
PRODUCT_NAME = minidump_generator_test_helper;
|
||||||
@ -2094,6 +2168,7 @@
|
|||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||||
GCC_MODEL_TUNING = G5;
|
GCC_MODEL_TUNING = G5;
|
||||||
|
HEADER_SEARCH_PATHS = ../..;
|
||||||
INSTALL_PATH = /usr/local/bin;
|
INSTALL_PATH = /usr/local/bin;
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = minidump_generator_test_helper;
|
PRODUCT_NAME = minidump_generator_test_helper;
|
||||||
@ -2109,7 +2184,7 @@
|
|||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
HEADER_SEARCH_PATHS = (
|
HEADER_SEARCH_PATHS = (
|
||||||
$inherited,
|
"$(inherited)",
|
||||||
../../testing,
|
../../testing,
|
||||||
../../testing/include,
|
../../testing/include,
|
||||||
../../testing/gtest,
|
../../testing/gtest,
|
||||||
@ -2124,6 +2199,13 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = gtest;
|
PRODUCT_NAME = gtest;
|
||||||
};
|
};
|
||||||
@ -2136,6 +2218,13 @@
|
|||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = YES;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
GCC_ENABLE_FIX_AND_CONTINUE = NO;
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
PREBINDING = NO;
|
PREBINDING = NO;
|
||||||
PRODUCT_NAME = gtest;
|
PRODUCT_NAME = gtest;
|
||||||
ZERO_LINK = NO;
|
ZERO_LINK = NO;
|
||||||
@ -2167,24 +2256,36 @@
|
|||||||
D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = {
|
D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\\\"$(SRCROOT)/build/Debug\\\"",
|
"\\\"$(SRCROOT)/build/Debug\\\"",
|
||||||
);
|
);
|
||||||
PRODUCT_NAME = handler_test;
|
PRODUCT_NAME = crash_generation_server_test;
|
||||||
};
|
};
|
||||||
name = "Debug With Code Coverage";
|
name = "Debug With Code Coverage";
|
||||||
};
|
};
|
||||||
D2F9A545121383A1002747C1 /* Release */ = {
|
D2F9A545121383A1002747C1 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\\\"$(SRCROOT)/build/Debug\\\"",
|
"\\\"$(SRCROOT)/build/Debug\\\"",
|
||||||
);
|
);
|
||||||
PRODUCT_NAME = handler_test;
|
PRODUCT_NAME = crash_generation_server_test;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@ -2271,7 +2372,14 @@
|
|||||||
F93803C10F808210004D428B /* Release */ = {
|
F93803C10F808210004D428B /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../..,
|
||||||
|
../../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
PRODUCT_NAME = generator_test;
|
PRODUCT_NAME = generator_test;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@ -2318,7 +2426,14 @@
|
|||||||
F93DE32F0F82C55700608B94 /* Release */ = {
|
F93DE32F0F82C55700608B94 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../../..,
|
||||||
|
../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/build/Debug\"",
|
"\"$(SRCROOT)/build/Debug\"",
|
||||||
@ -2417,7 +2532,14 @@
|
|||||||
F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = {
|
F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../..,
|
||||||
|
../../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
PRODUCT_NAME = generator_test;
|
PRODUCT_NAME = generator_test;
|
||||||
};
|
};
|
||||||
name = "Debug With Code Coverage";
|
name = "Debug With Code Coverage";
|
||||||
@ -2433,7 +2555,14 @@
|
|||||||
F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = {
|
F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
HEADER_SEARCH_PATHS = ../..;
|
HEADER_SEARCH_PATHS = (
|
||||||
|
../../..,
|
||||||
|
../..,
|
||||||
|
../../testing,
|
||||||
|
../../testing/include,
|
||||||
|
../../testing/gtest,
|
||||||
|
../../testing/gtest/include,
|
||||||
|
);
|
||||||
LIBRARY_SEARCH_PATHS = (
|
LIBRARY_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"\"$(SRCROOT)/build/Debug\"",
|
"\"$(SRCROOT)/build/Debug\"",
|
||||||
|
@ -41,6 +41,10 @@
|
|||||||
// modify the default behavior to suit your needs and wants and
|
// modify the default behavior to suit your needs and wants and
|
||||||
// desires.
|
// 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;
|
typedef void *BreakpadRef;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -50,49 +54,7 @@ extern "C" {
|
|||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <Foundation/Foundation.h>
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
// Keys for configuration file
|
#include "BreakpadDefines.h"
|
||||||
#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"
|
|
||||||
|
|
||||||
// Optional user-defined function to dec to decide if we should handle
|
// Optional user-defined function to dec to decide if we should handle
|
||||||
// this crash or forward it along.
|
// this crash or forward it along.
|
||||||
@ -224,13 +186,15 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
|
|||||||
// completeness. They are calculated by Breakpad during initialization &
|
// completeness. They are calculated by Breakpad during initialization &
|
||||||
// crash-dump generation, or entered in by the user.
|
// 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
|
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
|
||||||
// running. This parameter is not set
|
// has been running. This parameter is not
|
||||||
// until the crash-dump-generation phase.
|
// set until the crash-dump-generation phase.
|
||||||
//
|
//
|
||||||
// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
|
// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
|
||||||
// parameter dictionary correspond to log
|
// parameter dictionary correspond to log
|
||||||
|
@ -39,20 +39,19 @@
|
|||||||
#define DEBUGLOG if (gDebugLog) fprintf
|
#define DEBUGLOG if (gDebugLog) fprintf
|
||||||
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||||
|
|
||||||
#import "common/mac/MachIPC.h"
|
#import "client/mac/Framework/Breakpad.h"
|
||||||
#import "common/mac/SimpleStringDictionary.h"
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <sys/stat.h>
|
||||||
|
#import <sys/sysctl.h>
|
||||||
|
|
||||||
#import "client/mac/crash_generation/Inspector.h"
|
#import "client/mac/crash_generation/Inspector.h"
|
||||||
#import "client/mac/handler/exception_handler.h"
|
#import "client/mac/handler/exception_handler.h"
|
||||||
#import "client/mac/Framework/Breakpad.h"
|
#import "client/mac/Framework/Breakpad.h"
|
||||||
#import "client/mac/Framework/OnDemandServer.h"
|
#import "client/mac/Framework/OnDemandServer.h"
|
||||||
#import "client/mac/handler/protected_memory_allocator.h"
|
#import "client/mac/handler/protected_memory_allocator.h"
|
||||||
|
#import "common/mac/MachIPC.h"
|
||||||
#import <sys/stat.h>
|
#import "common/mac/SimpleStringDictionary.h"
|
||||||
#import <sys/sysctl.h>
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
|
|
||||||
using google_breakpad::KeyValueEntry;
|
using google_breakpad::KeyValueEntry;
|
||||||
using google_breakpad::MachPortSender;
|
using google_breakpad::MachPortSender;
|
||||||
|
@ -27,13 +27,12 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#import <iostream>
|
#include <mach/mach.h>
|
||||||
#import <mach/mach.h>
|
#include <servers/bootstrap.h>
|
||||||
#import <servers/bootstrap.h>
|
#include <stdio.h>
|
||||||
#import <stdio.h>
|
#include <stdlib.h>
|
||||||
#import <stdlib.h>
|
#include <sys/stat.h>
|
||||||
#import <sys/stat.h>
|
#include <unistd.h>
|
||||||
#import <unistd.h>
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// class OnDemandServer :
|
// class OnDemandServer :
|
||||||
|
@ -29,11 +29,23 @@
|
|||||||
|
|
||||||
#import "OnDemandServer.h"
|
#import "OnDemandServer.h"
|
||||||
|
|
||||||
|
#import "Breakpad.h"
|
||||||
|
#include "common/mac/bootstrap_compat.h"
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#define PRINT_MACH_RESULT(result_, message_) \
|
#define PRINT_MACH_RESULT(result_, message_) \
|
||||||
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
|
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
|
#else
|
||||||
#define PRINT_MACH_RESULT(result_, message_)
|
#define PRINT_MACH_RESULT(result_, message_)
|
||||||
|
#define PRINT_BOOTSTRAP_RESULT(result_, message_)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@ -67,34 +79,66 @@ kern_return_t OnDemandServer::Initialize(const char *server_command,
|
|||||||
bool unregister_on_cleanup) {
|
bool unregister_on_cleanup) {
|
||||||
unregister_on_cleanup_ = unregister_on_cleanup;
|
unregister_on_cleanup_ = unregister_on_cleanup;
|
||||||
|
|
||||||
kern_return_t kr =
|
mach_port_t self_task = mach_task_self();
|
||||||
bootstrap_create_server(bootstrap_port,
|
|
||||||
const_cast<char*>(server_command),
|
|
||||||
geteuid(), // server uid
|
|
||||||
true,
|
|
||||||
&server_port_);
|
|
||||||
|
|
||||||
|
mach_port_t bootstrap_port;
|
||||||
|
kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port);
|
||||||
if (kr != KERN_SUCCESS) {
|
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;
|
return kr;
|
||||||
}
|
}
|
||||||
|
|
||||||
strlcpy(service_name_, service_name, sizeof(service_name_));
|
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
|
// Create a service called service_name, and return send rights to
|
||||||
// that port in service_port_.
|
// that port in service_port_.
|
||||||
kr = bootstrap_create_service(server_port_,
|
kr = bootstrap_create_service(server_port_,
|
||||||
const_cast<char*>(service_name),
|
const_cast<char*>(service_name),
|
||||||
&service_port_);
|
&service_port_);
|
||||||
|
#pragma clang diagnostic pop
|
||||||
if (kr != KERN_SUCCESS) {
|
if (kr != BOOTSTRAP_SUCCESS) {
|
||||||
PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
|
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): ");
|
||||||
|
|
||||||
// perhaps the service has already been created - try to look it up
|
// perhaps the service has already been created - try to look it up
|
||||||
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
|
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) {
|
if (kr != BOOTSTRAP_SUCCESS) {
|
||||||
PRINT_MACH_RESULT(kr, "bootstrap_look_up() : ");
|
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): ");
|
||||||
Unregister(); // clean up server port
|
Unregister(); // clean up server port
|
||||||
return kr;
|
return kr;
|
||||||
}
|
}
|
||||||
@ -131,9 +175,9 @@ void OnDemandServer::Unregister() {
|
|||||||
|
|
||||||
if (server_port_ != MACH_PORT_NULL) {
|
if (server_port_ != MACH_PORT_NULL) {
|
||||||
// unregister the service
|
// unregister the service
|
||||||
kern_return_t kr = bootstrap_register(server_port_,
|
kern_return_t kr = breakpad::BootstrapRegister(server_port_,
|
||||||
service_name_,
|
service_name_,
|
||||||
MACH_PORT_NULL);
|
MACH_PORT_NULL);
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) {
|
if (kr != KERN_SUCCESS) {
|
||||||
PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");
|
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 "common/mac/SimpleStringDictionary.h"
|
||||||
|
|
||||||
#import <Foundation/Foundation.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;
|
extern bool gDebugLog;
|
||||||
|
|
||||||
@ -78,52 +79,10 @@ using google_breakpad::MinidumpGenerator;
|
|||||||
|
|
||||||
namespace google_breakpad {
|
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 {
|
class MinidumpLocation {
|
||||||
public:
|
public:
|
||||||
MinidumpLocation(const NSString *minidumpDir) {
|
MinidumpLocation(NSString *minidumpDir) {
|
||||||
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
|
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
|
||||||
assert(minidumpDir);
|
assert(minidumpDir);
|
||||||
if (!EnsureDirectoryPathExists(minidumpDir)) {
|
if (!EnsureDirectoryPathExists(minidumpDir)) {
|
||||||
@ -163,6 +122,18 @@ class Inspector {
|
|||||||
void Inspect(const char *receive_port_name);
|
void Inspect(const char *receive_port_name);
|
||||||
|
|
||||||
private:
|
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 ServiceCheckIn(const char *receive_port_name);
|
||||||
kern_return_t ServiceCheckOut(const char *receive_port_name);
|
kern_return_t ServiceCheckOut(const char *receive_port_name);
|
||||||
|
|
||||||
@ -172,7 +143,9 @@ class Inspector {
|
|||||||
kern_return_t SendAcknowledgement();
|
kern_return_t SendAcknowledgement();
|
||||||
void LaunchReporter(const char *inConfigFilePath);
|
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_;
|
mach_port_t service_rcv_port_;
|
||||||
|
|
||||||
|
@ -31,10 +31,10 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <servers/bootstrap.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#import "client/mac/crash_generation/Inspector.h"
|
#import "client/mac/crash_generation/Inspector.h"
|
||||||
|
|
||||||
@ -43,167 +43,22 @@
|
|||||||
|
|
||||||
#import "common/mac/SimpleStringDictionary.h"
|
#import "common/mac/SimpleStringDictionary.h"
|
||||||
#import "common/mac/MachIPC.h"
|
#import "common/mac/MachIPC.h"
|
||||||
|
#include "common/mac/bootstrap_compat.h"
|
||||||
|
|
||||||
#import "GTMDefines.h"
|
#import "GTMDefines.h"
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
#if VERBOSE
|
|
||||||
bool gDebugLog = true;
|
|
||||||
#else
|
|
||||||
bool gDebugLog = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
|
void Inspector::Inspect(const char *receive_port_name) {
|
||||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
kern_return_t result = ResetBootstrapPort();
|
||||||
|
if (result != KERN_SUCCESS) {
|
||||||
// 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));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
has_created_file_ = true;
|
result = ServiceCheckIn(receive_port_name);
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) {
|
if (result == KERN_SUCCESS) {
|
||||||
result = ReadMessages();
|
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) {
|
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
|
||||||
// We need to get the mach port representing this service, so we can
|
// We need to get the mach port representing this service, so we can
|
||||||
// get information from the crashed process.
|
// 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,
|
(char*)receive_port_name,
|
||||||
&service_rcv_port_);
|
&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.
|
// Unregister the service associated with the receive port.
|
||||||
kr = bootstrap_register(bootstrap_port,
|
kr = breakpad::BootstrapRegister(bootstrap_subset_port_,
|
||||||
(char*)receive_port_name,
|
(char*)receive_port_name,
|
||||||
MACH_PORT_NULL);
|
MACH_PORT_NULL);
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) {
|
if (kr != KERN_SUCCESS) {
|
||||||
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
|
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() {
|
bool Inspector::InspectTask() {
|
||||||
// keep the task quiet while we're looking at it
|
// keep the task quiet while we're looking at it
|
||||||
task_suspend(remote_task_);
|
task_suspend(remote_task_);
|
||||||
@ -397,7 +273,6 @@ bool Inspector::InspectTask() {
|
|||||||
const char *minidumpDirectory =
|
const char *minidumpDirectory =
|
||||||
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
|
||||||
|
|
||||||
SetCrashTimeParameters();
|
|
||||||
// If the client app has not specified a minidump directory,
|
// If the client app has not specified a minidump directory,
|
||||||
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
|
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
|
||||||
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
|
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
|
||||||
@ -448,7 +323,8 @@ bool Inspector::InspectTask() {
|
|||||||
[minidumpPath UTF8String]);
|
[minidumpPath UTF8String]);
|
||||||
|
|
||||||
|
|
||||||
config_file_.WriteFile( &config_params_,
|
config_file_.WriteFile( 0,
|
||||||
|
&config_params_,
|
||||||
minidumpLocation.GetPath(),
|
minidumpLocation.GetPath(),
|
||||||
minidumpLocation.GetID());
|
minidumpLocation.GetID());
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ namespace google_breakpad {
|
|||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer(
|
CrashGenerationServer::CrashGenerationServer(
|
||||||
const char *mach_port_name,
|
const char *mach_port_name,
|
||||||
|
FilterCallback filter,
|
||||||
|
void *filter_context,
|
||||||
OnClientDumpRequestCallback dump_callback,
|
OnClientDumpRequestCallback dump_callback,
|
||||||
void *dump_context,
|
void *dump_context,
|
||||||
OnClientExitingCallback exit_callback,
|
OnClientExitingCallback exit_callback,
|
||||||
@ -44,6 +46,8 @@ CrashGenerationServer::CrashGenerationServer(
|
|||||||
bool generate_dumps,
|
bool generate_dumps,
|
||||||
const std::string &dump_path)
|
const std::string &dump_path)
|
||||||
: dump_callback_(dump_callback),
|
: dump_callback_(dump_callback),
|
||||||
|
filter_(filter),
|
||||||
|
filter_context_(filter_context),
|
||||||
dump_context_(dump_context),
|
dump_context_(dump_context),
|
||||||
exit_callback_(exit_callback),
|
exit_callback_(exit_callback),
|
||||||
exit_context_(exit_context),
|
exit_context_(exit_context),
|
||||||
@ -110,7 +114,7 @@ bool CrashGenerationServer::WaitForOneMessage() {
|
|||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
std::string dump_path;
|
std::string dump_path;
|
||||||
if (generate_dumps_) {
|
if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
|
||||||
ScopedTaskSuspend suspend(remote_task);
|
ScopedTaskSuspend suspend(remote_task);
|
||||||
|
|
||||||
MinidumpGenerator generator(remote_task, handler_thread);
|
MinidumpGenerator generator(remote_task, handler_thread);
|
||||||
|
@ -65,10 +65,14 @@ class CrashGenerationServer {
|
|||||||
|
|
||||||
typedef void (*OnClientExitingCallback)(void *context,
|
typedef void (*OnClientExitingCallback)(void *context,
|
||||||
const ClientInfo &client_info);
|
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.
|
// Create an instance with the given parameters.
|
||||||
//
|
//
|
||||||
// mach_port_name: Named server port to listen on.
|
// 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_callback: Callback for a client crash dump request.
|
||||||
// dump_context: Context for client crash dump request callback.
|
// dump_context: Context for client crash dump request callback.
|
||||||
// exit_callback: Callback for client process exit.
|
// exit_callback: Callback for client process exit.
|
||||||
@ -80,6 +84,8 @@ class CrashGenerationServer {
|
|||||||
// dump_path: Path for generating dumps; required only if true is
|
// dump_path: Path for generating dumps; required only if true is
|
||||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
// passed for generateDumps parameter; NULL can be passed otherwise.
|
||||||
CrashGenerationServer(const char *mach_port_name,
|
CrashGenerationServer(const char *mach_port_name,
|
||||||
|
FilterCallback filter,
|
||||||
|
void *filter_context,
|
||||||
OnClientDumpRequestCallback dump_callback,
|
OnClientDumpRequestCallback dump_callback,
|
||||||
void *dump_context,
|
void *dump_context,
|
||||||
OnClientExitingCallback exit_callback,
|
OnClientExitingCallback exit_callback,
|
||||||
@ -109,6 +115,9 @@ class CrashGenerationServer {
|
|||||||
// if a quit message was received or if an error occurred.
|
// if a quit message was received or if an error occurred.
|
||||||
bool WaitForOneMessage();
|
bool WaitForOneMessage();
|
||||||
|
|
||||||
|
FilterCallback filter_;
|
||||||
|
void *filter_context_;
|
||||||
|
|
||||||
OnClientDumpRequestCallback dump_callback_;
|
OnClientDumpRequestCallback dump_callback_;
|
||||||
void *dump_context_;
|
void *dump_context_;
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@
|
|||||||
|
|
||||||
#include "breakpad_nlist_64.h"
|
#include "breakpad_nlist_64.h"
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <mach-o/nlist.h>
|
#include <mach-o/nlist.h>
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
@ -189,25 +190,25 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
|||||||
|
|
||||||
struct exec buf;
|
struct exec buf;
|
||||||
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
|
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
|
||||||
(N_BADMAG(buf) && *((long *)&buf) != magic &&
|
(N_BADMAG(buf) && *((uint32_t *)&buf) != magic &&
|
||||||
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
|
CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
|
||||||
/* The following is the big-endian ppc64 check */
|
/* The following is the big-endian ppc64 check */
|
||||||
(*((long*)&buf)) != FAT_MAGIC) {
|
(*((uint32_t*)&buf)) != FAT_MAGIC)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deal with fat file if necessary */
|
/* Deal with fat file if necessary */
|
||||||
unsigned arch_offset = 0;
|
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 */
|
/* The following is the big-endian ppc64 check */
|
||||||
*((unsigned int *)&buf) == FAT_MAGIC) {
|
*((unsigned int *)&buf) == FAT_MAGIC) {
|
||||||
/* Get host info */
|
/* Get host info */
|
||||||
host_t host = mach_host_self();
|
host_t host = mach_host_self();
|
||||||
unsigned i = HOST_BASIC_INFO_COUNT;
|
unsigned hic = HOST_BASIC_INFO_COUNT;
|
||||||
struct host_basic_info hbi;
|
struct host_basic_info hbi;
|
||||||
kern_return_t kr;
|
kern_return_t kr;
|
||||||
if ((kr = host_info(host, HOST_BASIC_INFO,
|
if ((kr = host_info(host, HOST_BASIC_INFO,
|
||||||
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
|
(host_info_t)(&hbi), &hic)) != KERN_SUCCESS) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
mach_port_deallocate(mach_task_self(), host);
|
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 */
|
/* 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 */
|
/* Read in the fat archs */
|
||||||
struct fat_arch *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,
|
if (read(fd, (char *)fat_archs,
|
||||||
sizeof(struct fat_arch) * fh.nfat_arch) !=
|
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);
|
free(fat_archs);
|
||||||
return -1;
|
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++) {
|
for (unsigned i = 0; i < fh.nfat_arch; i++) {
|
||||||
fat_archs[i].cputype =
|
fat_archs[i].cputype =
|
||||||
NXSwapBigIntToHost(fat_archs[i].cputype);
|
CFSwapInt32BigToHost(fat_archs[i].cputype);
|
||||||
fat_archs[i].cpusubtype =
|
fat_archs[i].cpusubtype =
|
||||||
NXSwapBigIntToHost(fat_archs[i].cpusubtype);
|
CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
|
||||||
fat_archs[i].offset =
|
fat_archs[i].offset =
|
||||||
NXSwapBigIntToHost(fat_archs[i].offset);
|
CFSwapInt32BigToHost(fat_archs[i].offset);
|
||||||
fat_archs[i].size =
|
fat_archs[i].size =
|
||||||
NXSwapBigIntToHost(fat_archs[i].size);
|
CFSwapInt32BigToHost(fat_archs[i].size);
|
||||||
fat_archs[i].align =
|
fat_archs[i].align =
|
||||||
NXSwapBigIntToHost(fat_archs[i].align);
|
CFSwapInt32BigToHost(fat_archs[i].align);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct fat_arch *fap = NULL;
|
struct fat_arch *fap = NULL;
|
||||||
@ -296,7 +297,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
|
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
|
||||||
mh.sizeofcmds) {
|
(ssize_t)mh.sizeofcmds) {
|
||||||
free(load_commands);
|
free(load_commands);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -304,7 +305,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
|
|||||||
struct load_command *lcp = load_commands;
|
struct load_command *lcp = load_commands;
|
||||||
// iterate through all load commands, looking for
|
// iterate through all load commands, looking for
|
||||||
// LC_SYMTAB load command
|
// 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 ||
|
if (lcp->cmdsize % sizeof(word_type) != 0 ||
|
||||||
lcp->cmdsize <= 0 ||
|
lcp->cmdsize <= 0 ||
|
||||||
(char *)lcp + lcp->cmdsize >
|
(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)
|
if (read(fd, (char *)space, m) != m)
|
||||||
break;
|
break;
|
||||||
n -= m;
|
n -= m;
|
||||||
long savpos = lseek(fd, 0, SEEK_CUR);
|
off_t savpos = lseek(fd, 0, SEEK_CUR);
|
||||||
if (savpos == -1) {
|
if (savpos == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -35,16 +35,43 @@ extern "C" { // needed to compile on Leopard
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "breakpad_nlist_64.h"
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <AvailabilityMacros.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <mach/mach_vm.h>
|
#include <mach/task_info.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#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 {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
@ -234,8 +261,8 @@ bool FindTextSection(DynamicImage& image) {
|
|||||||
reinterpret_cast<const mach_segment_command_type *>(cmd);
|
reinterpret_cast<const mach_segment_command_type *>(cmd);
|
||||||
|
|
||||||
if (!strcmp(seg->segname, "__TEXT")) {
|
if (!strcmp(seg->segname, "__TEXT")) {
|
||||||
image.vmaddr_ = seg->vmaddr;
|
image.vmaddr_ = static_cast<mach_vm_address_t>(seg->vmaddr);
|
||||||
image.vmsize_ = seg->vmsize;
|
image.vmsize_ = static_cast<mach_vm_size_t>(seg->vmsize);
|
||||||
image.slide_ = 0;
|
image.slide_ = 0;
|
||||||
|
|
||||||
if (seg->fileoff == 0 && seg->filesize != 0) {
|
if (seg->fileoff == 0 && seg->filesize != 0) {
|
||||||
@ -336,13 +363,49 @@ static uint64_t LookupSymbol(const char* symbol_name,
|
|||||||
return list.n_value;
|
return list.n_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
|
#if TARGET_OS_IPHONE
|
||||||
const char *imageSymbolName = "_dyld_all_image_infos";
|
static bool HasTaskDyldInfo() {
|
||||||
const char *dyldPath = "/usr/lib/dyld";
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static SInt32 GetOSVersionInternal() {
|
||||||
|
SInt32 os_version = 0;
|
||||||
|
Gestalt(gestaltSystemVersion, &os_version);
|
||||||
|
return os_version;
|
||||||
|
}
|
||||||
|
|
||||||
if (Is64Bit())
|
static SInt32 GetOSVersion() {
|
||||||
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_);
|
static SInt32 os_version = GetOSVersionInternal();
|
||||||
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_);
|
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)
|
mach_header_bytes) != KERN_SUCCESS)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
|
|
||||||
|
|
||||||
// Read the file name from the task's memory space.
|
// Read the file name from the task's memory space.
|
||||||
string file_path;
|
string file_path;
|
||||||
if (info.file_path_) {
|
if (info.file_path_) {
|
||||||
@ -429,7 +490,7 @@ void ReadImageInfo(DynamicImages& images,
|
|||||||
header_size,
|
header_size,
|
||||||
info.load_address_,
|
info.load_address_,
|
||||||
file_path,
|
file_path,
|
||||||
info.file_mod_date_,
|
static_cast<uintptr_t>(info.file_mod_date_),
|
||||||
images.task_,
|
images.task_,
|
||||||
images.cpu_type_);
|
images.cpu_type_);
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "mach_vm_compat.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
@ -281,6 +283,8 @@ class DynamicImages {
|
|||||||
return CPU_TYPE_POWERPC;
|
return CPU_TYPE_POWERPC;
|
||||||
#elif defined(__ppc64__)
|
#elif defined(__ppc64__)
|
||||||
return CPU_TYPE_POWERPC64;
|
return CPU_TYPE_POWERPC64;
|
||||||
|
#elif defined(__arm__)
|
||||||
|
return CPU_TYPE_ARM;
|
||||||
#else
|
#else
|
||||||
#error "GetNativeCPUType not implemented for this architecture"
|
#error "GetNativeCPUType not implemented for this architecture"
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,16 +28,25 @@
|
|||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <mach/exc.h>
|
||||||
|
#include <mach/mig.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
#include "client/mac/handler/exception_handler.h"
|
#include "client/mac/handler/exception_handler.h"
|
||||||
#include "client/mac/handler/minidump_generator.h"
|
#include "client/mac/handler/minidump_generator.h"
|
||||||
#include "common/mac/macho_utilities.h"
|
#include "common/mac/macho_utilities.h"
|
||||||
#include "common/mac/scoped_task_suspend-inl.h"
|
#include "common/mac/scoped_task_suspend-inl.h"
|
||||||
|
#include "google_breakpad/common/minidump_exception_mac.h"
|
||||||
|
|
||||||
#ifndef USE_PROTECTED_ALLOCATIONS
|
#ifndef USE_PROTECTED_ALLOCATIONS
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
#define USE_PROTECTED_ALLOCATIONS 1
|
||||||
|
#else
|
||||||
#define USE_PROTECTED_ALLOCATIONS 0
|
#define USE_PROTECTED_ALLOCATIONS 0
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// If USE_PROTECTED_ALLOCATIONS is activated then the
|
// If USE_PROTECTED_ALLOCATIONS is activated then the
|
||||||
// gBreakpadAllocator needs to be setup in other code
|
// gBreakpadAllocator needs to be setup in other code
|
||||||
@ -48,9 +57,15 @@
|
|||||||
extern ProtectedMemoryAllocator *gBreakpadAllocator;
|
extern ProtectedMemoryAllocator *gBreakpadAllocator;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace google_breakpad {
|
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;
|
using std::map;
|
||||||
|
|
||||||
// These structures and techniques are illustrated in
|
// These structures and techniques are illustrated in
|
||||||
@ -87,11 +102,11 @@ struct ExceptionReplyMessage {
|
|||||||
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
|
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
|
||||||
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
|
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
|
// Forward declarations for functions that need "C" style compilation
|
||||||
boolean_t exc_server(mach_msg_header_t *request,
|
boolean_t exc_server(mach_msg_header_t* request,
|
||||||
mach_msg_header_t *reply);
|
mach_msg_header_t* reply);
|
||||||
|
|
||||||
// This symbol must be visible to dlsym() - see
|
// This symbol must be visible to dlsym() - see
|
||||||
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
|
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
|
||||||
@ -102,128 +117,98 @@ extern "C"
|
|||||||
exception_data_t code,
|
exception_data_t code,
|
||||||
mach_msg_type_number_t code_count)
|
mach_msg_type_number_t code_count)
|
||||||
__attribute__((visibility("default")));
|
__attribute__((visibility("default")));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
kern_return_t ForwardException(mach_port_t task,
|
kern_return_t ForwardException(mach_port_t task,
|
||||||
mach_port_t failed_thread,
|
mach_port_t failed_thread,
|
||||||
exception_type_t exception,
|
exception_type_t exception,
|
||||||
exception_data_t code,
|
exception_data_t code,
|
||||||
mach_msg_type_number_t code_count);
|
mach_msg_type_number_t code_count);
|
||||||
|
|
||||||
kern_return_t exception_raise(mach_port_t target_port,
|
#if TARGET_OS_IPHONE
|
||||||
mach_port_t failed_thread,
|
// Implementation is based on the implementation generated by mig.
|
||||||
mach_port_t task,
|
boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP,
|
||||||
exception_type_t exception,
|
mach_msg_header_t* OutHeadP) {
|
||||||
exception_data_t exception_code,
|
OutHeadP->msgh_bits =
|
||||||
mach_msg_type_number_t exception_code_count);
|
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
|
if (InHeadP->msgh_id != 2401) {
|
||||||
exception_raise_state(mach_port_t target_port,
|
((mig_reply_error_t*)OutHeadP)->NDR = NDR_record;
|
||||||
mach_port_t failed_thread,
|
((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID;
|
||||||
mach_port_t task,
|
return FALSE;
|
||||||
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);
|
|
||||||
|
|
||||||
kern_return_t
|
#ifdef __MigPackStructs
|
||||||
exception_raise_state_identity(mach_port_t target_port,
|
#pragma pack(4)
|
||||||
mach_port_t failed_thread,
|
#endif
|
||||||
mach_port_t task,
|
typedef struct {
|
||||||
exception_type_t exception,
|
mach_msg_header_t Head;
|
||||||
exception_data_t exception_code,
|
/* start of the kernel processed data */
|
||||||
mach_msg_type_number_t exception_code_count,
|
mach_msg_body_t msgh_body;
|
||||||
thread_state_flavor_t *target_flavor,
|
mach_msg_port_descriptor_t thread;
|
||||||
thread_state_t in_thread_state,
|
mach_msg_port_descriptor_t task;
|
||||||
mach_msg_type_number_t in_thread_state_count,
|
/* end of the kernel processed data */
|
||||||
thread_state_t out_thread_state,
|
NDR_record_t NDR;
|
||||||
mach_msg_type_number_t *out_thread_state_count);
|
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,
|
typedef struct {
|
||||||
exception_type_t exception,
|
mach_msg_header_t Head;
|
||||||
const exception_data_t code,
|
NDR_record_t NDR;
|
||||||
mach_msg_type_number_t codeCnt,
|
kern_return_t RetCode;
|
||||||
int *flavor,
|
} Reply;
|
||||||
const thread_state_t old_state,
|
#ifdef __MigPackStructs
|
||||||
mach_msg_type_number_t old_stateCnt,
|
#pragma pack()
|
||||||
thread_state_t new_state,
|
#endif
|
||||||
mach_msg_type_number_t *new_stateCnt
|
|
||||||
);
|
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
|
Request* In0P = (Request*)InHeadP;
|
||||||
mach_port_t thread,
|
Reply* OutP = (Reply*)OutHeadP;
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
if (In0P->task.name != mach_task_self()) {
|
||||||
mach_port_t task,
|
return FALSE;
|
||||||
exception_type_t exception,
|
}
|
||||||
exception_data_t code,
|
OutP->RetCode = ForwardException(In0P->task.name,
|
||||||
mach_msg_type_number_t code_count);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback from exc_server()
|
||||||
|
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
|
||||||
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
|
mach_port_t task,
|
||||||
exception_type_t exception,
|
exception_type_t exception,
|
||||||
const exception_data_t code,
|
exception_data_t code,
|
||||||
mach_msg_type_number_t codeCnt,
|
mach_msg_type_number_t code_count) {
|
||||||
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) {
|
|
||||||
|
|
||||||
if (task != mach_task_self()) {
|
if (task != mach_task_self()) {
|
||||||
return KERN_FAILURE;
|
return KERN_FAILURE;
|
||||||
}
|
}
|
||||||
return ForwardException(task, failed_thread, exception, code, code_count);
|
return ForwardException(task, failed_thread, exception, code, code_count);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
ExceptionHandler::ExceptionHandler(const string &dump_path,
|
ExceptionHandler::ExceptionHandler(const string &dump_path,
|
||||||
FilterCallback filter,
|
FilterCallback filter,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void *callback_context,
|
void* callback_context,
|
||||||
bool install_handler,
|
bool install_handler,
|
||||||
const char *port_name)
|
const char* port_name)
|
||||||
: dump_path_(),
|
: dump_path_(),
|
||||||
filter_(filter),
|
filter_(filter),
|
||||||
callback_(callback),
|
callback_(callback),
|
||||||
@ -239,15 +224,17 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
|
|||||||
// This will update to the ID and C-string pointers
|
// This will update to the ID and C-string pointers
|
||||||
set_dump_path(dump_path);
|
set_dump_path(dump_path);
|
||||||
MinidumpGenerator::GatherSystemInformation();
|
MinidumpGenerator::GatherSystemInformation();
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
if (port_name)
|
if (port_name)
|
||||||
crash_generation_client_.reset(new CrashGenerationClient(port_name));
|
crash_generation_client_.reset(new CrashGenerationClient(port_name));
|
||||||
|
#endif
|
||||||
Setup(install_handler);
|
Setup(install_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// special constructor if we want to bypass minidump writing and
|
// special constructor if we want to bypass minidump writing and
|
||||||
// simply get a callback with the exception information
|
// simply get a callback with the exception information
|
||||||
ExceptionHandler::ExceptionHandler(DirectCallback callback,
|
ExceptionHandler::ExceptionHandler(DirectCallback callback,
|
||||||
void *callback_context,
|
void* callback_context,
|
||||||
bool install_handler)
|
bool install_handler)
|
||||||
: dump_path_(),
|
: dump_path_(),
|
||||||
filter_(NULL),
|
filter_(NULL),
|
||||||
@ -299,18 +286,18 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
|
|||||||
bool ExceptionHandler::WriteMinidump(const string &dump_path,
|
bool ExceptionHandler::WriteMinidump(const string &dump_path,
|
||||||
bool write_exception_stream,
|
bool write_exception_stream,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void *callback_context) {
|
void* callback_context) {
|
||||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
|
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
|
||||||
NULL);
|
NULL);
|
||||||
return handler.WriteMinidump(write_exception_stream);
|
return handler.WriteMinidump(write_exception_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
||||||
mach_port_t child_blamed_thread,
|
mach_port_t child_blamed_thread,
|
||||||
const string &dump_path,
|
const string &dump_path,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void *callback_context) {
|
void* callback_context) {
|
||||||
ScopedTaskSuspend suspend(child);
|
ScopedTaskSuspend suspend(child);
|
||||||
|
|
||||||
MinidumpGenerator generator(child, MACH_PORT_NULL);
|
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);
|
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
|
||||||
|
|
||||||
generator.SetExceptionInformation(EXC_BREAKPOINT,
|
generator.SetExceptionInformation(EXC_BREAKPOINT,
|
||||||
#if defined (__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
EXC_I386_BPT,
|
EXC_I386_BPT,
|
||||||
#elif defined (__ppc__) || defined (__ppc64__)
|
#elif defined(__ppc__) || defined(__ppc64__)
|
||||||
EXC_PPC_BREAKPOINT,
|
EXC_PPC_BREAKPOINT,
|
||||||
|
#elif defined(__arm__)
|
||||||
|
EXC_ARM_BREAKPOINT,
|
||||||
#else
|
#else
|
||||||
#error architecture not supported
|
#error architecture not supported
|
||||||
#endif
|
#endif
|
||||||
0,
|
0,
|
||||||
child_blamed_thread);
|
child_blamed_thread);
|
||||||
bool result = generator.Write(dump_filename.c_str());
|
bool result = generator.Write(dump_filename.c_str());
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
return callback(dump_path.c_str(), dump_id.c_str(),
|
return callback(dump_path.c_str(), dump_id.c_str(),
|
||||||
callback_context, result);
|
callback_context, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -339,8 +328,10 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
|
|||||||
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||||
int exception_code,
|
int exception_code,
|
||||||
int exception_subcode,
|
int exception_subcode,
|
||||||
|
ucontext_t* task_context,
|
||||||
mach_port_t thread_name,
|
mach_port_t thread_name,
|
||||||
bool exit_after_write) {
|
bool exit_after_write,
|
||||||
|
bool report_current_thread) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (directCallback_) {
|
if (directCallback_) {
|
||||||
@ -352,25 +343,30 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
|||||||
if (exit_after_write)
|
if (exit_after_write)
|
||||||
_exit(exception_type);
|
_exit(exception_type);
|
||||||
}
|
}
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
} else if (IsOutOfProcess()) {
|
} else if (IsOutOfProcess()) {
|
||||||
if (exception_type && exception_code) {
|
if (exception_type && exception_code) {
|
||||||
// If this is a real exception, give the filter (if any) a chance to
|
// If this is a real exception, give the filter (if any) a chance to
|
||||||
// decide if this should be sent.
|
// decide if this should be sent.
|
||||||
if (filter_ && !filter_(callback_context_))
|
if (filter_ && !filter_(callback_context_))
|
||||||
return false;
|
return false;
|
||||||
return crash_generation_client_->RequestDumpForException(
|
return crash_generation_client_->RequestDumpForException(
|
||||||
exception_type,
|
exception_type,
|
||||||
exception_code,
|
exception_code,
|
||||||
exception_subcode,
|
exception_subcode,
|
||||||
thread_name);
|
thread_name);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
string minidump_id;
|
string minidump_id;
|
||||||
|
|
||||||
// Putting the MinidumpGenerator in its own context will ensure that the
|
// Putting the MinidumpGenerator in its own context will ensure that the
|
||||||
// destructor is executed, closing the newly created minidump file.
|
// destructor is executed, closing the newly created minidump file.
|
||||||
if (!dump_path_.empty()) {
|
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 (exception_type && exception_code) {
|
||||||
// If this is a real exception, give the filter (if any) a chance to
|
// If this is a real exception, give the filter (if any) a chance to
|
||||||
// decide if this should be sent.
|
// 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;
|
current.count = EXC_TYPES_COUNT;
|
||||||
mach_port_t current_task = mach_task_self();
|
mach_port_t current_task = mach_task_self();
|
||||||
kern_return_t result = task_get_exception_ports(current_task,
|
task_get_exception_ports(current_task,
|
||||||
s_exception_mask,
|
s_exception_mask,
|
||||||
current.masks,
|
current.masks,
|
||||||
¤t.count,
|
¤t.count,
|
||||||
current.ports,
|
current.ports,
|
||||||
current.behaviors,
|
current.behaviors,
|
||||||
current.flavors);
|
current.flavors);
|
||||||
|
|
||||||
// Find the first exception handler that matches the exception
|
// Find the first exception handler that matches the exception
|
||||||
unsigned int found;
|
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];
|
mach_port_t target_port = current.ports[found];
|
||||||
exception_behavior_t target_behavior = current.behaviors[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;
|
kern_return_t result;
|
||||||
breakpad_thread_state_data_t thread_state;
|
|
||||||
switch (target_behavior) {
|
switch (target_behavior) {
|
||||||
case EXCEPTION_DEFAULT:
|
case EXCEPTION_DEFAULT:
|
||||||
result = exception_raise(target_port, failed_thread, task, exception,
|
result = exception_raise(target_port, failed_thread, task, exception,
|
||||||
code, code_count);
|
code, code_count);
|
||||||
break;
|
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:
|
default:
|
||||||
fprintf(stderr, "** Unknown exception behavior\n");
|
fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
|
||||||
result = KERN_FAILURE;
|
result = KERN_FAILURE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -484,22 +448,10 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
|||||||
return result;
|
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
|
// static
|
||||||
void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
void* ExceptionHandler::WaitForMessage(void* exception_handler_class) {
|
||||||
ExceptionHandler *self =
|
ExceptionHandler* self =
|
||||||
reinterpret_cast<ExceptionHandler *>(exception_handler_class);
|
reinterpret_cast<ExceptionHandler*>(exception_handler_class);
|
||||||
ExceptionMessage receive;
|
ExceptionMessage receive;
|
||||||
|
|
||||||
// Wait for the exception info
|
// Wait for the exception info
|
||||||
@ -534,7 +486,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
self->SuspendThreads();
|
self->SuspendThreads();
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS
|
#if USE_PROTECTED_ALLOCATIONS
|
||||||
if(gBreakpadAllocator)
|
if (gBreakpadAllocator)
|
||||||
gBreakpadAllocator->Unprotect();
|
gBreakpadAllocator->Unprotect();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -544,10 +496,12 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
|
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
|
||||||
thread = receive.thread.name;
|
thread = receive.thread.name;
|
||||||
exception_type = EXC_BREAKPOINT;
|
exception_type = EXC_BREAKPOINT;
|
||||||
#if defined (__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
exception_code = EXC_I386_BPT;
|
exception_code = EXC_I386_BPT;
|
||||||
#elif defined (__ppc__) || defined (__ppc64__)
|
#elif defined(__ppc__) || defined(__ppc64__)
|
||||||
exception_code = EXC_PPC_BREAKPOINT;
|
exception_code = EXC_PPC_BREAKPOINT;
|
||||||
|
#elif defined(__arm__)
|
||||||
|
exception_code = EXC_ARM_BREAKPOINT;
|
||||||
#else
|
#else
|
||||||
#error architecture not supported
|
#error architecture not supported
|
||||||
#endif
|
#endif
|
||||||
@ -556,11 +510,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
// Write out the dump and save the result for later retrieval
|
// Write out the dump and save the result for later retrieval
|
||||||
self->last_minidump_write_result_ =
|
self->last_minidump_write_result_ =
|
||||||
self->WriteMinidumpWithException(exception_type, exception_code,
|
self->WriteMinidumpWithException(exception_type, exception_code,
|
||||||
0, thread,
|
0, NULL, thread,
|
||||||
false);
|
false, false);
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS
|
#if USE_PROTECTED_ALLOCATIONS
|
||||||
if(gBreakpadAllocator)
|
if (gBreakpadAllocator)
|
||||||
gBreakpadAllocator->Protect();
|
gBreakpadAllocator->Protect();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -575,13 +529,13 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
// exceptions that occur in the parent process are caught and
|
// exceptions that occur in the parent process are caught and
|
||||||
// processed. If the exception was not caused by this task, we
|
// processed. If the exception was not caused by this task, we
|
||||||
// still need to call into the exception server and have it return
|
// 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
|
// to move onto the host exception handler for the child task
|
||||||
if (receive.task.name == mach_task_self()) {
|
if (receive.task.name == mach_task_self()) {
|
||||||
self->SuspendThreads();
|
self->SuspendThreads();
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS
|
#if USE_PROTECTED_ALLOCATIONS
|
||||||
if(gBreakpadAllocator)
|
if (gBreakpadAllocator)
|
||||||
gBreakpadAllocator->Unprotect();
|
gBreakpadAllocator->Unprotect();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -591,26 +545,35 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
|
|
||||||
// Generate the minidump with the exception data.
|
// Generate the minidump with the exception data.
|
||||||
self->WriteMinidumpWithException(receive.exception, receive.code[0],
|
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);
|
self->UninstallHandler(true);
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS
|
#if USE_PROTECTED_ALLOCATIONS
|
||||||
if(gBreakpadAllocator)
|
if (gBreakpadAllocator)
|
||||||
gBreakpadAllocator->Protect();
|
gBreakpadAllocator->Protect();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// Pass along the exception to the server, which will setup the
|
// 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.
|
// code into the reply.
|
||||||
ExceptionReplyMessage reply;
|
ExceptionReplyMessage reply;
|
||||||
if (!exc_server(&receive.header, &reply.header))
|
if (!breakpad_exc_server(&receive.header, &reply.header))
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
// Send a reply and exit
|
// Send a reply and exit
|
||||||
result = mach_msg(&(reply.header), MACH_SEND_MSG,
|
mach_msg(&(reply.header), MACH_SEND_MSG,
|
||||||
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
reply.header.msgh_size, 0, MACH_PORT_NULL,
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -618,7 +581,53 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
|||||||
return NULL;
|
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() {
|
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 {
|
try {
|
||||||
#if USE_PROTECTED_ALLOCATIONS
|
#if USE_PROTECTED_ALLOCATIONS
|
||||||
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
|
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
|
||||||
@ -626,7 +635,6 @@ bool ExceptionHandler::InstallHandler() {
|
|||||||
#else
|
#else
|
||||||
previous_ = new ExceptionParameters();
|
previous_ = new ExceptionParameters();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (std::bad_alloc) {
|
catch (std::bad_alloc) {
|
||||||
return false;
|
return false;
|
||||||
@ -657,6 +665,16 @@ bool ExceptionHandler::InstallHandler() {
|
|||||||
bool ExceptionHandler::UninstallHandler(bool in_exception) {
|
bool ExceptionHandler::UninstallHandler(bool in_exception) {
|
||||||
kern_return_t result = KERN_SUCCESS;
|
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_) {
|
if (installed_exception_handler_) {
|
||||||
mach_port_t current_task = mach_task_self();
|
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;
|
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result == KERN_SUCCESS ? true : false;
|
return result == KERN_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandler::Teardown() {
|
bool ExceptionHandler::Teardown() {
|
||||||
@ -736,7 +754,7 @@ bool ExceptionHandler::Teardown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler_thread_ = NULL;
|
handler_thread_ = NULL;
|
||||||
handler_port_ = NULL;
|
handler_port_ = MACH_PORT_NULL;
|
||||||
pthread_mutex_destroy(&minidump_write_mutex_);
|
pthread_mutex_destroy(&minidump_write_mutex_);
|
||||||
|
|
||||||
return result == KERN_SUCCESS;
|
return result == KERN_SUCCESS;
|
||||||
|
@ -37,12 +37,16 @@
|
|||||||
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
|
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
|
||||||
|
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
|
||||||
#include "processor/scoped_ptr.h"
|
#include "processor/scoped_ptr.h"
|
||||||
|
|
||||||
|
#if !TARGET_OS_IPHONE
|
||||||
|
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
@ -152,7 +156,11 @@ class ExceptionHandler {
|
|||||||
|
|
||||||
// Returns whether out-of-process dump generation is used or not.
|
// Returns whether out-of-process dump generation is used or not.
|
||||||
bool IsOutOfProcess() const {
|
bool IsOutOfProcess() const {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
return crash_generation_client_.get() != NULL;
|
return crash_generation_client_.get() != NULL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -174,17 +182,24 @@ class ExceptionHandler {
|
|||||||
// success, false otherwise.
|
// success, false otherwise.
|
||||||
bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
|
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,
|
bool WriteMinidumpWithException(int exception_type,
|
||||||
int exception_code,
|
int exception_code,
|
||||||
int exception_subcode,
|
int exception_subcode,
|
||||||
|
ucontext_t *task_context,
|
||||||
mach_port_t thread_name,
|
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
|
// When installed, this static function will be call from a newly created
|
||||||
// pthread with |this| as the argument
|
// pthread with |this| as the argument
|
||||||
static void *WaitForMessage(void *exception_handler_class);
|
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=
|
// disallow copy ctor and operator=
|
||||||
explicit ExceptionHandler(const ExceptionHandler &);
|
explicit ExceptionHandler(const ExceptionHandler &);
|
||||||
void operator=(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
|
// True, if we're using the mutext to indicate when mindump writing occurs
|
||||||
bool use_minidump_write_mutex_;
|
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.
|
// Client for out-of-process dump generation.
|
||||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // 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 <cstdio>
|
||||||
|
|
||||||
#include <mach/host_info.h>
|
#include <mach/host_info.h>
|
||||||
#include <mach/i386/thread_status.h>
|
#include <mach/machine.h>
|
||||||
#include <mach/mach_vm.h>
|
|
||||||
#include <mach/ppc/thread_status.h>
|
|
||||||
#include <mach/vm_statistics.h>
|
#include <mach/vm_statistics.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <mach-o/loader.h>
|
#include <mach-o/loader.h>
|
||||||
@ -43,8 +41,20 @@
|
|||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
#include "client/mac/handler/minidump_generator.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 "client/minidump_file_writer-inl.h"
|
||||||
#include "common/mac/file_id.h"
|
#include "common/mac/file_id.h"
|
||||||
|
#include "common/mac/macho_id.h"
|
||||||
#include "common/mac/string_utilities.h"
|
#include "common/mac/string_utilities.h"
|
||||||
|
|
||||||
using MacStringUtils::ConvertToString;
|
using MacStringUtils::ConvertToString;
|
||||||
@ -68,6 +78,7 @@ MinidumpGenerator::MinidumpGenerator()
|
|||||||
crashing_task_(mach_task_self()),
|
crashing_task_(mach_task_self()),
|
||||||
handler_thread_(mach_thread_self()),
|
handler_thread_(mach_thread_self()),
|
||||||
cpu_type_(DynamicImages::GetNativeCPUType()),
|
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||||
|
task_context_(NULL),
|
||||||
dynamic_images_(NULL),
|
dynamic_images_(NULL),
|
||||||
memory_blocks_(&allocator_) {
|
memory_blocks_(&allocator_) {
|
||||||
GatherSystemInformation();
|
GatherSystemInformation();
|
||||||
@ -85,6 +96,7 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
|
|||||||
crashing_task_(crashing_task),
|
crashing_task_(crashing_task),
|
||||||
handler_thread_(handler_thread),
|
handler_thread_(handler_thread),
|
||||||
cpu_type_(DynamicImages::GetNativeCPUType()),
|
cpu_type_(DynamicImages::GetNativeCPUType()),
|
||||||
|
task_context_(NULL),
|
||||||
dynamic_images_(NULL),
|
dynamic_images_(NULL),
|
||||||
memory_blocks_(&allocator_) {
|
memory_blocks_(&allocator_) {
|
||||||
if (crashing_task != mach_task_self()) {
|
if (crashing_task != mach_task_self()) {
|
||||||
@ -126,14 +138,19 @@ void MinidumpGenerator::GatherSystemInformation() {
|
|||||||
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
||||||
&error);
|
&error);
|
||||||
|
|
||||||
if (!data)
|
if (!data) {
|
||||||
|
CFRelease(sys_vers);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
||||||
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
||||||
NULL));
|
NULL));
|
||||||
if (!list)
|
if (!list) {
|
||||||
|
CFRelease(sys_vers);
|
||||||
|
CFRelease(data);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CFStringRef build_version = static_cast<CFStringRef>
|
CFStringRef build_version = static_cast<CFStringRef>
|
||||||
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
||||||
@ -154,6 +171,10 @@ void MinidumpGenerator::GatherSystemInformation() {
|
|||||||
os_build_number_ = IntegerValueAtIndex(product_str, 2);
|
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 MinidumpGenerator::UniqueNameInDirectory(const string &dir,
|
||||||
string *unique_name) {
|
string *unique_name) {
|
||||||
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
CFUUIDRef uuid = CFUUIDCreate(NULL);
|
||||||
@ -251,37 +272,45 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
|
|||||||
kern_return_t result =
|
kern_return_t result =
|
||||||
mach_vm_region_recurse(crashing_task_, &stack_region_base,
|
mach_vm_region_recurse(crashing_task_, &stack_region_base,
|
||||||
&stack_region_size, &nesting_level,
|
&stack_region_size, &nesting_level,
|
||||||
region_info,
|
region_info, &info_count);
|
||||||
&info_count);
|
|
||||||
|
|
||||||
if (start_addr < stack_region_base) {
|
if (result != KERN_SUCCESS || start_addr < stack_region_base) {
|
||||||
// probably stack corruption, since mach_vm_region had to go
|
// Failure or stack corruption, since mach_vm_region had to go
|
||||||
// higher in the process address space to find a valid region.
|
// higher in the process address space to find a valid region.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((cpu_type_ & CPU_ARCH_ABI64) &&
|
unsigned int tag = submap_info.user_tag;
|
||||||
(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.
|
|
||||||
|
|
||||||
// You can see this for yourself by running vmmap on a "hello,
|
// If the user tag is VM_MEMORY_STACK, look for more readable regions with
|
||||||
// world" program
|
// 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_region_size += next_region_size;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result == KERN_SUCCESS ?
|
return stack_region_base + stack_region_size - start_addr;
|
||||||
stack_region_base + stack_region_size - start_addr : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteStackFromStartAddress(
|
bool MinidumpGenerator::WriteStackFromStartAddress(
|
||||||
@ -336,14 +365,22 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
|
|||||||
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location) {
|
MDMemoryDescriptor *stack_location) {
|
||||||
switch (cpu_type_) {
|
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:
|
case CPU_TYPE_POWERPC:
|
||||||
return WriteStackPPC(state, stack_location);
|
return WriteStackPPC(state, stack_location);
|
||||||
case CPU_TYPE_POWERPC64:
|
case CPU_TYPE_POWERPC64:
|
||||||
return WriteStackPPC64(state, stack_location);
|
return WriteStackPPC64(state, stack_location);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
return WriteStackX86(state, stack_location);
|
return WriteStackX86(state, stack_location);
|
||||||
case CPU_TYPE_X86_64:
|
case CPU_TYPE_X86_64:
|
||||||
return WriteStackX86_64(state, stack_location);
|
return WriteStackX86_64(state, stack_location);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -352,14 +389,22 @@ bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
|
|||||||
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
||||||
MDLocationDescriptor *register_location) {
|
MDLocationDescriptor *register_location) {
|
||||||
switch (cpu_type_) {
|
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:
|
case CPU_TYPE_POWERPC:
|
||||||
return WriteContextPPC(state, register_location);
|
return WriteContextPPC(state, register_location);
|
||||||
case CPU_TYPE_POWERPC64:
|
case CPU_TYPE_POWERPC64:
|
||||||
return WriteContextPPC64(state, register_location);
|
return WriteContextPPC64(state, register_location);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
return WriteContextX86(state, register_location);
|
return WriteContextX86(state, register_location);
|
||||||
case CPU_TYPE_X86_64:
|
case CPU_TYPE_X86_64:
|
||||||
return WriteContextX86_64(state, register_location);
|
return WriteContextX86_64(state, register_location);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -368,20 +413,87 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
|
|||||||
u_int64_t MinidumpGenerator::CurrentPCForStack(
|
u_int64_t MinidumpGenerator::CurrentPCForStack(
|
||||||
breakpad_thread_state_data_t state) {
|
breakpad_thread_state_data_t state) {
|
||||||
switch (cpu_type_) {
|
switch (cpu_type_) {
|
||||||
|
#ifdef HAS_ARM_SUPPORT
|
||||||
|
case CPU_TYPE_ARM:
|
||||||
|
return CurrentPCForStackARM(state);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_PPC_SUPPORT
|
||||||
case CPU_TYPE_POWERPC:
|
case CPU_TYPE_POWERPC:
|
||||||
return CurrentPCForStackPPC(state);
|
return CurrentPCForStackPPC(state);
|
||||||
case CPU_TYPE_POWERPC64:
|
case CPU_TYPE_POWERPC64:
|
||||||
return CurrentPCForStackPPC64(state);
|
return CurrentPCForStackPPC64(state);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
return CurrentPCForStackX86(state);
|
return CurrentPCForStackX86(state);
|
||||||
case CPU_TYPE_X86_64:
|
case CPU_TYPE_X86_64:
|
||||||
return CurrentPCForStackX86_64(state);
|
return CurrentPCForStackX86_64(state);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
assert("Unknown CPU type!");
|
assert(0 && "Unknown CPU type!");
|
||||||
return 0;
|
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,
|
bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location) {
|
MDMemoryDescriptor *stack_location) {
|
||||||
ppc_thread_state_t *machine_state =
|
ppc_thread_state_t *machine_state =
|
||||||
@ -539,6 +651,9 @@ bool MinidumpGenerator::WriteContextPPC64(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
|
bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location) {
|
MDMemoryDescriptor *stack_location) {
|
||||||
i386_thread_state_t *machine_state =
|
i386_thread_state_t *machine_state =
|
||||||
@ -607,7 +722,7 @@ bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
|
|||||||
AddReg(eflags);
|
AddReg(eflags);
|
||||||
|
|
||||||
AddReg(eip);
|
AddReg(eip);
|
||||||
#undef AddReg(a)
|
#undef AddReg
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -653,28 +768,51 @@ bool MinidumpGenerator::WriteContextX86_64(
|
|||||||
AddReg(cs);
|
AddReg(cs);
|
||||||
AddReg(fs);
|
AddReg(fs);
|
||||||
AddReg(gs);
|
AddReg(gs);
|
||||||
#undef AddReg(a)
|
#undef AddReg
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
|
bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
|
||||||
thread_state_t state,
|
thread_state_t state,
|
||||||
mach_msg_type_number_t *count) {
|
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;
|
thread_state_flavor_t flavor;
|
||||||
switch (cpu_type_) {
|
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:
|
case CPU_TYPE_POWERPC:
|
||||||
flavor = PPC_THREAD_STATE;
|
flavor = PPC_THREAD_STATE;
|
||||||
break;
|
break;
|
||||||
case CPU_TYPE_POWERPC64:
|
case CPU_TYPE_POWERPC64:
|
||||||
flavor = PPC_THREAD_STATE64;
|
flavor = PPC_THREAD_STATE64;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
flavor = i386_THREAD_STATE;
|
flavor = i386_THREAD_STATE;
|
||||||
break;
|
break;
|
||||||
case CPU_TYPE_X86_64:
|
case CPU_TYPE_X86_64:
|
||||||
flavor = x86_THREAD_STATE64;
|
flavor = x86_THREAD_STATE64;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -760,26 +898,25 @@ bool MinidumpGenerator::WriteMemoryListStream(
|
|||||||
mach_msg_type_number_t stateCount
|
mach_msg_type_number_t stateCount
|
||||||
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
= static_cast<mach_msg_type_number_t>(sizeof(state));
|
||||||
|
|
||||||
if (thread_get_state(exception_thread_,
|
if (GetThreadState(exception_thread_, state, &stateCount)) {
|
||||||
BREAKPAD_MACHINE_THREAD_STATE,
|
|
||||||
state,
|
|
||||||
&stateCount) == KERN_SUCCESS) {
|
|
||||||
u_int64_t ip = CurrentPCForStack(state);
|
u_int64_t ip = CurrentPCForStack(state);
|
||||||
// Bound it to the upper and lower bounds of the region
|
// Bound it to the upper and lower bounds of the region
|
||||||
// it's contained within. If it's not in a known memory region,
|
// it's contained within. If it's not in a known memory region,
|
||||||
// don't bother trying to write it.
|
// 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;
|
mach_vm_size_t size;
|
||||||
natural_t nesting_level = 0;
|
natural_t nesting_level = 0;
|
||||||
vm_region_submap_info_64 info;
|
vm_region_submap_info_64 info;
|
||||||
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
|
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 =
|
kern_return_t ret =
|
||||||
mach_vm_region_recurse(crashing_task_,
|
mach_vm_region_recurse(crashing_task_,
|
||||||
&addr,
|
&addr,
|
||||||
&size,
|
&size,
|
||||||
&nesting_level,
|
&nesting_level,
|
||||||
(vm_region_recurse_info_t)&info,
|
recurse_info,
|
||||||
&info_count);
|
&info_count);
|
||||||
if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
|
if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
|
||||||
// Try to get 128 bytes before and after the IP, but
|
// 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)),
|
std::min(uintptr_t(ip + (kIPMemorySize / 2)),
|
||||||
uintptr_t(addr + size));
|
uintptr_t(addr + size));
|
||||||
ip_memory_d.memory.data_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;
|
have_ip_memory = true;
|
||||||
// This needs to get appended to the list even though
|
// This needs to get appended to the list even though
|
||||||
// the memory bytes aren't filled in yet so the entire
|
// the memory bytes aren't filled in yet so the entire
|
||||||
@ -904,10 +1042,18 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
|||||||
MDRawSystemInfo *info_ptr = info.get();
|
MDRawSystemInfo *info_ptr = info.get();
|
||||||
|
|
||||||
switch (cpu_type_) {
|
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_POWERPC:
|
||||||
case CPU_TYPE_POWERPC64:
|
case CPU_TYPE_POWERPC64:
|
||||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
case CPU_TYPE_I386:
|
case CPU_TYPE_I386:
|
||||||
case CPU_TYPE_X86_64:
|
case CPU_TYPE_X86_64:
|
||||||
if (cpu_type_ == CPU_TYPE_I386)
|
if (cpu_type_ == CPU_TYPE_I386)
|
||||||
@ -971,13 +1117,18 @@ bool MinidumpGenerator::WriteSystemInfoStream(
|
|||||||
|
|
||||||
#endif // __i386__ || __x86_64_
|
#endif // __i386__ || __x86_64_
|
||||||
break;
|
break;
|
||||||
|
#endif // HAS_X86_SUPPORT
|
||||||
default:
|
default:
|
||||||
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
|
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
|
||||||
break;
|
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;
|
info_ptr->platform_id = MD_OS_MAC_OS_X;
|
||||||
|
#endif // TARGET_OS_IPHONE
|
||||||
|
|
||||||
MDLocationDescriptor build_string_loc;
|
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
|
// We'll skip the executable module, because they don't have
|
||||||
// LC_ID_DYLIB load commands, and the crash processing server gets
|
// LC_ID_DYLIB load commands, and the crash processing server gets
|
||||||
// version information from the Plist file, anyway.
|
// 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.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
|
||||||
module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
|
module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
|
||||||
// Convert MAC dylib version format, which is a 32 bit number, to the
|
// 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);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1080,7 +1231,11 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
|
|||||||
module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
|
module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
|
||||||
module->module_name_rva = string_location.rva;
|
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 false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1118,7 +1273,7 @@ int MinidumpGenerator::FindExecutableModule() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
|
bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||||
const char *module_path) {
|
const char *module_path, bool in_memory) {
|
||||||
TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
|
TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
|
||||||
|
|
||||||
// Only return the last path component of the full module path
|
// 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;
|
cv_ptr->age = 0;
|
||||||
|
|
||||||
// Get the module identifier
|
// Get the module identifier
|
||||||
FileID file_id(module_path);
|
|
||||||
unsigned char identifier[16];
|
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)) {
|
if (!result) {
|
||||||
cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
|
FileID file_id(module_path);
|
||||||
(uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
|
result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE,
|
||||||
(uint32_t)identifier[3];
|
identifier);
|
||||||
cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
|
}
|
||||||
cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
|
|
||||||
|
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[0] = identifier[8];
|
||||||
cv_ptr->signature.data4[1] = identifier[9];
|
cv_ptr->signature.data4[1] = identifier[9];
|
||||||
cv_ptr->signature.data4[2] = identifier[10];
|
cv_ptr->signature.data4[2] = identifier[10];
|
||||||
@ -1170,8 +1343,9 @@ bool MinidumpGenerator::WriteModuleListStream(
|
|||||||
MDRawDirectory *module_list_stream) {
|
MDRawDirectory *module_list_stream) {
|
||||||
TypedMDRVA<MDRawModuleList> list(&writer_);
|
TypedMDRVA<MDRawModuleList> list(&writer_);
|
||||||
|
|
||||||
int image_count = dynamic_images_ ?
|
size_t image_count = dynamic_images_ ?
|
||||||
dynamic_images_->GetImageCount() : _dyld_image_count();
|
static_cast<size_t>(dynamic_images_->GetImageCount()) :
|
||||||
|
_dyld_image_count();
|
||||||
|
|
||||||
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
|
if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
|
||||||
return false;
|
return false;
|
||||||
@ -1182,7 +1356,7 @@ bool MinidumpGenerator::WriteModuleListStream(
|
|||||||
|
|
||||||
// Write out the executable module as the first one
|
// Write out the executable module as the first one
|
||||||
MDRawModule module;
|
MDRawModule module;
|
||||||
int executableIndex = FindExecutableModule();
|
size_t executableIndex = FindExecutableModule();
|
||||||
|
|
||||||
if (!WriteModuleStream(executableIndex, &module)) {
|
if (!WriteModuleStream(executableIndex, &module)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1191,7 +1365,7 @@ bool MinidumpGenerator::WriteModuleListStream(
|
|||||||
list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
|
list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
|
||||||
int destinationIndex = 1; // Write all other modules after this one
|
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 (i != executableIndex) {
|
||||||
if (!WriteModuleStream(i, &module)) {
|
if (!WriteModuleStream(i, &module)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1231,21 +1405,14 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
|
|||||||
info_ptr->process_kernel_time =
|
info_ptr->process_kernel_time =
|
||||||
static_cast<u_int32_t>(usage.ru_stime.tv_sec);
|
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]));
|
u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
|
||||||
size_t size;
|
struct kinfo_proc proc;
|
||||||
if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) {
|
size_t size = sizeof(proc);
|
||||||
mach_vm_address_t addr;
|
if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) {
|
||||||
if (mach_vm_allocate(mach_task_self(),
|
info_ptr->process_create_time =
|
||||||
&addr,
|
static_cast<u_int32_t>(proc.kp_proc.p_starttime.tv_sec);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speed
|
// Speed
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
|
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
|
||||||
|
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -42,17 +43,24 @@
|
|||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
#include "dynamic_images.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 {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using std::string;
|
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
|
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
|
||||||
// breakpad_thread_state_t structure.
|
// 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
|
// 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
|
// 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
|
// the SDK we're compiling against so we just toggle on the same preprocessor
|
||||||
@ -74,7 +82,7 @@ class MinidumpGenerator {
|
|||||||
MinidumpGenerator();
|
MinidumpGenerator();
|
||||||
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
|
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
|
||||||
|
|
||||||
~MinidumpGenerator();
|
virtual ~MinidumpGenerator();
|
||||||
|
|
||||||
// Return <dir>/<unique_name>.dmp
|
// Return <dir>/<unique_name>.dmp
|
||||||
// Sets |unique_name| (if requested) to the unique name for the minidump
|
// Sets |unique_name| (if requested) to the unique name for the minidump
|
||||||
@ -94,17 +102,28 @@ class MinidumpGenerator {
|
|||||||
exception_thread_ = thread_name;
|
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
|
// Gather system information. This should be call at least once before using
|
||||||
// the MinidumpGenerator class.
|
// the MinidumpGenerator class.
|
||||||
static void GatherSystemInformation();
|
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:
|
private:
|
||||||
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
|
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
|
||||||
|
|
||||||
// Stream writers
|
// Stream writers
|
||||||
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
|
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
|
||||||
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
|
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
|
||||||
bool WriteExceptionStream(MDRawDirectory *exception_stream);
|
|
||||||
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
|
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
|
||||||
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
|
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
|
||||||
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
|
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
|
||||||
@ -120,14 +139,21 @@ class MinidumpGenerator {
|
|||||||
MDMemoryDescriptor *stack_location);
|
MDMemoryDescriptor *stack_location);
|
||||||
bool WriteContext(breakpad_thread_state_data_t state,
|
bool WriteContext(breakpad_thread_state_data_t state,
|
||||||
MDLocationDescriptor *register_location);
|
MDLocationDescriptor *register_location);
|
||||||
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
|
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
||||||
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
const char *module_path, bool in_memory);
|
||||||
const char *module_path);
|
|
||||||
bool WriteModuleStream(unsigned int index, MDRawModule *module);
|
bool WriteModuleStream(unsigned int index, MDRawModule *module);
|
||||||
size_t CalculateStackSize(mach_vm_address_t start_addr);
|
size_t CalculateStackSize(mach_vm_address_t start_addr);
|
||||||
int FindExecutableModule();
|
int FindExecutableModule();
|
||||||
|
|
||||||
// Per-CPU implementations of these methods
|
// 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,
|
bool WriteStackPPC(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location);
|
MDMemoryDescriptor *stack_location);
|
||||||
bool WriteContextPPC(breakpad_thread_state_data_t state,
|
bool WriteContextPPC(breakpad_thread_state_data_t state,
|
||||||
@ -138,6 +164,8 @@ class MinidumpGenerator {
|
|||||||
bool WriteContextPPC64(breakpad_thread_state_data_t state,
|
bool WriteContextPPC64(breakpad_thread_state_data_t state,
|
||||||
MDLocationDescriptor *register_location);
|
MDLocationDescriptor *register_location);
|
||||||
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
|
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
|
||||||
|
#endif
|
||||||
|
#ifdef HAS_X86_SUPPORT
|
||||||
bool WriteStackX86(breakpad_thread_state_data_t state,
|
bool WriteStackX86(breakpad_thread_state_data_t state,
|
||||||
MDMemoryDescriptor *stack_location);
|
MDMemoryDescriptor *stack_location);
|
||||||
bool WriteContextX86(breakpad_thread_state_data_t state,
|
bool WriteContextX86(breakpad_thread_state_data_t state,
|
||||||
@ -148,14 +176,17 @@ class MinidumpGenerator {
|
|||||||
bool WriteContextX86_64(breakpad_thread_state_data_t state,
|
bool WriteContextX86_64(breakpad_thread_state_data_t state,
|
||||||
MDLocationDescriptor *register_location);
|
MDLocationDescriptor *register_location);
|
||||||
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
|
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
|
||||||
|
#endif
|
||||||
|
|
||||||
// disallow copy ctor and operator=
|
// disallow copy ctor and operator=
|
||||||
explicit MinidumpGenerator(const MinidumpGenerator &);
|
explicit MinidumpGenerator(const MinidumpGenerator &);
|
||||||
void operator=(const MinidumpGenerator &);
|
void operator=(const MinidumpGenerator &);
|
||||||
|
|
||||||
|
protected:
|
||||||
// Use this writer to put the data to disk
|
// Use this writer to put the data to disk
|
||||||
MinidumpFileWriter writer_;
|
MinidumpFileWriter writer_;
|
||||||
|
|
||||||
|
private:
|
||||||
// Exception information
|
// Exception information
|
||||||
int exception_type_;
|
int exception_type_;
|
||||||
int exception_code_;
|
int exception_code_;
|
||||||
@ -172,7 +203,10 @@ class MinidumpGenerator {
|
|||||||
static int os_major_version_;
|
static int os_major_version_;
|
||||||
static int os_minor_version_;
|
static int os_minor_version_;
|
||||||
static int os_build_number_;
|
static int os_build_number_;
|
||||||
|
|
||||||
|
// Context of the task to dump.
|
||||||
|
ucontext_t *task_context_;
|
||||||
|
|
||||||
// Information about dynamically loaded code
|
// Information about dynamically loaded code
|
||||||
DynamicImages *dynamic_images_;
|
DynamicImages *dynamic_images_;
|
||||||
|
|
||||||
@ -180,6 +214,7 @@ class MinidumpGenerator {
|
|||||||
// directly from the system, even while handling an exception.
|
// directly from the system, even while handling an exception.
|
||||||
mutable PageAllocator allocator_;
|
mutable PageAllocator allocator_;
|
||||||
|
|
||||||
|
protected:
|
||||||
// Blocks of memory written to the dump. These are all currently
|
// Blocks of memory written to the dump. These are all currently
|
||||||
// written while writing the thread list stream, but saved here
|
// written while writing the thread list stream, but saved here
|
||||||
// so a memory list stream can be written afterwards.
|
// so a memory list stream can be written afterwards.
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
|
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
|
||||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
|
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
|
||||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.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 */; };
|
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
|
||||||
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.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 */; };
|
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; };
|
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; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */,
|
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
|
||||||
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
|
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
|
||||||
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
|
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
|
||||||
F9721FA80E8B0E4800D7E813 /* md5.c */,
|
F9721FA80E8B0E4800D7E813 /* md5.cc */,
|
||||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
||||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
||||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
|
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
|
||||||
@ -451,7 +451,14 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
|
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
|
||||||
compatibilityVersion = "Xcode 3.2";
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
hasScannedForEncodings = 1;
|
hasScannedForEncodings = 1;
|
||||||
|
knownRegions = (
|
||||||
|
English,
|
||||||
|
Japanese,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
);
|
||||||
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
|
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
@ -594,7 +601,7 @@
|
|||||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
|
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
|
||||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
|
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
|
||||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
|
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
|
||||||
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */,
|
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */,
|
||||||
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
|
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
|
||||||
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
|
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
|
||||||
F93A887F0E8B4C8C0026AF89 /* macho_utilities.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
|
// pick test2 as a symbol we know to be valid to read
|
||||||
// anything will work, really
|
// anything will work, really
|
||||||
void *addr = reinterpret_cast<void*>(&test2);
|
void *addr = reinterpret_cast<void*>(&test2);
|
||||||
void *buf;
|
std::vector<uint8_t> buf(getpagesize());
|
||||||
|
|
||||||
fprintf(stderr, "reading 0x%p\n", addr);
|
fprintf(stderr, "reading 0x%p\n", addr);
|
||||||
buf = google_breakpad::ReadTaskMemory(mach_task_self(),
|
kr = google_breakpad::ReadTaskMemory(mach_task_self(),
|
||||||
addr,
|
(uint64_t)addr,
|
||||||
getpagesize(),
|
getpagesize(),
|
||||||
&kr);
|
buf);
|
||||||
|
|
||||||
CPTAssert(kr == KERN_SUCCESS);
|
CPTAssert(kr == KERN_SUCCESS);
|
||||||
|
|
||||||
CPTAssert(buf != NULL);
|
CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize()));
|
||||||
|
|
||||||
CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize()));
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
|
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
|
||||||
@ -79,7 +75,5 @@ void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
|
|||||||
|
|
||||||
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
|
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
|
||||||
|
|
||||||
d->TestPrint();
|
|
||||||
|
|
||||||
CPTAssert(d->GetImageCount() > 0);
|
CPTAssert(d->GetImageCount() > 0);
|
||||||
}
|
}
|
||||||
|
@ -32,17 +32,11 @@
|
|||||||
// It will perform throttling based on the parameters passed to it and will
|
// It will perform throttling based on the parameters passed to it and will
|
||||||
// prompt the user to send the minidump.
|
// 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"
|
#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
|
// We're sublcassing NSTextField in order to override a particular
|
||||||
// method (see the implementation) that lets us reject changes if they
|
// method (see the implementation) that lets us reject changes if they
|
||||||
// are longer than a particular length. Bindings would normally solve
|
// are longer than a particular length. Bindings would normally solve
|
||||||
@ -87,29 +81,12 @@ extern NSString *const kDefaultServerType;
|
|||||||
NSString *countdownMessage_; // Message indicating time
|
NSString *countdownMessage_; // Message indicating time
|
||||||
// left for input.
|
// left for input.
|
||||||
@private
|
@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
|
NSTimeInterval remainingDialogTime_; // Keeps track of how long
|
||||||
// we have until we cancel
|
// we have until we cancel
|
||||||
// the dialog
|
// the dialog
|
||||||
NSTimer *messageTimer_; // Timer we use to update
|
NSTimer *messageTimer_; // Timer we use to update
|
||||||
// the dialog
|
// the dialog
|
||||||
NSMutableDictionary *serverDictionary_; // The dictionary mapping a
|
Uploader* uploader_; // Uploader we use to send the data.
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
|
// 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
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#import <pwd.h>
|
#import "client/mac/sender/crash_report_sender.h"
|
||||||
#import <sys/stat.h>
|
|
||||||
#import <unistd.h>
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <pwd.h>
|
||||||
|
#import <sys/stat.h>
|
||||||
#import <SystemConfiguration/SystemConfiguration.h>
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
|
#import <unistd.h>
|
||||||
|
|
||||||
#import "common/mac/HTTPMultipartUpload.h"
|
#import "client/apple/Framework/BreakpadDefines.h"
|
||||||
|
|
||||||
#import "crash_report_sender.h"
|
|
||||||
#import "common/mac/GTMLogger.h"
|
#import "common/mac/GTMLogger.h"
|
||||||
|
#import "common/mac/HTTPMultipartUpload.h"
|
||||||
|
|
||||||
|
|
||||||
#define kLastSubmission @"LastSubmission"
|
#define kLastSubmission @"LastSubmission"
|
||||||
const int kMinidumpFileLengthLimit = 800000;
|
|
||||||
const int kUserCommentsMaxLength = 1500;
|
const int kUserCommentsMaxLength = 1500;
|
||||||
const int kEmailMaxLength = 64;
|
const int kEmailMaxLength = 64;
|
||||||
|
|
||||||
#define kApplePrefsSyncExcludeAllKey \
|
#define kApplePrefsSyncExcludeAllKey \
|
||||||
@"com.apple.PreferenceSync.ExcludeAllSyncKeys"
|
@"com.apple.PreferenceSync.ExcludeAllSyncKeys"
|
||||||
|
|
||||||
NSString *const kGoogleServerType = @"google";
|
|
||||||
NSString *const kSocorroServerType = @"socorro";
|
|
||||||
NSString *const kDefaultServerType = @"google";
|
|
||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
@interface NSView (ResizabilityExtentions)
|
@interface NSView (ResizabilityExtentions)
|
||||||
@ -160,18 +155,8 @@ NSString *const kDefaultServerType = @"google";
|
|||||||
|
|
||||||
#pragma mark -
|
#pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@interface Reporter(PrivateMethods)
|
@interface Reporter(PrivateMethods)
|
||||||
+ (uid_t)consoleUID;
|
- (id)initWithConfigFile:(const char *)configFile;
|
||||||
|
|
||||||
- (id)initWithConfigurationFD:(int)fd;
|
|
||||||
|
|
||||||
- (NSString *)readString;
|
|
||||||
- (NSData *)readData:(ssize_t)length;
|
|
||||||
|
|
||||||
- (BOOL)readConfigurationData;
|
|
||||||
- (BOOL)readMinidumpData;
|
|
||||||
- (BOOL)readLogFileData;
|
|
||||||
|
|
||||||
// Returns YES if it has been long enough since the last report that we should
|
// Returns YES if it has been long enough since the last report that we should
|
||||||
// submit a report for this crash.
|
// submit a report for this crash.
|
||||||
@ -221,30 +206,6 @@ NSString *const kDefaultServerType = @"google";
|
|||||||
- (NSInteger)runModalWindow:(NSWindow*)window
|
- (NSInteger)runModalWindow:(NSWindow*)window
|
||||||
withTimeout:(NSTimeInterval)timeout;
|
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
|
// This method is used to periodically update the UI with how many
|
||||||
// seconds are left in the dialog display.
|
// seconds are left in the dialog display.
|
||||||
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
|
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
|
||||||
@ -255,291 +216,24 @@ NSString *const kDefaultServerType = @"google";
|
|||||||
// in their comments/email.
|
// in their comments/email.
|
||||||
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
|
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
|
||||||
|
|
||||||
// Records the uploaded crash ID to the log file.
|
- (void)report;
|
||||||
- (void)logUploadWithID:(const char *)uploadID;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation Reporter
|
@implementation Reporter
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
+ (uid_t)consoleUID {
|
- (id)initWithConfigFile:(const char *)configFile {
|
||||||
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 {
|
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
configFile_ = fd;
|
|
||||||
remainingDialogTime_ = 0;
|
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;
|
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 {
|
- (BOOL)askUserPermissionToSend {
|
||||||
// Initialize Cocoa, needed to display the alert
|
// Initialize Cocoa, needed to display the alert
|
||||||
@ -560,12 +254,14 @@ NSString *const kDefaultServerType = @"google";
|
|||||||
|
|
||||||
buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
|
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]) {
|
if ([self commentsValue]) {
|
||||||
[parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS];
|
[[uploader_ parameters] setObject:[self commentsValue]
|
||||||
|
forKey:@BREAKPAD_COMMENTS];
|
||||||
}
|
}
|
||||||
if ([self emailValue]) {
|
if ([self emailValue]) {
|
||||||
[parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL];
|
[[uploader_ parameters] setObject:[self emailValue]
|
||||||
|
forKey:@BREAKPAD_EMAIL];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create an alert panel to tell the user something happened
|
// Create an alert panel to tell the user something happened
|
||||||
@ -804,9 +500,9 @@ doCommandBySelector:(SEL)commandSelector {
|
|||||||
#pragma mark -
|
#pragma mark -
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
- (BOOL)reportIntervalElapsed {
|
- (BOOL)reportIntervalElapsed {
|
||||||
float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL]
|
float interval = [[[uploader_ parameters]
|
||||||
floatValue];
|
objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue];
|
||||||
NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
|
NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
|
||||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||||
NSMutableDictionary *programDict =
|
NSMutableDictionary *programDict =
|
||||||
[NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
|
[NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
|
||||||
@ -831,29 +527,30 @@ doCommandBySelector:(SEL)commandSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isOnDemand {
|
- (BOOL)isOnDemand {
|
||||||
return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND]
|
return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND]
|
||||||
isEqualToString:@"YES"];
|
isEqualToString:@"YES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldSubmitSilently {
|
- (BOOL)shouldSubmitSilently {
|
||||||
return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM]
|
return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM]
|
||||||
isEqualToString:@"YES"];
|
isEqualToString:@"YES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldRequestComments {
|
- (BOOL)shouldRequestComments {
|
||||||
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS]
|
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS]
|
||||||
isEqualToString:@"YES"];
|
isEqualToString:@"YES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldRequestEmail {
|
- (BOOL)shouldRequestEmail {
|
||||||
return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL]
|
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL]
|
||||||
isEqualToString:@"YES"];
|
isEqualToString:@"YES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)shortDialogMessage {
|
- (NSString*)shortDialogMessage {
|
||||||
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
NSString *displayName =
|
||||||
|
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||||
if (![displayName length])
|
if (![displayName length])
|
||||||
displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT];
|
displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
|
||||||
|
|
||||||
if ([self isOnDemand]) {
|
if ([self isOnDemand]) {
|
||||||
return [NSString
|
return [NSString
|
||||||
@ -867,11 +564,12 @@ doCommandBySelector:(SEL)commandSelector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*)explanatoryDialogText {
|
- (NSString*)explanatoryDialogText {
|
||||||
NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
NSString *displayName =
|
||||||
|
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||||
if (![displayName length])
|
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])
|
if (![vendor length])
|
||||||
vendor = @"unknown vendor";
|
vendor = @"unknown vendor";
|
||||||
|
|
||||||
@ -888,8 +586,8 @@ doCommandBySelector:(SEL)commandSelector {
|
|||||||
|
|
||||||
- (NSTimeInterval)messageTimeout {
|
- (NSTimeInterval)messageTimeout {
|
||||||
// Get the timeout value for the notification.
|
// Get the timeout value for the notification.
|
||||||
NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]
|
NSTimeInterval timeout = [[[uploader_ parameters]
|
||||||
floatValue];
|
objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue];
|
||||||
// Require a timeout of at least a minute (except 0, which means no timeout).
|
// Require a timeout of at least a minute (except 0, which means no timeout).
|
||||||
if (timeout > 0.001 && timeout < 60.0) {
|
if (timeout > 0.001 && timeout < 60.0) {
|
||||||
timeout = 60.0;
|
timeout = 60.0;
|
||||||
@ -897,194 +595,13 @@ doCommandBySelector:(SEL)commandSelector {
|
|||||||
return timeout;
|
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 {
|
- (void)report {
|
||||||
NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]];
|
[uploader_ report];
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[parameters_ release];
|
[uploader_ release];
|
||||||
[minidumpContents_ release];
|
|
||||||
[logFileData_ release];
|
|
||||||
[googleDictionary_ release];
|
|
||||||
[socorroDictionary_ release];
|
|
||||||
[serverDictionary_ release];
|
|
||||||
[extraServerVars_ release];
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1175,40 +692,12 @@ int main(int argc, const char *argv[]) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the file before (potentially) switching to console user
|
Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]];
|
||||||
int configFile = open(argv[1], O_RDONLY, 0600);
|
if (!reporter) {
|
||||||
|
GTMLoggerDebug(@"reporter initialization failed");
|
||||||
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));
|
|
||||||
exit(1);
|
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
|
// only submit a report if we have not recently crashed in the past
|
||||||
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
|
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
|
||||||
BOOL okayToSend = NO;
|
BOOL okayToSend = NO;
|
||||||
|
@ -43,8 +43,8 @@
|
|||||||
#include "client/mac/crash_generation/crash_generation_client.h"
|
#include "client/mac/crash_generation/crash_generation_client.h"
|
||||||
#include "client/mac/crash_generation/crash_generation_server.h"
|
#include "client/mac/crash_generation/crash_generation_server.h"
|
||||||
#include "client/mac/handler/exception_handler.h"
|
#include "client/mac/handler/exception_handler.h"
|
||||||
#include "client/mac/tests/auto_tempdir.h"
|
|
||||||
#include "client/mac/tests/spawn_child_process.h"
|
#include "client/mac/tests/spawn_child_process.h"
|
||||||
|
#include "common/tests/auto_tempdir.h"
|
||||||
#include "google_breakpad/processor/minidump.h"
|
#include "google_breakpad/processor/minidump.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -84,12 +84,14 @@ public:
|
|||||||
AutoTempDir temp_dir;
|
AutoTempDir temp_dir;
|
||||||
// Counter just to ensure that we don't hit the same port again
|
// Counter just to ensure that we don't hit the same port again
|
||||||
static int i;
|
static int i;
|
||||||
|
bool filter_callback_called;
|
||||||
|
|
||||||
void SetUp() {
|
void SetUp() {
|
||||||
sprintf(mach_port_name,
|
sprintf(mach_port_name,
|
||||||
"com.google.breakpad.ServerTest.%d.%d", getpid(),
|
"com.google.breakpad.ServerTest.%d.%d", getpid(),
|
||||||
CrashGenerationServerTest::i++);
|
CrashGenerationServerTest::i++);
|
||||||
child_pid = (pid_t)-1;
|
child_pid = (pid_t)-1;
|
||||||
|
filter_callback_called = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
int CrashGenerationServerTest::i = 0;
|
int CrashGenerationServerTest::i = 0;
|
||||||
@ -97,12 +99,14 @@ int CrashGenerationServerTest::i = 0;
|
|||||||
// Test that starting and stopping a server works
|
// Test that starting and stopping a server works
|
||||||
TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
||||||
CrashGenerationServer server(mach_port_name,
|
CrashGenerationServer server(mach_port_name,
|
||||||
NULL, // dump callback
|
NULL, // filter callback
|
||||||
NULL, // dump context
|
NULL, // filter context
|
||||||
NULL, // exit callback
|
NULL, // dump callback
|
||||||
NULL, // exit context
|
NULL, // dump context
|
||||||
false, // generate dumps
|
NULL, // exit callback
|
||||||
""); // dump path
|
NULL, // exit context
|
||||||
|
false, // generate dumps
|
||||||
|
""); // dump path
|
||||||
ASSERT_TRUE(server.Start());
|
ASSERT_TRUE(server.Start());
|
||||||
ASSERT_TRUE(server.Stop());
|
ASSERT_TRUE(server.Stop());
|
||||||
}
|
}
|
||||||
@ -111,12 +115,14 @@ TEST_F(CrashGenerationServerTest, testStartStopServer) {
|
|||||||
// Test without actually dumping
|
// Test without actually dumping
|
||||||
TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
||||||
CrashGenerationServer server(mach_port_name,
|
CrashGenerationServer server(mach_port_name,
|
||||||
NULL, // dump callback
|
NULL, // filter callback
|
||||||
NULL, // dump context
|
NULL, // filter context
|
||||||
NULL, // exit callback
|
NULL, // dump callback
|
||||||
NULL, // exit context
|
NULL, // dump context
|
||||||
false, // don't generate dumps
|
NULL, // exit callback
|
||||||
temp_dir.path); // dump path
|
NULL, // exit context
|
||||||
|
false, // don't generate dumps
|
||||||
|
temp_dir.path()); // dump path
|
||||||
ASSERT_TRUE(server.Start());
|
ASSERT_TRUE(server.Start());
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@ -133,7 +139,7 @@ TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
|||||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||||
EXPECT_TRUE(server.Stop());
|
EXPECT_TRUE(server.Stop());
|
||||||
// check that no minidump was written
|
// check that no minidump was written
|
||||||
string pattern = temp_dir.path + "/*";
|
string pattern = temp_dir.path() + "/*";
|
||||||
glob_t dirContents;
|
glob_t dirContents;
|
||||||
ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
|
ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents);
|
||||||
EXPECT_EQ(GLOB_NOMATCH, ret);
|
EXPECT_EQ(GLOB_NOMATCH, ret);
|
||||||
@ -142,7 +148,7 @@ TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dumpCallback(void *context, const ClientInfo &client_info,
|
void dumpCallback(void *context, const ClientInfo &client_info,
|
||||||
const std::string &file_path) {
|
const std::string &file_path) {
|
||||||
if (context) {
|
if (context) {
|
||||||
CrashGenerationServerTest* self =
|
CrashGenerationServerTest* self =
|
||||||
reinterpret_cast<CrashGenerationServerTest*>(context);
|
reinterpret_cast<CrashGenerationServerTest*>(context);
|
||||||
@ -161,12 +167,14 @@ void *RequestDump(void *context) {
|
|||||||
// Test that actually writing a minidump works
|
// Test that actually writing a minidump works
|
||||||
TEST_F(CrashGenerationServerTest, testRequestDump) {
|
TEST_F(CrashGenerationServerTest, testRequestDump) {
|
||||||
CrashGenerationServer server(mach_port_name,
|
CrashGenerationServer server(mach_port_name,
|
||||||
dumpCallback, // dump callback
|
NULL, // filter callback
|
||||||
this, // dump context
|
NULL, // filter context
|
||||||
NULL, // exit callback
|
dumpCallback, // dump callback
|
||||||
NULL, // exit context
|
this, // dump context
|
||||||
true, // generate dumps
|
NULL, // exit callback
|
||||||
temp_dir.path); // dump path
|
NULL, // exit context
|
||||||
|
true, // generate dumps
|
||||||
|
temp_dir.path()); // dump path
|
||||||
ASSERT_TRUE(server.Start());
|
ASSERT_TRUE(server.Start());
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@ -209,12 +217,14 @@ static void Crasher() {
|
|||||||
// the parent.
|
// the parent.
|
||||||
TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
||||||
CrashGenerationServer server(mach_port_name,
|
CrashGenerationServer server(mach_port_name,
|
||||||
dumpCallback, // dump callback
|
NULL, // filter callback
|
||||||
this, // dump context
|
NULL, // filter context
|
||||||
NULL, // exit callback
|
dumpCallback, // dump callback
|
||||||
NULL, // exit context
|
this, // dump context
|
||||||
true, // generate dumps
|
NULL, // exit callback
|
||||||
temp_dir.path); // dump path
|
NULL, // exit context
|
||||||
|
true, // generate dumps
|
||||||
|
temp_dir.path()); // dump path
|
||||||
ASSERT_TRUE(server.Start());
|
ASSERT_TRUE(server.Start());
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
@ -270,12 +280,14 @@ TEST_F(CrashGenerationServerTest, testChildProcessCrash) {
|
|||||||
// produces a valid minidump.
|
// produces a valid minidump.
|
||||||
TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
|
TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) {
|
||||||
CrashGenerationServer server(mach_port_name,
|
CrashGenerationServer server(mach_port_name,
|
||||||
dumpCallback, // dump callback
|
NULL, // filter callback
|
||||||
this, // dump context
|
NULL, // filter context
|
||||||
NULL, // exit callback
|
dumpCallback, // dump callback
|
||||||
NULL, // exit context
|
this, // dump context
|
||||||
true, // generate dumps
|
NULL, // exit callback
|
||||||
temp_dir.path); // dump path
|
NULL, // exit context
|
||||||
|
true, // generate dumps
|
||||||
|
temp_dir.path()); // dump path
|
||||||
ASSERT_TRUE(server.Start());
|
ASSERT_TRUE(server.Start());
|
||||||
|
|
||||||
// Spawn a child process
|
// Spawn a child process
|
||||||
@ -342,4 +354,45 @@ const u_int32_t kExpectedContext =
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
} // namespace
|
||||||
|
@ -36,8 +36,8 @@
|
|||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "client/mac/handler/exception_handler.h"
|
#include "client/mac/handler/exception_handler.h"
|
||||||
#include "client/mac/tests/auto_tempdir.h"
|
|
||||||
#include "common/mac/MachIPC.h"
|
#include "common/mac/MachIPC.h"
|
||||||
|
#include "common/tests/auto_tempdir.h"
|
||||||
#include "google_breakpad/processor/minidump.h"
|
#include "google_breakpad/processor/minidump.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -64,6 +64,7 @@ using testing::Test;
|
|||||||
|
|
||||||
class ExceptionHandlerTest : public Test {
|
class ExceptionHandlerTest : public Test {
|
||||||
public:
|
public:
|
||||||
|
void InProcessCrash(bool aborting);
|
||||||
AutoTempDir tempDir;
|
AutoTempDir tempDir;
|
||||||
string lastDumpName;
|
string lastDumpName;
|
||||||
};
|
};
|
||||||
@ -75,8 +76,13 @@ static void Crasher() {
|
|||||||
fprintf(stdout, "A = %d", *a);
|
fprintf(stdout, "A = %d", *a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SoonToCrash() {
|
static void AbortCrasher() {
|
||||||
Crasher();
|
fprintf(stdout, "Going to crash...\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SoonToCrash(void(*crasher)()) {
|
||||||
|
crasher();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool MDCallback(const char *dump_dir, const char *file_name,
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
void ExceptionHandlerTest::InProcessCrash(bool aborting) {
|
||||||
// Give the child process a pipe to report back on.
|
// Give the child process a pipe to report back on.
|
||||||
int fds[2];
|
int fds[2];
|
||||||
ASSERT_EQ(0, pipe(fds));
|
ASSERT_EQ(0, pipe(fds));
|
||||||
@ -103,9 +109,9 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
|||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
// In the child process.
|
// In the child process.
|
||||||
close(fds[0]);
|
close(fds[0]);
|
||||||
ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL);
|
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||||
// crash
|
// crash
|
||||||
SoonToCrash();
|
SoonToCrash(aborting ? &AbortCrasher : &Crasher);
|
||||||
// not reached
|
// not reached
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@ -128,6 +134,16 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
|||||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
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,
|
static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
|
||||||
void *context, bool success) {
|
void *context, bool success) {
|
||||||
ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
|
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) {
|
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());
|
ASSERT_TRUE(eh.WriteMinidump());
|
||||||
|
|
||||||
// Ensure that minidump file exists and is > 0 bytes.
|
// Ensure that minidump file exists and is > 0 bytes.
|
||||||
@ -159,7 +176,8 @@ TEST_F(ExceptionHandlerTest, WriteMinidump) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) {
|
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));
|
ASSERT_TRUE(eh.WriteMinidump(true));
|
||||||
|
|
||||||
// Ensure that minidump file exists and is > 0 bytes.
|
// Ensure that minidump file exists and is > 0 bytes.
|
||||||
@ -227,10 +245,10 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) {
|
|||||||
|
|
||||||
// Write a minidump of the child process.
|
// Write a minidump of the child process.
|
||||||
bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
|
bool result = ExceptionHandler::WriteMinidumpForChild(child_task,
|
||||||
child_thread,
|
child_thread,
|
||||||
tempDir.path,
|
tempDir.path(),
|
||||||
DumpNameMDCallback,
|
DumpNameMDCallback,
|
||||||
this);
|
this);
|
||||||
ASSERT_EQ(true, result);
|
ASSERT_EQ(true, result);
|
||||||
|
|
||||||
// Ensure that minidump file exists and is > 0 bytes.
|
// Ensure that minidump file exists and is > 0 bytes.
|
||||||
@ -267,7 +285,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(fds[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.
|
// Get some executable memory.
|
||||||
char* memory =
|
char* memory =
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
reinterpret_cast<char*>(mmap(NULL,
|
||||||
@ -329,20 +347,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemory) {
|
|||||||
ASSERT_TRUE(context);
|
ASSERT_TRUE(context);
|
||||||
|
|
||||||
u_int64_t instruction_pointer;
|
u_int64_t instruction_pointer;
|
||||||
switch (context->GetContextCPU()) {
|
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpMemoryRegion* region =
|
MinidumpMemoryRegion* region =
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||||
@ -379,7 +384,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(fds[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.
|
// Get some executable memory.
|
||||||
char* memory =
|
char* memory =
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
reinterpret_cast<char*>(mmap(NULL,
|
||||||
@ -441,20 +446,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
|||||||
ASSERT_TRUE(context);
|
ASSERT_TRUE(context);
|
||||||
|
|
||||||
u_int64_t instruction_pointer;
|
u_int64_t instruction_pointer;
|
||||||
switch (context->GetContextCPU()) {
|
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpMemoryRegion* region =
|
MinidumpMemoryRegion* region =
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||||
@ -491,7 +483,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(fds[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.
|
// Get some executable memory.
|
||||||
char* memory =
|
char* memory =
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
reinterpret_cast<char*>(mmap(NULL,
|
||||||
@ -553,20 +545,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
ASSERT_TRUE(context);
|
ASSERT_TRUE(context);
|
||||||
|
|
||||||
u_int64_t instruction_pointer;
|
u_int64_t instruction_pointer;
|
||||||
switch (context->GetContextCPU()) {
|
ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
MinidumpMemoryRegion* region =
|
MinidumpMemoryRegion* region =
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||||
@ -594,7 +573,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(fds[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.
|
// Try calling a NULL pointer.
|
||||||
typedef void (*void_function)(void);
|
typedef void (*void_function)(void);
|
||||||
void_function memory_function =
|
void_function memory_function =
|
||||||
@ -651,7 +630,7 @@ TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) {
|
|||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
close(fds[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.
|
// Run an extra thread so >2 memory regions will be written.
|
||||||
pthread_t junk_thread;
|
pthread_t junk_thread;
|
||||||
|
@ -41,9 +41,9 @@
|
|||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "client/mac/handler/minidump_generator.h"
|
#include "client/mac/handler/minidump_generator.h"
|
||||||
#include "client/mac/tests/auto_tempdir.h"
|
|
||||||
#include "client/mac/tests/spawn_child_process.h"
|
#include "client/mac/tests/spawn_child_process.h"
|
||||||
#include "common/mac/MachIPC.h"
|
#include "common/mac/MachIPC.h"
|
||||||
|
#include "common/tests/auto_tempdir.h"
|
||||||
#include "google_breakpad/processor/minidump.h"
|
#include "google_breakpad/processor/minidump.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -88,8 +88,8 @@ static void *Junk(void* data) {
|
|||||||
|
|
||||||
TEST_F(MinidumpGeneratorTest, InProcess) {
|
TEST_F(MinidumpGeneratorTest, InProcess) {
|
||||||
MinidumpGenerator generator;
|
MinidumpGenerator generator;
|
||||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
string dump_filename =
|
||||||
NULL);
|
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||||
|
|
||||||
// Run an extra thread since MinidumpGenerator assumes there
|
// Run an extra thread since MinidumpGenerator assumes there
|
||||||
// are 2 or more threads.
|
// are 2 or more threads.
|
||||||
@ -179,8 +179,8 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
|||||||
|
|
||||||
// Write a minidump of the child process.
|
// Write a minidump of the child process.
|
||||||
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
||||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
string dump_filename =
|
||||||
NULL);
|
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||||
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
||||||
|
|
||||||
// Ensure that minidump file exists and is > 0 bytes.
|
// Ensure that minidump file exists and is > 0 bytes.
|
||||||
@ -258,8 +258,8 @@ TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
|
|||||||
|
|
||||||
// Write a minidump of the child process.
|
// Write a minidump of the child process.
|
||||||
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
MinidumpGenerator generator(child_task, MACH_PORT_NULL);
|
||||||
string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path,
|
string dump_filename =
|
||||||
NULL);
|
MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
|
||||||
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
ASSERT_TRUE(generator.Write(dump_filename.c_str()));
|
||||||
|
|
||||||
// Ensure that minidump file exists and is > 0 bytes.
|
// Ensure that minidump file exists and is > 0 bytes.
|
||||||
|
@ -48,11 +48,16 @@ namespace google_breakpad {
|
|||||||
|
|
||||||
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
|
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() {
|
MinidumpFileWriter::~MinidumpFileWriter() {
|
||||||
Close();
|
if (close_file_when_destroyed_)
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MinidumpFileWriter::Open(const char *path) {
|
bool MinidumpFileWriter::Open(const char *path) {
|
||||||
@ -66,6 +71,12 @@ bool MinidumpFileWriter::Open(const char *path) {
|
|||||||
return file_ != -1;
|
return file_ != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MinidumpFileWriter::SetFile(const int file) {
|
||||||
|
assert(file_ == -1);
|
||||||
|
file_ = file;
|
||||||
|
close_file_when_destroyed_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MinidumpFileWriter::Close() {
|
bool MinidumpFileWriter::Close() {
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
|
@ -55,6 +55,16 @@ template<typename MDType> class TypedMDRVA;
|
|||||||
// header->get()->signature = MD_HEADER_SIGNATURE;
|
// header->get()->signature = MD_HEADER_SIGNATURE;
|
||||||
// :
|
// :
|
||||||
// writer.Close();
|
// 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 {
|
class MinidumpFileWriter {
|
||||||
public:
|
public:
|
||||||
// Invalid MDRVA (Minidump Relative Virtual Address)
|
// Invalid MDRVA (Minidump Relative Virtual Address)
|
||||||
@ -66,11 +76,19 @@ public:
|
|||||||
|
|
||||||
// Open |path| as the destination of the minidump data. Any existing file
|
// Open |path| as the destination of the minidump data. Any existing file
|
||||||
// will be overwritten.
|
// will be overwritten.
|
||||||
// Return true on success, or false on failure
|
// Return true on success, or false on failure.
|
||||||
bool Open(const char *path);
|
bool Open(const char *path);
|
||||||
|
|
||||||
// Close the current file
|
// Sets the file descriptor |file| as the destination of the minidump data.
|
||||||
// Return true on success, or false on failure
|
// 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();
|
bool Close();
|
||||||
|
|
||||||
// Copy the contents of |str| to a MDString and write it to the file.
|
// Copy the contents of |str| to a MDString and write it to the file.
|
||||||
@ -106,9 +124,12 @@ public:
|
|||||||
// unable to allocate the bytes.
|
// unable to allocate the bytes.
|
||||||
MDRVA Allocate(size_t size);
|
MDRVA Allocate(size_t size);
|
||||||
|
|
||||||
// The file descriptor for the output file
|
// The file descriptor for the output file.
|
||||||
int file_;
|
int file_;
|
||||||
|
|
||||||
|
// Whether |file_| should be closed when the instance is destroyed.
|
||||||
|
bool close_file_when_destroyed_;
|
||||||
|
|
||||||
// Current position in buffer
|
// Current position in buffer
|
||||||
MDRVA position_;
|
MDRVA position_;
|
||||||
|
|
||||||
@ -151,7 +172,8 @@ class UntypedMDRVA {
|
|||||||
|
|
||||||
// Return size and position
|
// Return size and position
|
||||||
inline MDLocationDescriptor location() const {
|
inline MDLocationDescriptor location() const {
|
||||||
MDLocationDescriptor location = { static_cast<int>(size_), position_ };
|
MDLocationDescriptor location = { static_cast<u_int32_t>(size_),
|
||||||
|
position_ };
|
||||||
return location;
|
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.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
// A buffer holding a series of bytes.
|
// 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
|
// 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
|
// complete_ flag, and set STR to the empty string. Return a reference to
|
||||||
// this cursor.
|
// this cursor.
|
||||||
ByteCursor &CString(std::string *str) {
|
ByteCursor &CString(string *str) {
|
||||||
const uint8_t *end
|
const uint8_t *end
|
||||||
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
|
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
|
||||||
if (end) {
|
if (end) {
|
||||||
@ -191,7 +193,7 @@ class ByteCursor {
|
|||||||
//
|
//
|
||||||
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
|
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
|
||||||
// cursor by LIMIT bytes.
|
// cursor by LIMIT bytes.
|
||||||
ByteCursor &CString(std::string *str, size_t limit) {
|
ByteCursor &CString(string *str, size_t limit) {
|
||||||
if (CheckAvailable(limit)) {
|
if (CheckAvailable(limit)) {
|
||||||
const uint8_t *end
|
const uint8_t *end
|
||||||
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
|
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "common/dwarf/bytereader.h"
|
#include "common/dwarf/bytereader.h"
|
||||||
#include "common/dwarf/bytereader-inl.h"
|
#include "common/dwarf/bytereader-inl.h"
|
||||||
#include "common/dwarf/cfi_assembler.h"
|
#include "common/dwarf/cfi_assembler.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
using dwarf2reader::ByteReader;
|
using dwarf2reader::ByteReader;
|
||||||
using dwarf2reader::DwarfPointerEncoding;
|
using dwarf2reader::DwarfPointerEncoding;
|
||||||
@ -47,7 +48,6 @@ using google_breakpad::test_assembler::Label;
|
|||||||
using google_breakpad::test_assembler::kBigEndian;
|
using google_breakpad::test_assembler::kBigEndian;
|
||||||
using google_breakpad::test_assembler::kLittleEndian;
|
using google_breakpad::test_assembler::kLittleEndian;
|
||||||
using google_breakpad::test_assembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
using std::string;
|
|
||||||
using testing::Test;
|
using testing::Test;
|
||||||
|
|
||||||
struct ReaderFixture {
|
struct ReaderFixture {
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
|
|
||||||
#include "common/dwarf/dwarf2enums.h"
|
#include "common/dwarf/dwarf2enums.h"
|
||||||
#include "common/test_assembler.h"
|
#include "common/test_assembler.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -49,7 +50,6 @@ using dwarf2reader::DwarfPointerEncoding;
|
|||||||
using google_breakpad::test_assembler::Endianness;
|
using google_breakpad::test_assembler::Endianness;
|
||||||
using google_breakpad::test_assembler::Label;
|
using google_breakpad::test_assembler::Label;
|
||||||
using google_breakpad::test_assembler::Section;
|
using google_breakpad::test_assembler::Section;
|
||||||
using std::string;
|
|
||||||
|
|
||||||
class CFISection: public Section {
|
class CFISection: public Section {
|
||||||
public:
|
public:
|
||||||
|
@ -31,10 +31,13 @@
|
|||||||
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
|
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
|
||||||
// See dwarf2diehandler.h for details.
|
// See dwarf2diehandler.h for details.
|
||||||
|
|
||||||
#include "common/dwarf/dwarf2diehandler.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/dwarf/dwarf2diehandler.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
|
|
||||||
DIEDispatcher::~DIEDispatcher() {
|
DIEDispatcher::~DIEDispatcher() {
|
||||||
@ -183,4 +186,14 @@ void DIEDispatcher::ProcessAttributeString(uint64 offset,
|
|||||||
current.handler_->ProcessAttributeString(attr, form, data);
|
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
|
} // namespace dwarf2reader
|
||||||
|
@ -157,10 +157,12 @@
|
|||||||
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
|
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/dwarf/types.h"
|
#include "common/dwarf/types.h"
|
||||||
#include "common/dwarf/dwarf2enums.h"
|
#include "common/dwarf/dwarf2enums.h"
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
|
|
||||||
@ -209,6 +211,9 @@ class DIEHandler {
|
|||||||
virtual void ProcessAttributeString(enum DwarfAttribute attr,
|
virtual void ProcessAttributeString(enum DwarfAttribute attr,
|
||||||
enum DwarfForm form,
|
enum DwarfForm form,
|
||||||
const string& data) { }
|
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
|
// 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
|
// 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 DwarfAttribute attr,
|
||||||
enum DwarfForm form,
|
enum DwarfForm form,
|
||||||
const string &data);
|
const string &data);
|
||||||
|
void ProcessAttributeSignature(uint64 offset,
|
||||||
|
enum DwarfAttribute attr,
|
||||||
|
enum DwarfForm form,
|
||||||
|
uint64 signature);
|
||||||
void EndDIE(uint64 offset);
|
void EndDIE(uint64 offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -347,7 +356,7 @@ class DIEDispatcher: public Dwarf2Handler {
|
|||||||
// - When we decide to ignore a subtree, we only push an entry on
|
// - 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
|
// the stack for the root of the tree being ignored, rather than
|
||||||
// pushing lots of stack entries with handler_ set to NULL.
|
// 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
|
// The root handler. We don't push it on die_handlers_ until we
|
||||||
// actually get the StartDIE call for the root.
|
// actually get the StartDIE call for the root.
|
||||||
|
@ -32,9 +32,15 @@
|
|||||||
|
|
||||||
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
|
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
|
|
||||||
#include "common/dwarf/dwarf2diehandler.h"
|
#include "common/dwarf/dwarf2diehandler.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
|
using std::make_pair;
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
using ::testing::ContainerEq;
|
using ::testing::ContainerEq;
|
||||||
@ -65,6 +71,8 @@ class MockDIEHandler: public DIEHandler {
|
|||||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||||
MOCK_METHOD3(ProcessAttributeString,
|
MOCK_METHOD3(ProcessAttributeString,
|
||||||
void(DwarfAttribute, DwarfForm, const string &));
|
void(DwarfAttribute, DwarfForm, const string &));
|
||||||
|
MOCK_METHOD3(ProcessAttributeSignature,
|
||||||
|
void(DwarfAttribute, DwarfForm, uint64));
|
||||||
MOCK_METHOD0(EndAttributes, bool());
|
MOCK_METHOD0(EndAttributes, bool());
|
||||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||||
const AttributeList &));
|
const AttributeList &));
|
||||||
@ -83,6 +91,8 @@ class MockRootDIEHandler: public RootDIEHandler {
|
|||||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||||
MOCK_METHOD3(ProcessAttributeString,
|
MOCK_METHOD3(ProcessAttributeString,
|
||||||
void(DwarfAttribute, DwarfForm, const string &));
|
void(DwarfAttribute, DwarfForm, const string &));
|
||||||
|
MOCK_METHOD3(ProcessAttributeSignature,
|
||||||
|
void(DwarfAttribute, DwarfForm, uint64));
|
||||||
MOCK_METHOD0(EndAttributes, bool());
|
MOCK_METHOD0(EndAttributes, bool());
|
||||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||||
const AttributeList &));
|
const AttributeList &));
|
||||||
@ -238,6 +248,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
|
|||||||
(DwarfForm) 0x15762fec,
|
(DwarfForm) 0x15762fec,
|
||||||
StrEq(str)))
|
StrEq(str)))
|
||||||
.WillOnce(Return());
|
.WillOnce(Return());
|
||||||
|
EXPECT_CALL(mock_root_handler,
|
||||||
|
ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
|
||||||
|
(DwarfForm) 0x4159f138,
|
||||||
|
0x94682463613e6a5fULL))
|
||||||
|
.WillOnce(Return());
|
||||||
EXPECT_CALL(mock_root_handler, EndAttributes())
|
EXPECT_CALL(mock_root_handler, EndAttributes())
|
||||||
.WillOnce(Return(true));
|
.WillOnce(Return(true));
|
||||||
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
|
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
|
||||||
@ -279,6 +294,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
|
|||||||
(DwarfAttribute) 0x310ed065,
|
(DwarfAttribute) 0x310ed065,
|
||||||
(DwarfForm) 0x15762fec,
|
(DwarfForm) 0x15762fec,
|
||||||
str);
|
str);
|
||||||
|
die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
|
||||||
|
(DwarfAttribute) 0x58790d72,
|
||||||
|
(DwarfForm) 0x4159f138,
|
||||||
|
0x94682463613e6a5fULL);
|
||||||
|
|
||||||
// Finish the root DIE (and thus the CU).
|
// Finish the root DIE (and thus the CU).
|
||||||
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
|
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
|
||||||
|
@ -143,7 +143,13 @@ enum DwarfForm {
|
|||||||
DW_FORM_ref4 = 0x13,
|
DW_FORM_ref4 = 0x13,
|
||||||
DW_FORM_ref8 = 0x14,
|
DW_FORM_ref8 = 0x14,
|
||||||
DW_FORM_ref_udata = 0x15,
|
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
|
// Attribute names and codes
|
||||||
|
@ -39,12 +39,15 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/dwarf/bytereader-inl.h"
|
#include "common/dwarf/bytereader-inl.h"
|
||||||
#include "common/dwarf/bytereader.h"
|
#include "common/dwarf/bytereader.h"
|
||||||
#include "common/dwarf/line_state_machine.h"
|
#include "common/dwarf/line_state_machine.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
|
|
||||||
@ -74,7 +77,7 @@ void CompilationUnit::ReadAbbrevs() {
|
|||||||
iter = sections_.find("__debug_abbrev");
|
iter = sections_.find("__debug_abbrev");
|
||||||
assert(iter != sections_.end());
|
assert(iter != sections_.end());
|
||||||
|
|
||||||
abbrevs_ = new vector<Abbrev>;
|
abbrevs_ = new std::vector<Abbrev>;
|
||||||
abbrevs_->resize(1);
|
abbrevs_->resize(1);
|
||||||
|
|
||||||
// The only way to check whether we are reading over the end of the
|
// 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 =
|
const enum DwarfAttribute name =
|
||||||
static_cast<enum DwarfAttribute>(nametemp);
|
static_cast<enum DwarfAttribute>(nametemp);
|
||||||
const enum DwarfForm form = static_cast<enum DwarfForm>(formtemp);
|
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());
|
assert(abbrev.number == abbrevs_->size());
|
||||||
abbrevs_->push_back(abbrev);
|
abbrevs_->push_back(abbrev);
|
||||||
@ -150,41 +153,35 @@ const char* CompilationUnit::SkipAttribute(const char* start,
|
|||||||
&len));
|
&len));
|
||||||
start += len;
|
start += len;
|
||||||
return SkipAttribute(start, form);
|
return SkipAttribute(start, form);
|
||||||
break;
|
|
||||||
|
|
||||||
|
case DW_FORM_flag_present:
|
||||||
|
return start;
|
||||||
case DW_FORM_data1:
|
case DW_FORM_data1:
|
||||||
case DW_FORM_flag:
|
case DW_FORM_flag:
|
||||||
case DW_FORM_ref1:
|
case DW_FORM_ref1:
|
||||||
return start + 1;
|
return start + 1;
|
||||||
break;
|
|
||||||
case DW_FORM_ref2:
|
case DW_FORM_ref2:
|
||||||
case DW_FORM_data2:
|
case DW_FORM_data2:
|
||||||
return start + 2;
|
return start + 2;
|
||||||
break;
|
|
||||||
case DW_FORM_ref4:
|
case DW_FORM_ref4:
|
||||||
case DW_FORM_data4:
|
case DW_FORM_data4:
|
||||||
return start + 4;
|
return start + 4;
|
||||||
break;
|
|
||||||
case DW_FORM_ref8:
|
case DW_FORM_ref8:
|
||||||
case DW_FORM_data8:
|
case DW_FORM_data8:
|
||||||
|
case DW_FORM_ref_sig8:
|
||||||
return start + 8;
|
return start + 8;
|
||||||
break;
|
|
||||||
case DW_FORM_string:
|
case DW_FORM_string:
|
||||||
return start + strlen(start) + 1;
|
return start + strlen(start) + 1;
|
||||||
break;
|
|
||||||
case DW_FORM_udata:
|
case DW_FORM_udata:
|
||||||
case DW_FORM_ref_udata:
|
case DW_FORM_ref_udata:
|
||||||
reader_->ReadUnsignedLEB128(start, &len);
|
reader_->ReadUnsignedLEB128(start, &len);
|
||||||
return start + len;
|
return start + len;
|
||||||
break;
|
|
||||||
|
|
||||||
case DW_FORM_sdata:
|
case DW_FORM_sdata:
|
||||||
reader_->ReadSignedLEB128(start, &len);
|
reader_->ReadSignedLEB128(start, &len);
|
||||||
return start + len;
|
return start + len;
|
||||||
break;
|
|
||||||
case DW_FORM_addr:
|
case DW_FORM_addr:
|
||||||
return start + reader_->AddressSize();
|
return start + reader_->AddressSize();
|
||||||
break;
|
|
||||||
case DW_FORM_ref_addr:
|
case DW_FORM_ref_addr:
|
||||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||||
// offset size.
|
// offset size.
|
||||||
@ -194,27 +191,21 @@ const char* CompilationUnit::SkipAttribute(const char* start,
|
|||||||
} else if (header_.version == 3) {
|
} else if (header_.version == 3) {
|
||||||
return start + reader_->OffsetSize();
|
return start + reader_->OffsetSize();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case DW_FORM_block1:
|
case DW_FORM_block1:
|
||||||
return start + 1 + reader_->ReadOneByte(start);
|
return start + 1 + reader_->ReadOneByte(start);
|
||||||
break;
|
|
||||||
case DW_FORM_block2:
|
case DW_FORM_block2:
|
||||||
return start + 2 + reader_->ReadTwoBytes(start);
|
return start + 2 + reader_->ReadTwoBytes(start);
|
||||||
break;
|
|
||||||
case DW_FORM_block4:
|
case DW_FORM_block4:
|
||||||
return start + 4 + reader_->ReadFourBytes(start);
|
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);
|
uint64 size = reader_->ReadUnsignedLEB128(start, &len);
|
||||||
return start + size + len;
|
return start + size + len;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case DW_FORM_strp:
|
case DW_FORM_strp:
|
||||||
return start + reader_->OffsetSize();
|
case DW_FORM_sec_offset:
|
||||||
break;
|
return start + reader_->OffsetSize();
|
||||||
default:
|
|
||||||
fprintf(stderr,"Unhandled form type");
|
|
||||||
}
|
}
|
||||||
fprintf(stderr,"Unhandled form type");
|
fprintf(stderr,"Unhandled form type");
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -326,85 +317,78 @@ const char* CompilationUnit::ProcessAttribute(
|
|||||||
&len));
|
&len));
|
||||||
start += len;
|
start += len;
|
||||||
return ProcessAttribute(dieoffset, start, attr, form);
|
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_data1:
|
||||||
case DW_FORM_flag:
|
case DW_FORM_flag:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadOneByte(start));
|
reader_->ReadOneByte(start));
|
||||||
return start + 1;
|
return start + 1;
|
||||||
break;
|
|
||||||
case DW_FORM_data2:
|
case DW_FORM_data2:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadTwoBytes(start));
|
reader_->ReadTwoBytes(start));
|
||||||
return start + 2;
|
return start + 2;
|
||||||
break;
|
|
||||||
case DW_FORM_data4:
|
case DW_FORM_data4:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadFourBytes(start));
|
reader_->ReadFourBytes(start));
|
||||||
return start + 4;
|
return start + 4;
|
||||||
break;
|
|
||||||
case DW_FORM_data8:
|
case DW_FORM_data8:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadEightBytes(start));
|
reader_->ReadEightBytes(start));
|
||||||
return start + 8;
|
return start + 8;
|
||||||
break;
|
|
||||||
case DW_FORM_string: {
|
case DW_FORM_string: {
|
||||||
const char* str = start;
|
const char* str = start;
|
||||||
handler_->ProcessAttributeString(dieoffset, attr, form,
|
handler_->ProcessAttributeString(dieoffset, attr, form,
|
||||||
str);
|
str);
|
||||||
return start + strlen(str) + 1;
|
return start + strlen(str) + 1;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case DW_FORM_udata:
|
case DW_FORM_udata:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadUnsignedLEB128(start,
|
reader_->ReadUnsignedLEB128(start,
|
||||||
&len));
|
&len));
|
||||||
return start + len;
|
return start + len;
|
||||||
break;
|
|
||||||
|
|
||||||
case DW_FORM_sdata:
|
case DW_FORM_sdata:
|
||||||
handler_->ProcessAttributeSigned(dieoffset, attr, form,
|
handler_->ProcessAttributeSigned(dieoffset, attr, form,
|
||||||
reader_->ReadSignedLEB128(start, &len));
|
reader_->ReadSignedLEB128(start, &len));
|
||||||
return start + len;
|
return start + len;
|
||||||
break;
|
|
||||||
case DW_FORM_addr:
|
case DW_FORM_addr:
|
||||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||||
reader_->ReadAddress(start));
|
reader_->ReadAddress(start));
|
||||||
return start + reader_->AddressSize();
|
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:
|
case DW_FORM_ref1:
|
||||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||||
reader_->ReadOneByte(start)
|
reader_->ReadOneByte(start)
|
||||||
+ offset_from_section_start_);
|
+ offset_from_section_start_);
|
||||||
return start + 1;
|
return start + 1;
|
||||||
break;
|
|
||||||
case DW_FORM_ref2:
|
case DW_FORM_ref2:
|
||||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||||
reader_->ReadTwoBytes(start)
|
reader_->ReadTwoBytes(start)
|
||||||
+ offset_from_section_start_);
|
+ offset_from_section_start_);
|
||||||
return start + 2;
|
return start + 2;
|
||||||
break;
|
|
||||||
case DW_FORM_ref4:
|
case DW_FORM_ref4:
|
||||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||||
reader_->ReadFourBytes(start)
|
reader_->ReadFourBytes(start)
|
||||||
+ offset_from_section_start_);
|
+ offset_from_section_start_);
|
||||||
return start + 4;
|
return start + 4;
|
||||||
break;
|
|
||||||
case DW_FORM_ref8:
|
case DW_FORM_ref8:
|
||||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||||
reader_->ReadEightBytes(start)
|
reader_->ReadEightBytes(start)
|
||||||
+ offset_from_section_start_);
|
+ offset_from_section_start_);
|
||||||
return start + 8;
|
return start + 8;
|
||||||
break;
|
|
||||||
case DW_FORM_ref_udata:
|
case DW_FORM_ref_udata:
|
||||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||||
reader_->ReadUnsignedLEB128(start,
|
reader_->ReadUnsignedLEB128(start,
|
||||||
&len)
|
&len)
|
||||||
+ offset_from_section_start_);
|
+ offset_from_section_start_);
|
||||||
return start + len;
|
return start + len;
|
||||||
break;
|
|
||||||
case DW_FORM_ref_addr:
|
case DW_FORM_ref_addr:
|
||||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||||
// offset size.
|
// offset size.
|
||||||
@ -419,35 +403,36 @@ const char* CompilationUnit::ProcessAttribute(
|
|||||||
return start + reader_->OffsetSize();
|
return start + reader_->OffsetSize();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case DW_FORM_ref_sig8:
|
||||||
|
handler_->ProcessAttributeSignature(dieoffset, attr, form,
|
||||||
|
reader_->ReadEightBytes(start));
|
||||||
|
return start + 8;
|
||||||
|
|
||||||
case DW_FORM_block1: {
|
case DW_FORM_block1: {
|
||||||
uint64 datalen = reader_->ReadOneByte(start);
|
uint64 datalen = reader_->ReadOneByte(start);
|
||||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1,
|
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1,
|
||||||
datalen);
|
datalen);
|
||||||
return start + 1 + datalen;
|
return start + 1 + datalen;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case DW_FORM_block2: {
|
case DW_FORM_block2: {
|
||||||
uint64 datalen = reader_->ReadTwoBytes(start);
|
uint64 datalen = reader_->ReadTwoBytes(start);
|
||||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2,
|
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2,
|
||||||
datalen);
|
datalen);
|
||||||
return start + 2 + datalen;
|
return start + 2 + datalen;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case DW_FORM_block4: {
|
case DW_FORM_block4: {
|
||||||
uint64 datalen = reader_->ReadFourBytes(start);
|
uint64 datalen = reader_->ReadFourBytes(start);
|
||||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4,
|
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4,
|
||||||
datalen);
|
datalen);
|
||||||
return start + 4 + datalen;
|
return start + 4 + datalen;
|
||||||
}
|
}
|
||||||
break;
|
case DW_FORM_block:
|
||||||
case DW_FORM_block: {
|
case DW_FORM_exprloc: {
|
||||||
uint64 datalen = reader_->ReadUnsignedLEB128(start, &len);
|
uint64 datalen = reader_->ReadUnsignedLEB128(start, &len);
|
||||||
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len,
|
handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len,
|
||||||
datalen);
|
datalen);
|
||||||
return start + datalen + len;
|
return start + datalen + len;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case DW_FORM_strp: {
|
case DW_FORM_strp: {
|
||||||
assert(string_buffer_ != NULL);
|
assert(string_buffer_ != NULL);
|
||||||
|
|
||||||
@ -459,11 +444,8 @@ const char* CompilationUnit::ProcessAttribute(
|
|||||||
str);
|
str);
|
||||||
return start + reader_->OffsetSize();
|
return start + reader_->OffsetSize();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Unhandled form type");
|
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Unhandled form type");
|
fprintf(stderr, "Unhandled form type\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,7 +475,7 @@ void CompilationUnit::ProcessDIEs() {
|
|||||||
else
|
else
|
||||||
lengthstart += 4;
|
lengthstart += 4;
|
||||||
|
|
||||||
stack<uint64> die_stack;
|
std::stack<uint64> die_stack;
|
||||||
|
|
||||||
while (dieptr < (lengthstart + header_.length)) {
|
while (dieptr < (lengthstart + header_.length)) {
|
||||||
// We give the user the absolute offset from the beginning of
|
// We give the user the absolute offset from the beginning of
|
||||||
@ -583,7 +565,7 @@ void LineInfo::ReadHeader() {
|
|||||||
header_.opcode_base = reader_->ReadOneByte(lineptr);
|
header_.opcode_base = reader_->ReadOneByte(lineptr);
|
||||||
lineptr += 1;
|
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->resize(header_.opcode_base + 1);
|
||||||
(*header_.std_opcode_lengths)[0] = 0;
|
(*header_.std_opcode_lengths)[0] = 0;
|
||||||
for (int i = 1; i < header_.opcode_base; i++) {
|
for (int i = 1; i < header_.opcode_base; i++) {
|
||||||
@ -1096,7 +1078,7 @@ class CallFrameInfo::RuleMap {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// A map from register numbers to Rules.
|
// 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_.
|
// Remove all register rules and clear cfa_rule_.
|
||||||
void Clear();
|
void Clear();
|
||||||
@ -1341,7 +1323,7 @@ class CallFrameInfo::State {
|
|||||||
|
|
||||||
// A stack of saved states, for DW_CFA_remember_state and
|
// A stack of saved states, for DW_CFA_remember_state and
|
||||||
// DW_CFA_restore_state.
|
// DW_CFA_restore_state.
|
||||||
stack<RuleMap> saved_rules_;
|
std::stack<RuleMap> saved_rules_;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
|
bool CallFrameInfo::State::InterpretCIE(const CIE &cie) {
|
||||||
@ -1877,20 +1859,14 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||||||
cie->version = reader_->ReadOneByte(cursor);
|
cie->version = reader_->ReadOneByte(cursor);
|
||||||
cursor++;
|
cursor++;
|
||||||
|
|
||||||
// If we don't recognize the version, we can't parse any more fields
|
// If we don't recognize the version, we can't parse any more fields of the
|
||||||
// of the CIE. For DWARF CFI, we handle versions 1 through 3 (there
|
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
|
||||||
// was never a version 2 of CFI data). For .eh_frame, we handle only
|
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
|
||||||
// version 1.
|
// the difference between those versions seems to be the same as for
|
||||||
if (eh_frame_) {
|
// .debug_frame.
|
||||||
if (cie->version != 1) {
|
if (cie->version < 1 || cie->version > 3) {
|
||||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (cie->version < 1 || cie->version > 3) {
|
|
||||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *augmentation_start = cursor;
|
const char *augmentation_start = cursor;
|
||||||
@ -1898,7 +1874,8 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||||||
memchr(augmentation_start, '\0', cie->end - augmentation_start);
|
memchr(augmentation_start, '\0', cie->end - augmentation_start);
|
||||||
if (! augmentation_end) return ReportIncomplete(cie);
|
if (! augmentation_end) return ReportIncomplete(cie);
|
||||||
cursor = static_cast<const char *>(augmentation_end);
|
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'.
|
// Skip the terminating '\0'.
|
||||||
cursor++;
|
cursor++;
|
||||||
|
|
||||||
|
@ -49,8 +49,7 @@
|
|||||||
#include "common/dwarf/bytereader.h"
|
#include "common/dwarf/bytereader.h"
|
||||||
#include "common/dwarf/dwarf2enums.h"
|
#include "common/dwarf/dwarf2enums.h"
|
||||||
#include "common/dwarf/types.h"
|
#include "common/dwarf/types.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
struct LineStateMachine;
|
struct LineStateMachine;
|
||||||
@ -59,8 +58,9 @@ class LineInfoHandler;
|
|||||||
|
|
||||||
// This maps from a string naming a section to a pair containing a
|
// This maps from a string naming a section to a pair containing a
|
||||||
// the data for the section, and the size of the section.
|
// the data for the section, and the size of the section.
|
||||||
typedef map<string, pair<const char*, uint64> > SectionMap;
|
typedef std::map<string, std::pair<const char*, uint64> > SectionMap;
|
||||||
typedef list<pair<enum DwarfAttribute, enum DwarfForm> > AttributeList;
|
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
|
||||||
|
AttributeList;
|
||||||
typedef AttributeList::iterator AttributeIterator;
|
typedef AttributeList::iterator AttributeIterator;
|
||||||
typedef AttributeList::const_iterator ConstAttributeIterator;
|
typedef AttributeList::const_iterator ConstAttributeIterator;
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ struct LineInfoHeader {
|
|||||||
uint8 opcode_base;
|
uint8 opcode_base;
|
||||||
// Use a pointer so that signalsafe_addr2line is able to use this structure
|
// Use a pointer so that signalsafe_addr2line is able to use this structure
|
||||||
// without heap allocation problem.
|
// without heap allocation problem.
|
||||||
vector<unsigned char> *std_opcode_lengths;
|
std::vector<unsigned char> *std_opcode_lengths;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LineInfo {
|
class LineInfo {
|
||||||
@ -313,7 +313,7 @@ class CompilationUnit {
|
|||||||
// Set of DWARF2/3 abbreviations for this compilation unit. Indexed
|
// Set of DWARF2/3 abbreviations for this compilation unit. Indexed
|
||||||
// by abbreviation number, which means that abbrevs_[0] is not
|
// by abbreviation number, which means that abbrevs_[0] is not
|
||||||
// valid.
|
// valid.
|
||||||
vector<Abbrev>* abbrevs_;
|
std::vector<Abbrev>* abbrevs_;
|
||||||
|
|
||||||
// String section buffer and length, if we have a string section.
|
// String section buffer and length, if we have a string section.
|
||||||
// This is here to avoid doing a section lookup for strings in
|
// This is here to avoid doing a section lookup for strings in
|
||||||
@ -394,6 +394,15 @@ class Dwarf2Handler {
|
|||||||
enum DwarfForm form,
|
enum DwarfForm form,
|
||||||
const string& data) { }
|
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.
|
// Called when finished processing the DIE at OFFSET.
|
||||||
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
||||||
// before ends of the previous DIE, as we process children before
|
// before ends of the previous DIE, as we process children before
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// The '.eh_frame' format, used by the Linux C++ ABI for exception
|
// 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/bytereader-inl.h"
|
||||||
#include "common/dwarf/cfi_assembler.h"
|
#include "common/dwarf/cfi_assembler.h"
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
#include "google_breakpad/common/breakpad_types.h"
|
#include "google_breakpad/common/breakpad_types.h"
|
||||||
|
|
||||||
using google_breakpad::CFISection;
|
using google_breakpad::CFISection;
|
||||||
|
@ -39,14 +39,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/dwarf/functioninfo.h"
|
#include "common/dwarf/functioninfo.h"
|
||||||
|
|
||||||
#include "common/dwarf/bytereader.h"
|
#include "common/dwarf/bytereader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
|
|
||||||
CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
|
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
|
||||||
vector<string>* dirs,
|
std::vector<string>* dirs,
|
||||||
LineMap* linemap):linemap_(linemap),
|
LineMap* linemap):linemap_(linemap),
|
||||||
files_(files),
|
files_(files),
|
||||||
dirs_(dirs) {
|
dirs_(dirs) {
|
||||||
@ -95,8 +94,10 @@ void CULineInfoHandler::DefineFile(const string& name,
|
|||||||
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
|
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
|
||||||
uint32 line_num, uint32 column_num) {
|
uint32 line_num, uint32 column_num) {
|
||||||
if (file_num < files_->size()) {
|
if (file_num < files_->size()) {
|
||||||
linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(),
|
linemap_->insert(
|
||||||
line_num)));
|
std::make_pair(address,
|
||||||
|
std::make_pair(files_->at(file_num).name.c_str(),
|
||||||
|
line_num)));
|
||||||
|
|
||||||
if(address < files_->at(file_num).lowpc) {
|
if(address < files_->at(file_num).lowpc) {
|
||||||
files_->at(file_num).lowpc = address;
|
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_->name = "";
|
||||||
current_function_info_->line = 0;
|
current_function_info_->line = 0;
|
||||||
current_function_info_->file = "";
|
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
|
// FALLTHROUGH
|
||||||
case DW_TAG_compile_unit:
|
case DW_TAG_compile_unit:
|
||||||
@ -164,9 +166,9 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
|
|||||||
assert(iter != sections_.end());
|
assert(iter != sections_.end());
|
||||||
|
|
||||||
// this should be a scoped_ptr but we dont' use boost :-(
|
// this should be a scoped_ptr but we dont' use boost :-(
|
||||||
auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
|
std::auto_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
|
||||||
iter->second.second - data,
|
iter->second.second - data,
|
||||||
reader_, linehandler_));
|
reader_, linehandler_));
|
||||||
lireader->Start();
|
lireader->Start();
|
||||||
} else if (current_function_info_) {
|
} else if (current_function_info_) {
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
@ -219,8 +221,8 @@ void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
|
|||||||
|
|
||||||
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
|
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
|
||||||
if (current_function_info_ && current_function_info_->lowpc)
|
if (current_function_info_ && current_function_info_->lowpc)
|
||||||
address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc,
|
address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
|
||||||
current_function_info_));
|
current_function_info_));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dwarf2reader
|
} // namespace dwarf2reader
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
|
|
||||||
namespace dwarf2reader {
|
namespace dwarf2reader {
|
||||||
@ -66,8 +67,8 @@ struct SourceFileInfo {
|
|||||||
uint64 lowpc;
|
uint64 lowpc;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef map<uint64, FunctionInfo*> FunctionMap;
|
typedef std::map<uint64, FunctionInfo*> FunctionMap;
|
||||||
typedef map<uint64, pair<string, uint32> > LineMap;
|
typedef std::map<uint64, std::pair<string, uint32> > LineMap;
|
||||||
|
|
||||||
// This class is a basic line info handler that fills in the dirs,
|
// 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
|
// file, and linemap passed into it with the data produced from the
|
||||||
@ -76,8 +77,8 @@ class CULineInfoHandler: public LineInfoHandler {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
//
|
//
|
||||||
CULineInfoHandler(vector<SourceFileInfo>* files,
|
CULineInfoHandler(std::vector<SourceFileInfo>* files,
|
||||||
vector<string>* dirs,
|
std::vector<string>* dirs,
|
||||||
LineMap* linemap);
|
LineMap* linemap);
|
||||||
virtual ~CULineInfoHandler() { }
|
virtual ~CULineInfoHandler() { }
|
||||||
|
|
||||||
@ -102,14 +103,14 @@ class CULineInfoHandler: public LineInfoHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
LineMap* linemap_;
|
LineMap* linemap_;
|
||||||
vector<SourceFileInfo>* files_;
|
std::vector<SourceFileInfo>* files_;
|
||||||
vector<string>* dirs_;
|
std::vector<string>* dirs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CUFunctionInfoHandler: public Dwarf2Handler {
|
class CUFunctionInfoHandler: public Dwarf2Handler {
|
||||||
public:
|
public:
|
||||||
CUFunctionInfoHandler(vector<SourceFileInfo>* files,
|
CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
|
||||||
vector<string>* dirs,
|
std::vector<string>* dirs,
|
||||||
LineMap* linemap,
|
LineMap* linemap,
|
||||||
FunctionMap* offset_to_funcinfo,
|
FunctionMap* offset_to_funcinfo,
|
||||||
FunctionMap* address_to_funcinfo,
|
FunctionMap* address_to_funcinfo,
|
||||||
@ -172,8 +173,8 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
|
|||||||
virtual void EndDIE(uint64 offset);
|
virtual void EndDIE(uint64 offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vector<SourceFileInfo>* files_;
|
std::vector<SourceFileInfo>* files_;
|
||||||
vector<string>* dirs_;
|
std::vector<string>* dirs_;
|
||||||
LineMap* linemap_;
|
LineMap* linemap_;
|
||||||
FunctionMap* offset_to_funcinfo_;
|
FunctionMap* offset_to_funcinfo_;
|
||||||
FunctionMap* address_to_funcinfo_;
|
FunctionMap* address_to_funcinfo_;
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#ifndef _COMMON_DWARF_TYPES_H__
|
#ifndef _COMMON_DWARF_TYPES_H__
|
||||||
#define _COMMON_DWARF_TYPES_H__
|
#define _COMMON_DWARF_TYPES_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef signed char int8;
|
typedef signed char int8;
|
||||||
typedef short int16;
|
typedef short int16;
|
||||||
typedef int int32;
|
typedef int int32;
|
||||||
|
@ -84,12 +84,22 @@ vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
|
|||||||
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per ARM IHI 0040A, section 3.1
|
||||||
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
|
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
|
||||||
static const char *const names[] = {
|
static const char *const names[] = {
|
||||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
|
"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]));
|
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||||
@ -132,7 +142,8 @@ string DwarfCFIToModule::RegisterName(int i) {
|
|||||||
if (reg == return_address_)
|
if (reg == return_address_)
|
||||||
return ra_name_;
|
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];
|
return register_names_[reg];
|
||||||
|
|
||||||
reporter_->UnnamedRegister(entry_offset_, reg);
|
reporter_->UnnamedRegister(entry_offset_, reg);
|
||||||
|
@ -48,13 +48,13 @@
|
|||||||
|
|
||||||
#include "common/module.h"
|
#include "common/module.h"
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using dwarf2reader::CallFrameInfo;
|
using dwarf2reader::CallFrameInfo;
|
||||||
using google_breakpad::Module;
|
using google_breakpad::Module;
|
||||||
using std::set;
|
using std::set;
|
||||||
using std::string;
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
// A class that accepts parsed call frame information from the DWARF
|
// A class that accepts parsed call frame information from the DWARF
|
||||||
|
@ -39,7 +39,9 @@
|
|||||||
#include "common/dwarf_cu_to_module.h"
|
#include "common/dwarf_cu_to_module.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <cxxabi.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -55,7 +57,7 @@ using std::set;
|
|||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
// Data provided by a DWARF specification DIE.
|
// Data provided by a DWARF specification DIE.
|
||||||
//
|
//
|
||||||
// In DWARF, the DIE for a definition may contain a DW_AT_specification
|
// In DWARF, the DIE for a definition may contain a DW_AT_specification
|
||||||
// attribute giving the offset of the corresponding declaration DIE, and
|
// attribute giving the offset of the corresponding declaration DIE, and
|
||||||
// the definition DIE may omit information given in the declaration. For
|
// the definition DIE may omit information given in the declaration. For
|
||||||
@ -72,6 +74,9 @@ using std::vector;
|
|||||||
// A Specification holds information gathered from a declaration DIE that
|
// A Specification holds information gathered from a declaration DIE that
|
||||||
// we may need if we find a DW_AT_specification link pointing to it.
|
// we may need if we find a DW_AT_specification link pointing to it.
|
||||||
struct DwarfCUToModule::Specification {
|
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.
|
// The name of the enclosing scope, or the empty string if there is none.
|
||||||
string enclosing_name;
|
string enclosing_name;
|
||||||
|
|
||||||
@ -96,12 +101,20 @@ struct DwarfCUToModule::FilePrivate {
|
|||||||
// A set of strings used in this CU. Before storing a string in one of
|
// A set of strings used in this CU. Before storing a string in one of
|
||||||
// our data structures, insert it into this set, and then use the string
|
// our data structures, insert it into this set, and then use the string
|
||||||
// from the set.
|
// from the set.
|
||||||
//
|
//
|
||||||
// Because std::string uses reference counting internally, simply using
|
// In some STL implementations, strings are reference-counted internally,
|
||||||
// strings from this set, even if passed by value, assigned, or held
|
// meaning that simply using strings from this set, even if passed by
|
||||||
// directly in structures and containers (map<string, ...>, for example),
|
// value, assigned, or held directly in structures and containers
|
||||||
// causes those strings to share a single instance of each distinct piece
|
// (map<string, ...>, for example), causes those strings to share a
|
||||||
// of text.
|
// 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;
|
set<string> common_strings;
|
||||||
|
|
||||||
// A map from offsets of DIEs within the .debug_info section to
|
// 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_;
|
DIEContext *parent_context_;
|
||||||
uint64 offset_;
|
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.
|
// If this DIE has a DW_AT_declaration attribute, this is its value.
|
||||||
// It is false on DIEs with no DW_AT_declaration attribute.
|
// It is false on DIEs with no DW_AT_declaration attribute.
|
||||||
bool declaration_;
|
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
|
// The value of the DW_AT_name attribute, or the empty string if the
|
||||||
// DIE has no such attribute.
|
// DIE has no such attribute.
|
||||||
string name_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(
|
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(
|
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||||
enum DwarfAttribute attr,
|
enum DwarfAttribute attr,
|
||||||
enum DwarfForm form,
|
enum DwarfForm form,
|
||||||
const string &data) {
|
const string &data) {
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case dwarf2reader::DW_AT_name: {
|
case dwarf2reader::DW_AT_name:
|
||||||
// Place the name in our global set of strings, and then use the
|
name_attribute_ = AddStringToPool(data);
|
||||||
// string from the set. Even though the assignment looks like a copy,
|
break;
|
||||||
// all the major std::string implementations use reference counting
|
case dwarf2reader::DW_AT_MIPS_linkage_name: {
|
||||||
// internally, so the effect is to have all our data structures share
|
char* demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, NULL);
|
||||||
// copies of strings whenever possible.
|
if (demangled) {
|
||||||
pair<set<string>::iterator, bool> result =
|
demangled_name_ = AddStringToPool(demangled);
|
||||||
cu_context_->file_context->file_private->common_strings.insert(data);
|
free(reinterpret_cast<void*>(demangled));
|
||||||
name_attribute_ = *result.first;
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
@ -293,33 +325,54 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
|||||||
}
|
}
|
||||||
|
|
||||||
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||||
// Find our unqualified name. If the DIE has its own DW_AT_name
|
// Use the demangled name, if one is available. Demangled names are
|
||||||
// attribute, then use that; otherwise, check our specification.
|
// preferable to those inferred from the DWARF structure because they
|
||||||
const string *unqualified_name;
|
// include argument types.
|
||||||
if (name_attribute_.empty() && specification_)
|
const string *qualified_name = NULL;
|
||||||
unqualified_name = &specification_->unqualified_name;
|
if (!demangled_name_.empty()) {
|
||||||
else
|
// Found it is this DIE.
|
||||||
unqualified_name = &name_attribute_;
|
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
|
const string *unqualified_name;
|
||||||
// specification, it's the specification's enclosing context that
|
|
||||||
// counts; otherwise, use this DIE's context.
|
|
||||||
const string *enclosing_name;
|
const string *enclosing_name;
|
||||||
if (specification_)
|
if (!qualified_name) {
|
||||||
enclosing_name = &specification_->enclosing_name;
|
// Find our unqualified name. If the DIE has its own DW_AT_name
|
||||||
else
|
// attribute, then use that; otherwise, check our specification.
|
||||||
enclosing_name = &parent_context_->name;
|
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
|
// If this DIE was marked as a declaration, record its names in the
|
||||||
// specification table.
|
// specification table.
|
||||||
if (declaration_) {
|
if (declaration_) {
|
||||||
FileContext *file_context = cu_context_->file_context;
|
FileContext *file_context = cu_context_->file_context;
|
||||||
Specification spec;
|
Specification spec;
|
||||||
spec.enclosing_name = *enclosing_name;
|
if (qualified_name)
|
||||||
spec.unqualified_name = *unqualified_name;
|
spec.qualified_name = *qualified_name;
|
||||||
|
else {
|
||||||
|
spec.enclosing_name = *enclosing_name;
|
||||||
|
spec.unqualified_name = *unqualified_name;
|
||||||
|
}
|
||||||
file_context->file_private->specifications[offset_] = spec;
|
file_context->file_private->specifications[offset_] = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (qualified_name)
|
||||||
|
return *qualified_name;
|
||||||
|
|
||||||
// Combine the enclosing name and unqualified name to produce our
|
// Combine the enclosing name and unqualified name to produce our
|
||||||
// own fully-qualified name.
|
// own fully-qualified name.
|
||||||
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||||
@ -439,7 +492,11 @@ void DwarfCUToModule::FuncHandler::Finish() {
|
|||||||
func->address = low_pc_;
|
func->address = low_pc_;
|
||||||
func->size = high_pc_ - low_pc_;
|
func->size = high_pc_ - low_pc_;
|
||||||
func->parameter_size = 0;
|
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_) {
|
} else if (inline_) {
|
||||||
AbstractOrigin origin(name_);
|
AbstractOrigin origin(name_);
|
||||||
cu_context_->file_context->file_private->origins[offset_] = origin;
|
cu_context_->file_context->file_private->origins[offset_] = origin;
|
||||||
@ -469,7 +526,7 @@ bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
|
|||||||
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
|
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
|
||||||
uint64 offset,
|
uint64 offset,
|
||||||
enum DwarfTag tag,
|
enum DwarfTag tag,
|
||||||
const AttributeList &attrs) {
|
const AttributeList &/*attrs*/) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case dwarf2reader::DW_TAG_subprogram:
|
case dwarf2reader::DW_TAG_subprogram:
|
||||||
return new FuncHandler(cu_context_, &child_context_, offset);
|
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) {
|
void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||||
CUHeading();
|
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);
|
filename_.c_str(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +668,7 @@ bool DwarfCUToModule::EndAttributes() {
|
|||||||
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||||
uint64 offset,
|
uint64 offset,
|
||||||
enum DwarfTag tag,
|
enum DwarfTag tag,
|
||||||
const AttributeList &attrs) {
|
const AttributeList &/*attrs*/) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case dwarf2reader::DW_TAG_subprogram:
|
case dwarf2reader::DW_TAG_subprogram:
|
||||||
return new FuncHandler(cu_context_, child_context_, offset);
|
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,
|
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
|
// We don't deal with partial compilation units (the only other tag
|
||||||
// likely to be used for root DIE).
|
// likely to be used for root DIE).
|
||||||
return tag == dwarf2reader::DW_TAG_compile_unit;
|
return tag == dwarf2reader::DW_TAG_compile_unit;
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "common/dwarf/bytereader.h"
|
#include "common/dwarf/bytereader.h"
|
||||||
#include "common/dwarf/dwarf2diehandler.h"
|
#include "common/dwarf/dwarf2diehandler.h"
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
@ -58,9 +59,10 @@ using dwarf2reader::DwarfTag;
|
|||||||
// Populate a google_breakpad::Module with DWARF debugging information.
|
// Populate a google_breakpad::Module with DWARF debugging information.
|
||||||
//
|
//
|
||||||
// An instance of this class can be provided as a handler to a
|
// An instance of this class can be provided as a handler to a
|
||||||
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the
|
// dwarf2reader::DIEDispatcher, which can in turn be a handler for a
|
||||||
// results of parsing to populate a google_breakpad::Module with
|
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results
|
||||||
// source file, function, and source line information.
|
// of parsing to populate a google_breakpad::Module with source file,
|
||||||
|
// function, and source line information.
|
||||||
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||||
struct FilePrivate;
|
struct FilePrivate;
|
||||||
public:
|
public:
|
||||||
|
@ -32,7 +32,12 @@
|
|||||||
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
|
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
|
||||||
// See dwarf_line_to_module.h for details.
|
// See dwarf_line_to_module.h for details.
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/dwarf_line_to_module.h"
|
#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
|
// 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
|
// 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,
|
// 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.
|
// 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))
|
if (PathIsAbsolute(path))
|
||||||
return path;
|
return path;
|
||||||
return base + "/" + path;
|
return base + "/" + path;
|
||||||
@ -68,7 +74,7 @@ void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
|
|||||||
else if (file_num > highest_file_number_)
|
else if (file_num > highest_file_number_)
|
||||||
highest_file_number_ = file_num;
|
highest_file_number_ = file_num;
|
||||||
|
|
||||||
std::string full_name;
|
string full_name;
|
||||||
if (dir_num != 0) {
|
if (dir_num != 0) {
|
||||||
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
||||||
if (directory_it != directories_.end()) {
|
if (directory_it != directories_.end()) {
|
||||||
|
@ -38,8 +38,11 @@
|
|||||||
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||||
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/module.h"
|
#include "common/module.h"
|
||||||
#include "common/dwarf/dwarf2reader.h"
|
#include "common/dwarf/dwarf2reader.h"
|
||||||
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
@ -127,8 +130,8 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
|||||||
|
|
||||||
~DwarfLineToModule() { }
|
~DwarfLineToModule() { }
|
||||||
|
|
||||||
void DefineDir(const std::string &name, uint32 dir_num);
|
void DefineDir(const string &name, uint32 dir_num);
|
||||||
void DefineFile(const std::string &name, int32 file_num,
|
void DefineFile(const string &name, int32 file_num,
|
||||||
uint32 dir_num, uint64 mod_time,
|
uint32 dir_num, uint64 mod_time,
|
||||||
uint64 length);
|
uint64 length);
|
||||||
void AddLine(uint64 address, uint64 length,
|
void AddLine(uint64 address, uint64 length,
|
||||||
@ -136,7 +139,7 @@ class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef std::map<uint32, std::string> DirectoryTable;
|
typedef std::map<uint32, string> DirectoryTable;
|
||||||
typedef std::map<uint32, Module::File *> FileTable;
|
typedef std::map<uint32, Module::File *> FileTable;
|
||||||
|
|
||||||
// The module we're contributing debugging info to. Owned by our
|
// 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.
|
// Java language-specific operations.
|
||||||
class JavaLanguage: public Language {
|
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>
|
#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 abstract base class for language-specific operations. We choose
|
||||||
// an instance of a subclass of this when we find the CU's language.
|
// an instance of a subclass of this when we find the CU's language.
|
||||||
@ -50,6 +50,10 @@ using std::string;
|
|||||||
// language.
|
// language.
|
||||||
class Language {
|
class Language {
|
||||||
public:
|
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
|
// Return true if this language has functions to which we can assign
|
||||||
// line numbers. (Debugging info for assembly language, for example,
|
// line numbers. (Debugging info for assembly language, for example,
|
||||||
// can have source location information, but does not have functions
|
// 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__;\
|
__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 <arpa/inet.h>
|
||||||
#include <assert.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 <string.h>
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "common/linux/elfutils.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
|
#include "common/linux/memory_mapped_file.h"
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
#ifndef NT_GNU_BUILD_ID
|
||||||
|
#define NT_GNU_BUILD_ID 3
|
||||||
|
#endif
|
||||||
|
|
||||||
FileID::FileID(const char* path) {
|
FileID::FileID(const char* path) {
|
||||||
strncpy(path_, path, sizeof(path_));
|
strncpy(path_, path, sizeof(path_));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ElfClass32 {
|
// These six functions are also used inside the crashed process, so be safe
|
||||||
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
|
|
||||||
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
||||||
|
|
||||||
template<typename ElfClass>
|
template<typename ElfClass>
|
||||||
static void FindElfClassTextSection(const char *elf_base,
|
static bool ElfClassBuildIDNoteIdentifier(const void *section,
|
||||||
const void **text_start,
|
uint8_t identifier[kMDGUIDSize]) {
|
||||||
int *text_size) {
|
typedef typename ElfClass::Nhdr Nhdr;
|
||||||
typedef typename ElfClass::Ehdr Ehdr;
|
|
||||||
typedef typename ElfClass::Shdr Shdr;
|
|
||||||
|
|
||||||
assert(elf_base);
|
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
|
||||||
assert(text_start);
|
if (note_header->n_type != NT_GNU_BUILD_ID ||
|
||||||
assert(text_size);
|
note_header->n_descsz == 0) {
|
||||||
|
|
||||||
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 {
|
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// Attempt to locate a .note.gnu.build-id section in an ELF binary
|
||||||
bool FileID::ElfFileIdentifierFromMappedFile(void* base,
|
// and copy as many bytes of it as will fit into |identifier|.
|
||||||
uint8_t identifier[kMDGUIDSize])
|
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
||||||
{
|
uint8_t identifier[kMDGUIDSize]) {
|
||||||
const void* text_section = NULL;
|
void* note_section;
|
||||||
int text_size = 0;
|
int note_size, elfclass;
|
||||||
bool success = false;
|
if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
|
||||||
if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) {
|
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||||
my_memset(identifier, 0, kMDGUIDSize);
|
note_size == 0) {
|
||||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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]) {
|
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
||||||
int fd = open(path_, O_RDONLY);
|
MemoryMappedFile mapped_file(path_);
|
||||||
if (fd < 0)
|
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||||
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)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool success = ElfFileIdentifierFromMappedFile(base, identifier);
|
return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||||
munmap(base, st.st_size);
|
|
||||||
return success;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -49,14 +49,15 @@ class FileID {
|
|||||||
// Load the identifier for the elf file path specified in the constructor into
|
// 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
|
// |identifier|. Return false if the identifier could not be created for the
|
||||||
// file.
|
// file.
|
||||||
// The current implementation will XOR the first 4096 bytes of the
|
// The current implementation will look for a .note.gnu.build-id
|
||||||
// .text section to generate an identifier.
|
// 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]);
|
bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]);
|
||||||
|
|
||||||
// Load the identifier for the elf file mapped into memory at |base| into
|
// 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
|
// |identifier|. Return false if the identifier could not be created for the
|
||||||
// file.
|
// file.
|
||||||
static bool ElfFileIdentifierFromMappedFile(void* base,
|
static bool ElfFileIdentifierFromMappedFile(const void* base,
|
||||||
uint8_t identifier[kMDGUIDSize]);
|
uint8_t identifier[kMDGUIDSize]);
|
||||||
|
|
||||||
// Convert the |identifier| data to a NULL terminated string. The string will
|
// Convert the |identifier| data to a NULL terminated string. The string will
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include "common/linux/guid_creator.h"
|
#include "common/linux/guid_creator.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -45,10 +46,6 @@
|
|||||||
//
|
//
|
||||||
class GUIDGenerator {
|
class GUIDGenerator {
|
||||||
public:
|
public:
|
||||||
GUIDGenerator() {
|
|
||||||
srandom(time(NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
|
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
|
||||||
return ((u_int32_t) bytes[0]
|
return ((u_int32_t) bytes[0]
|
||||||
| ((u_int32_t) bytes[1] << 8)
|
| ((u_int32_t) bytes[1] << 8)
|
||||||
@ -63,7 +60,8 @@ class GUIDGenerator {
|
|||||||
bytes[3] = (n >> 24) & 0xff;
|
bytes[3] = (n >> 24) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreateGUID(GUID *guid) const {
|
static bool CreateGUID(GUID *guid) {
|
||||||
|
InitOnce();
|
||||||
guid->data1 = random();
|
guid->data1 = random();
|
||||||
guid->data2 = (u_int16_t)(random());
|
guid->data2 = (u_int16_t)(random());
|
||||||
guid->data3 = (u_int16_t)(random());
|
guid->data3 = (u_int16_t)(random());
|
||||||
@ -71,13 +69,23 @@ class GUIDGenerator {
|
|||||||
UInt32ToBytes(&guid->data4[4], random());
|
UInt32ToBytes(&guid->data4[4], random());
|
||||||
return true;
|
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.
|
pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
|
||||||
const GUIDGenerator kGuidGenerator;
|
|
||||||
|
|
||||||
bool CreateGUID(GUID *guid) {
|
bool CreateGUID(GUID *guid) {
|
||||||
return kGuidGenerator.CreateGUID(guid);
|
return GUIDGenerator::CreateGUID(guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse guid to string.
|
// 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" {
|
extern "C" {
|
||||||
|
|
||||||
static inline size_t
|
extern size_t my_strlen(const char* s);
|
||||||
my_strlen(const char* s) {
|
|
||||||
size_t len = 0;
|
|
||||||
while (*s++) len++;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
extern int my_strcmp(const char* a, const char* b);
|
||||||
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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int
|
extern int my_strncmp(const char* a, const char* b, size_t len);
|
||||||
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.
|
// Parse a non-negative integer.
|
||||||
// result: (output) the resulting non-negative integer
|
// result: (output) the resulting non-negative integer
|
||||||
// s: a NUL terminated string
|
// s: a NUL terminated string
|
||||||
// Return true iff successful.
|
// Return true iff successful.
|
||||||
static inline bool
|
extern bool my_strtoui(int* result, const char* s);
|
||||||
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 the length of the given unsigned integer when expressed in base 10.
|
||||||
return true;
|
extern unsigned my_uint_len(uintmax_t i);
|
||||||
}
|
|
||||||
|
|
||||||
// Return the length of the given, non-negative integer when expressed in base
|
// Convert an unsigned integer to a string
|
||||||
// 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
|
|
||||||
// output: (output) the resulting string is written here. This buffer must be
|
// 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.
|
// required length.
|
||||||
// i: the non-negative integer to serialise.
|
// i: the unsigned integer to serialise.
|
||||||
// i_len: the length of the integer in base 10 (see |my_int_len|).
|
// i_len: the length of the integer in base 10 (see |my_uint_len|).
|
||||||
static inline void
|
extern void my_uitos(char* output, uintmax_t i, unsigned i_len);
|
||||||
my_itos(char* output, int i, unsigned i_len) {
|
|
||||||
for (unsigned index = i_len; index; --index, i /= 10)
|
|
||||||
output[index - 1] = '0' + (i % 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline const char*
|
extern const char* my_strchr(const char* haystack, char needle);
|
||||||
my_strchr(const char* haystack, char needle) {
|
|
||||||
while (*haystack && *haystack != needle)
|
extern const char* my_strrchr(const char* haystack, char needle);
|
||||||
haystack++;
|
|
||||||
if (*haystack == needle)
|
|
||||||
return haystack;
|
|
||||||
return (const char*) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a hex value
|
// Read a hex value
|
||||||
// result: (output) the resulting value
|
// result: (output) the resulting value
|
||||||
// s: a string
|
// s: a string
|
||||||
// Returns a pointer to the first invalid charactor.
|
// Returns a pointer to the first invalid charactor.
|
||||||
static inline const char*
|
extern const char* my_read_hex_ptr(uintptr_t* result, const char* s);
|
||||||
my_read_hex_ptr(uintptr_t* result, const char* s) {
|
|
||||||
uintptr_t r = 0;
|
|
||||||
|
|
||||||
for (;; ++s) {
|
extern const char* my_read_decimal_ptr(uintptr_t* result, const char* 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;
|
extern void my_memset(void* ip, char c, size_t len);
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
// The following are considered safe to use in a compromised environment.
|
||||||
my_memset(void* ip, char c, size_t len) {
|
// Besides, this gives the compiler an opportunity to optimize their calls.
|
||||||
char* p = (char *) ip;
|
#define my_memcpy memcpy
|
||||||
while (len--)
|
#define my_memmove memmove
|
||||||
*p++ = c;
|
#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"
|
} // 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)
|
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
|
||||||
SDKROOT = macosx10.5
|
SDKROOT = macosx10.5
|
||||||
SDKROOT[arch=i386] = macosx10.4
|
|
||||||
SDKROOT[arch=ppc] = macosx10.4
|
|
||||||
|
|
||||||
GCC_VERSION = 4.2
|
GCC_VERSION = 4.2
|
||||||
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0
|
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0
|
||||||
|
279
3rdparty/google-breakpad/common/mac/GTMDefines.h
vendored
279
3rdparty/google-breakpad/common/mac/GTMDefines.h
vendored
@ -1,4 +1,4 @@
|
|||||||
//
|
//
|
||||||
// GTMDefines.h
|
// GTMDefines.h
|
||||||
//
|
//
|
||||||
// Copyright 2008 Google Inc.
|
// Copyright 2008 Google Inc.
|
||||||
@ -6,21 +6,29 @@
|
|||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
// use this file except in compliance with the License. You may obtain a copy
|
// use this file except in compliance with the License. You may obtain a copy
|
||||||
// of the License at
|
// of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
// License for the specific language governing permissions and limitations under
|
// License for the specific language governing permissions and limitations under
|
||||||
// the License.
|
// the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
#include <AvailabilityMacros.h>
|
#include <AvailabilityMacros.h>
|
||||||
#include <TargetConditionals.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
|
// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs
|
||||||
#ifndef MAC_OS_X_VERSION_10_5
|
#ifndef MAC_OS_X_VERSION_10_5
|
||||||
#define MAC_OS_X_VERSION_10_5 1050
|
#define MAC_OS_X_VERSION_10_5 1050
|
||||||
@ -28,6 +36,29 @@
|
|||||||
#ifndef MAC_OS_X_VERSION_10_6
|
#ifndef MAC_OS_X_VERSION_10_6
|
||||||
#define MAC_OS_X_VERSION_10_6 1060
|
#define MAC_OS_X_VERSION_10_6 1060
|
||||||
#endif
|
#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
|
// CPP symbols that can be overridden in a prefix to control how the toolbox
|
||||||
@ -35,7 +66,7 @@
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
|
// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and
|
||||||
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
|
// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens
|
||||||
// when a validation fails. If you implement your own validators, you may want
|
// when a validation fails. If you implement your own validators, you may want
|
||||||
// to control their internals using the same macros for consistency.
|
// to control their internals using the same macros for consistency.
|
||||||
@ -47,7 +78,7 @@
|
|||||||
// a few different actual definitions, so we're based off of the foundation
|
// a few different actual definitions, so we're based off of the foundation
|
||||||
// one.
|
// one.
|
||||||
#if !defined(GTM_INLINE)
|
#if !defined(GTM_INLINE)
|
||||||
#if defined (__GNUC__) && (__GNUC__ == 4)
|
#if (defined (__GNUC__) && (__GNUC__ == 4)) || defined (__clang__)
|
||||||
#define GTM_INLINE static __inline__ __attribute__((always_inline))
|
#define GTM_INLINE static __inline__ __attribute__((always_inline))
|
||||||
#else
|
#else
|
||||||
#define GTM_INLINE static __inline__
|
#define GTM_INLINE static __inline__
|
||||||
@ -59,8 +90,12 @@
|
|||||||
#if !defined (GTM_EXTERN)
|
#if !defined (GTM_EXTERN)
|
||||||
#if defined __cplusplus
|
#if defined __cplusplus
|
||||||
#define GTM_EXTERN extern "C"
|
#define GTM_EXTERN extern "C"
|
||||||
|
#define GTM_EXTERN_C_BEGIN extern "C" {
|
||||||
|
#define GTM_EXTERN_C_END }
|
||||||
#else
|
#else
|
||||||
#define GTM_EXTERN extern
|
#define GTM_EXTERN extern
|
||||||
|
#define GTM_EXTERN_C_BEGIN
|
||||||
|
#define GTM_EXTERN_C_END
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -70,6 +105,12 @@
|
|||||||
#define GTM_EXPORT __attribute__((visibility("default")))
|
#define GTM_EXPORT __attribute__((visibility("default")))
|
||||||
#endif
|
#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
|
||||||
//
|
//
|
||||||
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
|
// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for
|
||||||
@ -82,12 +123,12 @@
|
|||||||
// _GTMDevLog log some error/problem in debug builds
|
// _GTMDevLog log some error/problem in debug builds
|
||||||
// _GTMDevAssert assert if conditon isn't met w/in a method/function
|
// _GTMDevAssert assert if conditon isn't met w/in a method/function
|
||||||
// in all builds.
|
// in all builds.
|
||||||
//
|
//
|
||||||
// To replace this system, just provide different macro definitions in your
|
// To replace this system, just provide different macro definitions in your
|
||||||
// prefix header. Remember, any implementation you provide *must* be thread
|
// prefix header. Remember, any implementation you provide *must* be thread
|
||||||
// safe since this could be called by anything in what ever situtation it has
|
// safe since this could be called by anything in what ever situtation it has
|
||||||
// been placed in.
|
// been placed in.
|
||||||
//
|
//
|
||||||
|
|
||||||
// We only define the simple macros if nothing else has defined this.
|
// We only define the simple macros if nothing else has defined this.
|
||||||
#ifndef _GTMDevLog
|
#ifndef _GTMDevLog
|
||||||
@ -100,11 +141,6 @@
|
|||||||
|
|
||||||
#endif // _GTMDevLog
|
#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
|
#ifndef _GTMDevAssert
|
||||||
// we directly invoke the NSAssert handler so we can pass on the varargs
|
// 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)
|
// (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) ]
|
typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ]
|
||||||
#endif // _GTMCompileAssert
|
#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
|
// 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
|
// simple things to test against w/o scattering the knowledge of project
|
||||||
@ -183,11 +197,26 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||||||
#else
|
#else
|
||||||
#define GTM_IPHONE_DEVICE 1
|
#define GTM_IPHONE_DEVICE 1
|
||||||
#endif // TARGET_IPHONE_SIMULATOR
|
#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
|
#else
|
||||||
// For MacOS specific stuff
|
// For MacOS specific stuff
|
||||||
#define GTM_MACOS_SDK 1
|
#define GTM_MACOS_SDK 1
|
||||||
#endif
|
#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
|
// Provide a symbol to include/exclude extra code for GC support. (This mainly
|
||||||
// just controls the inclusion of finalize methods).
|
// just controls the inclusion of finalize methods).
|
||||||
#ifndef GTM_SUPPORT_GC
|
#ifndef GTM_SUPPORT_GC
|
||||||
@ -197,7 +226,7 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||||||
#else
|
#else
|
||||||
// We can't find a symbol to tell if GC is supported/required, so best we
|
// 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.
|
// 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
|
#define GTM_SUPPORT_GC 0
|
||||||
#else
|
#else
|
||||||
#define GTM_SUPPORT_GC 1
|
#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
|
// To simplify support for 64bit (and Leopard in general), we provide the type
|
||||||
// defines for non Leopard SDKs
|
// 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
|
// NSInteger/NSUInteger and Max/Mins
|
||||||
#ifndef NSINTEGER_DEFINED
|
#ifndef NSINTEGER_DEFINED
|
||||||
#if __LP64__ || NS_BUILD_32_LIKE_64
|
#if __LP64__ || NS_BUILD_32_LIKE_64
|
||||||
@ -238,4 +267,178 @@ GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...);
|
|||||||
#endif /* !defined(__LP64__) || !__LP64__ */
|
#endif /* !defined(__LP64__) || !__LP64__ */
|
||||||
#define CGFLOAT_DEFINED 1
|
#define CGFLOAT_DEFINED 1
|
||||||
#endif // CGFLOAT_DEFINED
|
#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__
|
||||||
|
128
3rdparty/google-breakpad/common/mac/GTMLogger.h
vendored
128
3rdparty/google-breakpad/common/mac/GTMLogger.h
vendored
@ -6,9 +6,9 @@
|
|||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
// use this file except in compliance with the License. You may obtain a copy
|
// use this file except in compliance with the License. You may obtain a copy
|
||||||
// of the License at
|
// of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
@ -18,15 +18,15 @@
|
|||||||
|
|
||||||
// Key Abstractions
|
// Key Abstractions
|
||||||
// ----------------
|
// ----------------
|
||||||
//
|
//
|
||||||
// This file declares multiple classes and protocols that are used by the
|
// This file declares multiple classes and protocols that are used by the
|
||||||
// GTMLogger logging system. The 4 main abstractions used in this file are the
|
// GTMLogger logging system. The 4 main abstractions used in this file are the
|
||||||
// following:
|
// following:
|
||||||
//
|
//
|
||||||
// * logger (GTMLogger) - The main logging class that users interact with. It
|
// * logger (GTMLogger) - The main logging class that users interact with. It
|
||||||
// has methods for logging at different levels and uses a log writer, a log
|
// has methods for logging at different levels and uses a log writer, a log
|
||||||
// formatter, and a log filter to get the job done.
|
// formatter, and a log filter to get the job done.
|
||||||
//
|
//
|
||||||
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
|
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
|
||||||
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
|
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
|
||||||
// or even some in-memory structure (e.g., a ring buffer).
|
// or even some in-memory structure (e.g., a ring buffer).
|
||||||
@ -44,7 +44,7 @@
|
|||||||
// flexibility to dynamically enable debug logging in Release builds.
|
// flexibility to dynamically enable debug logging in Release builds.
|
||||||
//
|
//
|
||||||
// This file also declares some classes to handle the common log writer, log
|
// This file also declares some classes to handle the common log writer, log
|
||||||
// formatter, and log filter cases. Callers can also create their own writers,
|
// formatter, and log filter cases. Callers can also create their own writers,
|
||||||
// formatters, and filters and they can even build them on top of the ones
|
// formatters, and filters and they can even build them on top of the ones
|
||||||
// declared here. Keep in mind that your custom writer/formatter/filter may be
|
// declared here. Keep in mind that your custom writer/formatter/filter may be
|
||||||
// called from multiple threads, so it must be thread-safe.
|
// called from multiple threads, so it must be thread-safe.
|
||||||
@ -55,12 +55,6 @@
|
|||||||
// Predeclaration of used protocols that are declared later in this file.
|
// Predeclaration of used protocols that are declared later in this file.
|
||||||
@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
|
@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
|
||||||
//
|
//
|
||||||
// GTMLogger is the primary user-facing class for an object-oriented logging
|
// GTMLogger is the primary user-facing class for an object-oriented logging
|
||||||
@ -69,7 +63,7 @@
|
|||||||
// sent to a GTMLogger to log a message, the message is formatted using the log
|
// sent to a GTMLogger to log a message, the message is formatted using the log
|
||||||
// formatter, then the log filter is consulted to see if the message should be
|
// formatter, then the log filter is consulted to see if the message should be
|
||||||
// logged, and if so, the message is sent to the log writer to be written out.
|
// logged, and if so, the message is sent to the log writer to be written out.
|
||||||
//
|
//
|
||||||
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
|
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
|
||||||
// flexibility comes from the fact that GTMLogger instances can be customized
|
// flexibility comes from the fact that GTMLogger instances can be customized
|
||||||
// with user defined formatters, filters, and writers. And these writers,
|
// with user defined formatters, filters, and writers. And these writers,
|
||||||
@ -77,7 +71,7 @@
|
|||||||
// ways to suit the needs at hand. For example, multiple writers can be used at
|
// ways to suit the needs at hand. For example, multiple writers can be used at
|
||||||
// the same time, and a GTMLogger instance can even be used as another
|
// the same time, and a GTMLogger instance can even be used as another
|
||||||
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
|
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
|
||||||
//
|
//
|
||||||
// A standard GTMLogger uses a writer that sends messages to standard out, a
|
// A standard GTMLogger uses a writer that sends messages to standard out, a
|
||||||
// formatter that smacks a timestamp and a few other bits of interesting
|
// formatter that smacks a timestamp and a few other bits of interesting
|
||||||
// information on the message, and a filter that filters out debug messages from
|
// information on the message, and a filter that filters out debug messages from
|
||||||
@ -85,13 +79,13 @@
|
|||||||
// the following:
|
// the following:
|
||||||
//
|
//
|
||||||
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
|
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
|
||||||
//
|
//
|
||||||
// The output contains the date and time of the log message, the name of the
|
// The output contains the date and time of the log message, the name of the
|
||||||
// process followed by its process ID/thread ID, the log level at which the
|
// process followed by its process ID/thread ID, the log level at which the
|
||||||
// message was logged (in the previous example the level was 1:
|
// message was logged (in the previous example the level was 1:
|
||||||
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
|
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
|
||||||
// this case, the log message was @"foo=%@", foo).
|
// this case, the log message was @"foo=%@", foo).
|
||||||
//
|
//
|
||||||
// Multiple instances of GTMLogger can be created, each configured their own
|
// Multiple instances of GTMLogger can be created, each configured their own
|
||||||
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
|
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
|
||||||
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
|
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
|
||||||
@ -113,10 +107,10 @@
|
|||||||
// with behavior that many developers are currently used to. Note that this
|
// with behavior that many developers are currently used to. Note that this
|
||||||
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
|
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
|
||||||
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
|
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
|
||||||
//
|
//
|
||||||
// Standard loggers are created with the GTMLogLevelFilter log filter, which
|
// Standard loggers are created with the GTMLogLevelFilter log filter, which
|
||||||
// filters out certain log messages based on log level, and some other settings.
|
// filters out certain log messages based on log level, and some other settings.
|
||||||
//
|
//
|
||||||
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
|
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
|
||||||
// GTMLogger itself, there are also C macros that make usage of the shared
|
// GTMLogger itself, there are also C macros that make usage of the shared
|
||||||
// GTMLogger instance very convenient. These macros are:
|
// GTMLogger instance very convenient. These macros are:
|
||||||
@ -146,7 +140,7 @@
|
|||||||
// GTMLogger class directly in order to configure the shared logger, which all
|
// GTMLogger class directly in order to configure the shared logger, which all
|
||||||
// of the code using the macros will be using. Again, this is just the typical
|
// of the code using the macros will be using. Again, this is just the typical
|
||||||
// situation.
|
// situation.
|
||||||
//
|
//
|
||||||
// To be complete, there are cases where you may want to use GTMLogger directly,
|
// To be complete, there are cases where you may want to use GTMLogger directly,
|
||||||
// or even create separate GTMLogger instances for some reason. That's fine,
|
// or even create separate GTMLogger instances for some reason. That's fine,
|
||||||
// too.
|
// too.
|
||||||
@ -160,14 +154,14 @@
|
|||||||
//
|
//
|
||||||
// GTMLoggerDebug(@"foo = %@", foo);
|
// GTMLoggerDebug(@"foo = %@", foo);
|
||||||
//
|
//
|
||||||
// 2. The previous example is similar to the following. The major difference is
|
// 2. The previous example is similar to the following. The major difference is
|
||||||
// that the previous call (example 1) will be compiled out of Release builds
|
// that the previous call (example 1) will be compiled out of Release builds
|
||||||
// but this statement will not be compiled out.
|
// but this statement will not be compiled out.
|
||||||
//
|
//
|
||||||
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
|
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
|
||||||
//
|
//
|
||||||
// 3. Send all logging output from the shared logger to a file. We do this by
|
// 3. Send all logging output from the shared logger to a file. We do this by
|
||||||
// creating an NSFileHandle for writing associated with a file, and setting
|
// creating an NSFileHandle for writing associated with a file, and setting
|
||||||
// that file handle as the logger's writer.
|
// that file handle as the logger's writer.
|
||||||
//
|
//
|
||||||
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
|
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
|
||||||
@ -185,12 +179,12 @@
|
|||||||
// 5. Create a logger that writes to stdout and does NOT do any formatting to
|
// 5. Create a logger that writes to stdout and does NOT do any formatting to
|
||||||
// the log message. This might be useful, for example, when writing a help
|
// the log message. This might be useful, for example, when writing a help
|
||||||
// screen for a command-line tool to standard output.
|
// screen for a command-line tool to standard output.
|
||||||
//
|
//
|
||||||
// GTMLogger *logger = [GTMLogger logger];
|
// GTMLogger *logger = [GTMLogger logger];
|
||||||
// [logger logInfo:@"%@ version 0.1 usage", progName];
|
// [logger logInfo:@"%@ version 0.1 usage", progName];
|
||||||
//
|
//
|
||||||
// 6. Send log output to stdout AND to a log file. The trick here is that
|
// 6. Send log output to stdout AND to a log file. The trick here is that
|
||||||
// NSArrays function as composite log writers, which means when an array is
|
// NSArrays function as composite log writers, which means when an array is
|
||||||
// set as the log writer, it forwards all logging messages to all of its
|
// set as the log writer, it forwards all logging messages to all of its
|
||||||
// contained GTMLogWriters.
|
// contained GTMLogWriters.
|
||||||
//
|
//
|
||||||
@ -198,7 +192,7 @@
|
|||||||
// NSArray *writers = [NSArray arrayWithObjects:
|
// NSArray *writers = [NSArray arrayWithObjects:
|
||||||
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
|
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
|
||||||
// [NSFileHandle fileHandleWithStandardOutput], nil];
|
// [NSFileHandle fileHandleWithStandardOutput], nil];
|
||||||
//
|
//
|
||||||
// GTMLogger *logger = [GTMLogger standardLogger];
|
// GTMLogger *logger = [GTMLogger standardLogger];
|
||||||
// [logger setWriter:writers];
|
// [logger setWriter:writers];
|
||||||
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
|
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
|
||||||
@ -244,6 +238,10 @@
|
|||||||
// Same as +standardLogger, but logs to stderr.
|
// Same as +standardLogger, but logs to stderr.
|
||||||
+ (id)standardLoggerWithStderr;
|
+ (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
|
// Returns a new standard GTMLogger instance with a log writer that will
|
||||||
// write to the file at |path|, and will use the GTMLogStandardFormatter and
|
// write to the file at |path|, and will use the GTMLogStandardFormatter and
|
||||||
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
|
// GTMLogLevelFilter classes. If |path| does not exist, it will be created.
|
||||||
@ -274,20 +272,20 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// Logs a message at the debug level (kGTMLoggerLevelDebug).
|
// 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).
|
// 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).
|
// 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).
|
// 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);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Accessors
|
// Accessors
|
||||||
//
|
//
|
||||||
|
|
||||||
// Accessor methods for the log writer. If the log writer is set to nil,
|
// Accessor methods for the log writer. If the log writer is set to nil,
|
||||||
// [NSFileHandle fileHandleWithStandardOutput] is used.
|
// [NSFileHandle fileHandleWithStandardOutput] is used.
|
||||||
- (id<GTMLogWriter>)writer;
|
- (id<GTMLogWriter>)writer;
|
||||||
- (void)setWriter:(id<GTMLogWriter>)writer;
|
- (void)setWriter:(id<GTMLogWriter>)writer;
|
||||||
@ -306,20 +304,23 @@
|
|||||||
@end // GTMLogger
|
@end // GTMLogger
|
||||||
|
|
||||||
|
|
||||||
// Helper functions that are used by the convenience GTMLogger*() macros that
|
// Helper functions that are used by the convenience GTMLogger*() macros that
|
||||||
// enable the logging of function names.
|
// enable the logging of function names.
|
||||||
@interface GTMLogger (GTMLoggerMacroHelpers)
|
@interface GTMLogger (GTMLoggerMacroHelpers)
|
||||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
|
- (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, ...
|
- (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, ...
|
- (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, ...
|
- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
|
||||||
CHECK_FORMAT_NSSTRING(2, 3);
|
NS_FORMAT_FUNCTION(2, 3);
|
||||||
@end // GTMLoggerMacroHelpers
|
@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
|
// Convenience macros that log to the shared GTMLogger instance. These macros
|
||||||
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
|
// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
|
||||||
// calls will be compiled out of non-Debug builds.
|
// calls will be compiled out of non-Debug builds.
|
||||||
@ -339,6 +340,8 @@
|
|||||||
#define GTMLoggerDebug(...) do {} while(0)
|
#define GTMLoggerDebug(...) do {} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif // !defined(GTMLoggerInfo)
|
||||||
|
|
||||||
// Log levels.
|
// Log levels.
|
||||||
typedef enum {
|
typedef enum {
|
||||||
kGTMLoggerLevelUnknown,
|
kGTMLoggerLevelUnknown,
|
||||||
@ -365,7 +368,7 @@ typedef enum {
|
|||||||
// now becomes a valid log writer. Log messages are written to the file handle
|
// now becomes a valid log writer. Log messages are written to the file handle
|
||||||
// with a newline appended.
|
// with a newline appended.
|
||||||
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
|
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
|
||||||
// Opens the file at |path| in append mode, and creates the file with |mode|
|
// Opens the file at |path| in append mode, and creates the file with |mode|
|
||||||
// if it didn't previously exist.
|
// if it didn't previously exist.
|
||||||
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
|
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
|
||||||
@end // NSFileHandle
|
@end // NSFileHandle
|
||||||
@ -379,7 +382,7 @@ typedef enum {
|
|||||||
//
|
//
|
||||||
// This is useful in situations where you would like to send log output to
|
// This is useful in situations where you would like to send log output to
|
||||||
// multiple log writers at the same time. Simply create an NSArray of the log
|
// multiple log writers at the same time. Simply create an NSArray of the log
|
||||||
// writers you wish to use, then set the array as the "writer" for your
|
// writers you wish to use, then set the array as the "writer" for your
|
||||||
// GTMLogger instance.
|
// GTMLogger instance.
|
||||||
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
|
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
|
||||||
@end // GTMArrayCompositeLogWriter
|
@end // GTMArrayCompositeLogWriter
|
||||||
@ -390,7 +393,7 @@ typedef enum {
|
|||||||
//
|
//
|
||||||
// This is useful when you want to configure a logger to log to a specific
|
// This is useful when you want to configure a logger to log to a specific
|
||||||
// writer with a specific formatter and/or filter. But you want to also compose
|
// writer with a specific formatter and/or filter. But you want to also compose
|
||||||
// that with a different log writer that may have its own formatter and/or
|
// that with a different log writer that may have its own formatter and/or
|
||||||
// filter.
|
// filter.
|
||||||
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
|
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
|
||||||
@end // GTMLoggerLogWriter
|
@end // GTMLoggerLogWriter
|
||||||
@ -407,14 +410,18 @@ typedef enum {
|
|||||||
- (NSString *)stringForFunc:(NSString *)func
|
- (NSString *)stringForFunc:(NSString *)func
|
||||||
withFormat:(NSString *)fmt
|
withFormat:(NSString *)fmt
|
||||||
valist:(va_list)args
|
valist:(va_list)args
|
||||||
level:(GTMLoggerLevel)level;
|
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
|
||||||
@end // GTMLogFormatter
|
@end // GTMLogFormatter
|
||||||
|
|
||||||
|
|
||||||
// A basic log formatter that formats a string the same way that NSLog (or
|
// A basic log formatter that formats a string the same way that NSLog (or
|
||||||
// printf) would. It does not do anything fancy, nor does it add any data of its
|
// printf) would. It does not do anything fancy, nor does it add any data of its
|
||||||
// own.
|
// own.
|
||||||
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
|
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
|
||||||
|
|
||||||
|
// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
|
||||||
|
- (NSString *)prettyNameForFunc:(NSString *)func;
|
||||||
|
|
||||||
@end // GTMLogBasicFormatter
|
@end // GTMLogBasicFormatter
|
||||||
|
|
||||||
|
|
||||||
@ -450,9 +457,48 @@ typedef enum {
|
|||||||
@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
|
@interface GTMLogLevelFilter : NSObject <GTMLogFilter>
|
||||||
@end // GTMLogLevelFilter
|
@end // GTMLogLevelFilter
|
||||||
|
|
||||||
|
|
||||||
// A simple log filter that does NOT filter anything out;
|
// A simple log filter that does NOT filter anything out;
|
||||||
// -filterAllowsMessage:level will always return YES. This can be a convenient
|
// -filterAllowsMessage:level will always return YES. This can be a convenient
|
||||||
// way to enable debug-level logging in release builds (if you so desire).
|
// way to enable debug-level logging in release builds (if you so desire).
|
||||||
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
|
@interface GTMLogNoFilter : NSObject <GTMLogFilter>
|
||||||
@end // GTMLogNoFilter
|
@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
|
||||||
|
|
||||||
|
371
3rdparty/google-breakpad/common/mac/GTMLogger.m
vendored
371
3rdparty/google-breakpad/common/mac/GTMLogger.m
vendored
@ -6,9 +6,9 @@
|
|||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
// use this file except in compliance with the License. You may obtain a copy
|
// use this file except in compliance with the License. You may obtain a copy
|
||||||
// of the License at
|
// of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
@ -24,39 +24,30 @@
|
|||||||
#import <pthread.h>
|
#import <pthread.h>
|
||||||
|
|
||||||
|
|
||||||
// Define a trivial assertion macro to avoid dependencies
|
#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42)
|
||||||
#ifdef DEBUG
|
// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting
|
||||||
#define GTMLOGGER_ASSERT(expr) assert(expr)
|
// -Wmissing-format-attribute
|
||||||
#else
|
// when the function is anything more complex than foo(NSString *fmt, ...).
|
||||||
#define GTMLOGGER_ASSERT(expr)
|
// You see the error inside the function when you turn ... into va_args and
|
||||||
#endif
|
// 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
|
||||||
@interface GTMLogger (PrivateMethods)
|
|
||||||
|
|
||||||
- (void)logInternalFunc:(const char *)func
|
|
||||||
format:(NSString *)fmt
|
|
||||||
valist:(va_list)args
|
|
||||||
level:(GTMLoggerLevel)level;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
// Reference to the shared GTMLogger instance. This is not a singleton, it's
|
|
||||||
// just an easy reference to one shared instance.
|
// just an easy reference to one shared instance.
|
||||||
static GTMLogger *gSharedLogger = nil;
|
static GTMLogger *gSharedLogger = nil;
|
||||||
|
|
||||||
|
|
||||||
@implementation GTMLogger
|
@implementation GTMLogger
|
||||||
|
|
||||||
// Returns a pointer to the shared logger instance. If none exists, a standard
|
// Returns a pointer to the shared logger instance. If none exists, a standard
|
||||||
// logger is created and returned.
|
// logger is created and returned.
|
||||||
+ (id)sharedLogger {
|
+ (id)sharedLogger {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
if (gSharedLogger == nil) {
|
if (gSharedLogger == nil) {
|
||||||
gSharedLogger = [[self standardLogger] retain];
|
gSharedLogger = [[self standardLogger] retain];
|
||||||
}
|
}
|
||||||
GTMLOGGER_ASSERT(gSharedLogger != nil);
|
|
||||||
}
|
}
|
||||||
return [[gSharedLogger retain] autorelease];
|
return [[gSharedLogger retain] autorelease];
|
||||||
}
|
}
|
||||||
@ -69,24 +60,85 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ (id)standardLogger {
|
+ (id)standardLogger {
|
||||||
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
|
// Don't trust NSFileHandle not to throw
|
||||||
id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease];
|
@try {
|
||||||
id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease];
|
id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput];
|
||||||
return [self loggerWithWriter:writer formatter:fr filter:filter];
|
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)standardLoggerWithStderr {
|
||||||
id me = [self standardLogger];
|
// Don't trust NSFileHandle not to throw
|
||||||
[me setWriter:[NSFileHandle fileHandleWithStandardError]];
|
@try {
|
||||||
return me;
|
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 {
|
+ (id)standardLoggerWithPath:(NSString *)path {
|
||||||
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
|
@try {
|
||||||
if (fh == nil) return nil;
|
NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644];
|
||||||
id me = [self standardLogger];
|
if (fh == nil) return nil;
|
||||||
[me setWriter:fh];
|
id me = [self standardLogger];
|
||||||
return me;
|
[me setWriter:fh];
|
||||||
|
return me;
|
||||||
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
|
+ (id)loggerWithWriter:(id<GTMLogWriter>)writer
|
||||||
@ -112,69 +164,85 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
[self setWriter:writer];
|
[self setWriter:writer];
|
||||||
[self setFormatter:formatter];
|
[self setFormatter:formatter];
|
||||||
[self setFilter:filter];
|
[self setFilter:filter];
|
||||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
|
||||||
GTMLOGGER_ASSERT(filter_ != nil);
|
|
||||||
GTMLOGGER_ASSERT(writer_ != nil);
|
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
GTMLOGGER_ASSERT(writer_ != nil);
|
// Unlikely, but |writer_| may be an NSFileHandle, which can throw
|
||||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
@try {
|
||||||
GTMLOGGER_ASSERT(filter_ != nil);
|
[formatter_ release];
|
||||||
[writer_ release];
|
[filter_ release];
|
||||||
[formatter_ release];
|
[writer_ release];
|
||||||
[filter_ release];
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<GTMLogWriter>)writer {
|
- (id<GTMLogWriter>)writer {
|
||||||
GTMLOGGER_ASSERT(writer_ != nil);
|
|
||||||
return [[writer_ retain] autorelease];
|
return [[writer_ retain] autorelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setWriter:(id<GTMLogWriter>)writer {
|
- (void)setWriter:(id<GTMLogWriter>)writer {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
[writer_ autorelease];
|
[writer_ autorelease];
|
||||||
if (writer == nil)
|
writer_ = nil;
|
||||||
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
if (writer == nil) {
|
||||||
else
|
// Try to use stdout, but don't trust NSFileHandle
|
||||||
|
@try {
|
||||||
|
writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain];
|
||||||
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Leave |writer_| nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
writer_ = [writer retain];
|
writer_ = [writer retain];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GTMLOGGER_ASSERT(writer_ != nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<GTMLogFormatter>)formatter {
|
- (id<GTMLogFormatter>)formatter {
|
||||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
|
||||||
return [[formatter_ retain] autorelease];
|
return [[formatter_ retain] autorelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
|
- (void)setFormatter:(id<GTMLogFormatter>)formatter {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
[formatter_ autorelease];
|
[formatter_ autorelease];
|
||||||
if (formatter == nil)
|
formatter_ = nil;
|
||||||
formatter_ = [[GTMLogBasicFormatter alloc] init];
|
if (formatter == nil) {
|
||||||
else
|
@try {
|
||||||
|
formatter_ = [[GTMLogBasicFormatter alloc] init];
|
||||||
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Leave |formatter_| nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
formatter_ = [formatter retain];
|
formatter_ = [formatter retain];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<GTMLogFilter>)filter {
|
- (id<GTMLogFilter>)filter {
|
||||||
GTMLOGGER_ASSERT(filter_ != nil);
|
|
||||||
return [[filter_ retain] autorelease];
|
return [[filter_ retain] autorelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setFilter:(id<GTMLogFilter>)filter {
|
- (void)setFilter:(id<GTMLogFilter>)filter {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
[filter_ autorelease];
|
[filter_ autorelease];
|
||||||
if (filter == nil)
|
filter_ = nil;
|
||||||
filter_ = [[GTMLogNoFilter alloc] init];
|
if (filter == nil) {
|
||||||
else
|
@try {
|
||||||
|
filter_ = [[GTMLogNoFilter alloc] init];
|
||||||
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Leave |filter_| nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
filter_ = [filter retain];
|
filter_ = [filter retain];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GTMLOGGER_ASSERT(filter_ != nil);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)logDebug:(NSString *)fmt, ... {
|
- (void)logDebug:(NSString *)fmt, ... {
|
||||||
@ -207,7 +275,6 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
|
|
||||||
@end // GTMLogger
|
@end // GTMLogger
|
||||||
|
|
||||||
|
|
||||||
@implementation GTMLogger (GTMLoggerMacroHelpers)
|
@implementation GTMLogger (GTMLoggerMacroHelpers)
|
||||||
|
|
||||||
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
|
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... {
|
||||||
@ -240,24 +307,26 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
|
|
||||||
@end // GTMLoggerMacroHelpers
|
@end // GTMLoggerMacroHelpers
|
||||||
|
|
||||||
|
|
||||||
@implementation GTMLogger (PrivateMethods)
|
@implementation GTMLogger (PrivateMethods)
|
||||||
|
|
||||||
- (void)logInternalFunc:(const char *)func
|
- (void)logInternalFunc:(const char *)func
|
||||||
format:(NSString *)fmt
|
format:(NSString *)fmt
|
||||||
valist:(va_list)args
|
valist:(va_list)args
|
||||||
level:(GTMLoggerLevel)level {
|
level:(GTMLoggerLevel)level {
|
||||||
GTMLOGGER_ASSERT(formatter_ != nil);
|
// Primary point where logging happens, logging should never throw, catch
|
||||||
GTMLOGGER_ASSERT(filter_ != nil);
|
// everything.
|
||||||
GTMLOGGER_ASSERT(writer_ != nil);
|
@try {
|
||||||
|
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
|
||||||
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
|
NSString *msg = [formatter_ stringForFunc:fname
|
||||||
NSString *msg = [formatter_ stringForFunc:fname
|
withFormat:fmt
|
||||||
withFormat:fmt
|
valist:args
|
||||||
valist:args
|
level:level];
|
||||||
level:level];
|
if (msg && [filter_ filterAllowsMessage:msg level:level])
|
||||||
if (msg && [filter_ filterAllowsMessage:msg level:level])
|
[writer_ logMessage:msg level:level];
|
||||||
[writer_ logMessage:msg level:level];
|
}
|
||||||
|
@catch (id e) {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@end // PrivateMethods
|
@end // PrivateMethods
|
||||||
@ -278,8 +347,16 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
|
|
||||||
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
|
// Closed pipes should not generate exceptions in our caller. Catch here
|
||||||
[self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,18 +383,18 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case kGTMLoggerLevelDebug:
|
case kGTMLoggerLevelDebug:
|
||||||
[self logDebug:@"%@", msg];
|
[self logDebug:@"%@", msg];
|
||||||
break;
|
break;
|
||||||
case kGTMLoggerLevelInfo:
|
case kGTMLoggerLevelInfo:
|
||||||
[self logInfo:@"%@", msg];
|
[self logInfo:@"%@", msg];
|
||||||
break;
|
break;
|
||||||
case kGTMLoggerLevelError:
|
case kGTMLoggerLevelError:
|
||||||
[self logError:@"%@", msg];
|
[self logError:@"%@", msg];
|
||||||
break;
|
break;
|
||||||
case kGTMLoggerLevelAssert:
|
case kGTMLoggerLevelAssert:
|
||||||
[self logAssert:@"%@", msg];
|
[self logAssert:@"%@", msg];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore the message.
|
// Ignore the message.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -328,19 +405,32 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
|
|
||||||
@implementation GTMLogBasicFormatter
|
@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
|
- (NSString *)stringForFunc:(NSString *)func
|
||||||
withFormat:(NSString *)fmt
|
withFormat:(NSString *)fmt
|
||||||
valist:(va_list)args
|
valist:(va_list)args
|
||||||
level:(GTMLoggerLevel)level {
|
level:(GTMLoggerLevel)level {
|
||||||
// Performance note: since we always have to create a new NSString from the
|
// Performance note: We may want to do a quick check here to see if |fmt|
|
||||||
// returned CFStringRef, we may want to do a quick check here to see if |fmt|
|
// contains a '%', and if not, simply return 'fmt'.
|
||||||
// contains a '%', and if not, simply return 'fmt'.
|
if (!(fmt && args)) return nil;
|
||||||
CFStringRef cfmsg = NULL;
|
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
|
||||||
cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault,
|
|
||||||
NULL, // format options
|
|
||||||
(CFStringRef)fmt,
|
|
||||||
args);
|
|
||||||
return GTMCFAutorelease(cfmsg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end // GTMLogBasicFormatter
|
@end // GTMLogBasicFormatter
|
||||||
@ -355,6 +445,10 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
|
[dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"];
|
||||||
pname_ = [[[NSProcessInfo processInfo] processName] copy];
|
pname_ = [[[NSProcessInfo processInfo] processName] copy];
|
||||||
pid_ = [[NSProcessInfo processInfo] processIdentifier];
|
pid_ = [[NSProcessInfo processInfo] processIdentifier];
|
||||||
|
if (!(dateFormatter_ && pname_)) {
|
||||||
|
[self release];
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -367,17 +461,17 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
|
|
||||||
- (NSString *)stringForFunc:(NSString *)func
|
- (NSString *)stringForFunc:(NSString *)func
|
||||||
withFormat:(NSString *)fmt
|
withFormat:(NSString *)fmt
|
||||||
valist:(va_list)args
|
valist:(va_list)args
|
||||||
level:(GTMLoggerLevel)level {
|
level:(GTMLoggerLevel)level {
|
||||||
GTMLOGGER_ASSERT(dateFormatter_ != nil);
|
|
||||||
NSString *tstamp = nil;
|
NSString *tstamp = nil;
|
||||||
@synchronized (dateFormatter_) {
|
@synchronized (dateFormatter_) {
|
||||||
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
|
tstamp = [dateFormatter_ stringFromDate:[NSDate date]];
|
||||||
}
|
}
|
||||||
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
|
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
|
||||||
tstamp, pname_, pid_, pthread_self(),
|
tstamp, pname_, pid_, pthread_self(),
|
||||||
level, (func ? func : @"(no func)"),
|
level, [self prettyNameForFunc:func],
|
||||||
[super stringForFunc:func withFormat:fmt valist:args level:level]];
|
// |super| has guard for nil |fmt| and |args|
|
||||||
|
[super stringForFunc:func withFormat:fmt valist:args level:level]];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end // GTMLogStandardFormatter
|
@end // GTMLogStandardFormatter
|
||||||
@ -391,14 +485,20 @@ static GTMLogger *gSharedLogger = nil;
|
|||||||
// COV_NF_START
|
// COV_NF_START
|
||||||
static BOOL IsVerboseLoggingEnabled(void) {
|
static BOOL IsVerboseLoggingEnabled(void) {
|
||||||
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
|
static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging";
|
||||||
static char *env = NULL;
|
NSString *value = [[[NSProcessInfo processInfo] environment]
|
||||||
if (env == NULL)
|
objectForKey:kVerboseLoggingKey];
|
||||||
env = getenv([kVerboseLoggingKey UTF8String]);
|
if (value) {
|
||||||
|
// Emulate [NSString boolValue] for pre-10.5
|
||||||
if (env && env[0]) {
|
value = [value stringByTrimmingCharactersInSet:
|
||||||
return (strtol(env, NULL, 10) != 0);
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||||
|
if ([[value uppercaseString] hasPrefix:@"Y"] ||
|
||||||
|
[[value uppercaseString] hasPrefix:@"T"] ||
|
||||||
|
[value intValue]) {
|
||||||
|
return YES;
|
||||||
|
} else {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
|
return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey];
|
||||||
}
|
}
|
||||||
// COV_NF_END
|
// COV_NF_END
|
||||||
@ -409,15 +509,15 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
return YES;
|
return YES;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
BOOL allow = YES;
|
BOOL allow = YES;
|
||||||
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case kGTMLoggerLevelDebug:
|
case kGTMLoggerLevelDebug:
|
||||||
allow = NO;
|
allow = NO;
|
||||||
break;
|
break;
|
||||||
case kGTMLoggerLevelInfo:
|
case kGTMLoggerLevelInfo:
|
||||||
allow = (IsVerboseLoggingEnabled() == YES);
|
allow = IsVerboseLoggingEnabled();
|
||||||
break;
|
break;
|
||||||
case kGTMLoggerLevelError:
|
case kGTMLoggerLevelError:
|
||||||
allow = YES;
|
allow = YES;
|
||||||
@ -443,3 +543,70 @@ static BOOL IsVerboseLoggingEnabled(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end // GTMLogNoFilter
|
@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
|
// Each file is sent with a name field in addition to the filename and data
|
||||||
// The data will be sent synchronously.
|
// The data will be sent synchronously.
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@interface HTTPMultipartUpload : NSObject {
|
@interface HTTPMultipartUpload : NSObject {
|
||||||
@protected
|
@protected
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
@interface HTTPMultipartUpload(PrivateMethods)
|
@interface HTTPMultipartUpload(PrivateMethods)
|
||||||
- (NSString *)multipartBoundary;
|
- (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 *)formDataForKey:(NSString *)key value:(NSString *)value;
|
||||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
|
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name;
|
||||||
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
|
- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name;
|
||||||
@ -67,11 +69,9 @@
|
|||||||
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
|
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
|
||||||
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
|
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
|
||||||
NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
|
NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
|
||||||
NSString *post = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
|
|
||||||
|
|
||||||
[data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
|
[data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
[data appendData:contents];
|
[data appendData:contents];
|
||||||
[data appendData:[post dataUsingEncoding:NSUTF8StringEncoding]];
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -182,6 +182,9 @@
|
|||||||
[postBody appendData:fileData];
|
[postBody appendData:fileData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
|
||||||
|
[postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
|
||||||
|
|
||||||
[req setHTTPBody:postBody];
|
[req setHTTPBody:postBody];
|
||||||
[req setHTTPMethod:@"POST"];
|
[req setHTTPMethod:@"POST"];
|
||||||
|
|
||||||
@ -193,7 +196,8 @@
|
|||||||
error:error];
|
error:error];
|
||||||
|
|
||||||
[response_ retain];
|
[response_ retain];
|
||||||
|
[req release];
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +139,6 @@ class MachMsgPortDescriptor : public mach_msg_port_descriptor_t {
|
|||||||
return disposition;
|
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
|
// For convenience
|
||||||
operator mach_port_t() const {
|
operator mach_port_t() const {
|
||||||
return GetMachPort();
|
return GetMachPort();
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#import <stdio.h>
|
#import <stdio.h>
|
||||||
#import "MachIPC.h"
|
#import "MachIPC.h"
|
||||||
|
#include "common/mac/bootstrap_compat.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@ -187,9 +188,10 @@ ReceivePort::ReceivePort(const char *receive_port_name) {
|
|||||||
if (init_result_ != KERN_SUCCESS)
|
if (init_result_ != KERN_SUCCESS)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
init_result_ = bootstrap_register(bootstrap_port,
|
init_result_ = breakpad::BootstrapRegister(
|
||||||
const_cast<char*>(receive_port_name),
|
bootstrap_port,
|
||||||
port_);
|
const_cast<char*>(receive_port_name),
|
||||||
|
port_);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -134,7 +134,7 @@ class SimpleStringDictionary {
|
|||||||
// Given |key|, returns its corresponding |value|.
|
// Given |key|, returns its corresponding |value|.
|
||||||
// If |key| is NULL, an assert will fire or NULL will be returned. If |key|
|
// 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.
|
// 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
|
// 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
|
// 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);
|
assert(key);
|
||||||
if (!key)
|
if (!key)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for (int i = 0; i < MAX_NUM_ENTRIES; ++i) {
|
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)) {
|
if (entry.IsActive() && !strcmp(entry.GetKey(), key)) {
|
||||||
return entry.GetValue();
|
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_
|
25
3rdparty/google-breakpad/common/mac/dump_syms.h
vendored
25
3rdparty/google-breakpad/common/mac/dump_syms.h
vendored
@ -1,6 +1,6 @@
|
|||||||
// -*- mode: c++ -*-
|
// -*- mode: c++ -*-
|
||||||
|
|
||||||
// Copyright (c) 2010, Google Inc.
|
// Copyright (c) 2011, Google Inc.
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
@ -40,6 +40,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -51,9 +52,9 @@ namespace google_breakpad {
|
|||||||
|
|
||||||
class DumpSymbols {
|
class DumpSymbols {
|
||||||
public:
|
public:
|
||||||
DumpSymbols()
|
DumpSymbols()
|
||||||
: input_pathname_(),
|
: input_pathname_(),
|
||||||
object_filename_(),
|
object_filename_(),
|
||||||
contents_(),
|
contents_(),
|
||||||
selected_object_file_(),
|
selected_object_file_(),
|
||||||
selected_object_name_() { }
|
selected_object_name_() { }
|
||||||
@ -83,9 +84,9 @@ class DumpSymbols {
|
|||||||
// object file, then the dumper will dump the object file whose
|
// object file, then the dumper will dump the object file whose
|
||||||
// architecture matches that of this dumper program.
|
// architecture matches that of this dumper program.
|
||||||
bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
|
bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
|
||||||
|
|
||||||
// If this dumper's file includes an object file for |arch_name|, then select
|
// If this dumper's file includes an object file for |arch_name|, then select
|
||||||
// that object file for dumping, and return true. Otherwise, return false,
|
// that object file for dumping, and return true. Otherwise, return false,
|
||||||
// and leave this dumper's selected architecture unchanged.
|
// and leave this dumper's selected architecture unchanged.
|
||||||
//
|
//
|
||||||
// By default, if this dumper's file contains only one object file, then
|
// By default, if this dumper's file contains only one object file, then
|
||||||
@ -93,7 +94,7 @@ class DumpSymbols {
|
|||||||
// object file, then the dumper will dump the object file whose
|
// object file, then the dumper will dump the object file whose
|
||||||
// architecture matches that of this dumper program.
|
// architecture matches that of this dumper program.
|
||||||
bool SetArchitecture(const std::string &arch_name);
|
bool SetArchitecture(const std::string &arch_name);
|
||||||
|
|
||||||
// Return a pointer to an array of 'struct fat_arch' structures,
|
// Return a pointer to an array of 'struct fat_arch' structures,
|
||||||
// describing the object files contained in this dumper's file. Set
|
// describing the object files contained in this dumper's file. Set
|
||||||
// *|count| to the number of elements in the array. The returned array is
|
// *|count| to the number of elements in the array. The returned array is
|
||||||
@ -108,10 +109,10 @@ class DumpSymbols {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the selected object file's debugging information, and write it
|
// Read the selected object file's debugging information, and write it out to
|
||||||
// out to |stream|. Return true on success; if an error occurs, report it
|
// |stream|. Write the CFI section if |cfi| is true. Return true on success;
|
||||||
// and return false.
|
// if an error occurs, report it and return false.
|
||||||
bool WriteSymbolFile(FILE *stream);
|
bool WriteSymbolFile(std::ostream &stream, bool cfi);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Used internally.
|
// Used internally.
|
||||||
@ -157,7 +158,7 @@ class DumpSymbols {
|
|||||||
// has exactly one element.
|
// has exactly one element.
|
||||||
vector<struct fat_arch> object_files_;
|
vector<struct fat_arch> object_files_;
|
||||||
|
|
||||||
// The object file in object_files_ selected to dump, or NULL if
|
// The object file in object_files_ selected to dump, or NULL if
|
||||||
// SetArchitecture hasn't been called yet.
|
// SetArchitecture hasn't been called yet.
|
||||||
const struct fat_arch *selected_object_file_;
|
const struct fat_arch *selected_object_file_;
|
||||||
|
|
||||||
|
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