3 Commits

Author SHA1 Message Date
nealsid
3e7863fc84 Integrated revisions 378-381 into branch to prepare for re-merge.
git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-rewrite@382 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-08-11 01:48:59 +00:00
nealsid
de8df2f34a Merging in changes from Chrome's Breakpad Linux fork
git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-rewrite@377 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-08-06 22:05:53 +00:00
nealsid
60e5e9547a Creating a private branch for linux rewrite from Chrome.
git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-rewrite@376 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-08-06 21:47:58 +00:00
562 changed files with 45624 additions and 141504 deletions

44
DEPS
View File

@@ -1,44 +0,0 @@
# Copyright (c) 2010, 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.
# We only use this file to ease the steps of generating projects after
# syncing, if we use gclient. All dependencies are svn:externals instead.
# If you're not using gclient, you need to run the gyp python script to
# generate the projects.
# This can be done by the following command (assuming current directory):
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp
hooks = [
{
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
"pattern": ".",
"action": ["python",
"src/src/tools/gyp/gyp",
"src/src/client/windows/breakpad_client.gyp"],
},
]

View File

@@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
# Copyright (c) 2011, Google Inc.
# Copyright (c) 2006, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -33,28 +33,6 @@
# This allows #includes to be relative to src/
AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CXXFLAGS =
if ANDROID_HOST
# This allows using fixed NDK headers when building for Android.
AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/include
# This is only necessary for building the unit tests until GTest is upgraded
# to a future version.
AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/testing/include
endif
if GCC
# These are good warnings to be treated as errors
AM_CXXFLAGS += \
-Werror=non-virtual-dtor \
-Werror=vla \
-Werror=unused-variable \
-Werror=missing-braces \
-Werror=overloaded-virtual
endif
# Specify include paths for ac macros
ACLOCAL_AMFLAGS = -I m4
## Documentation
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
@@ -69,41 +47,9 @@ dist_doc_DATA = \
## Libraries
noinst_LIBRARIES =
lib_LIBRARIES =
bin_PROGRAMS =
check_PROGRAMS =
lib_LTLIBRARIES = src/libbreakpad.la
if !DISABLE_PROCESSOR
lib_LIBRARIES += src/libbreakpad.a
noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a
endif
if LINUX_HOST
lib_LIBRARIES += src/client/linux/libbreakpad_client.a
src_client_linux_libbreakpad_client_a_SOURCES = \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/log/log.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/minidump_file_writer.cc \
src/common/convert_UTF.c \
src/common/md5.cc \
src/common/string_conversion.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc
endif LINUX_HOST
if !DISABLE_PROCESSOR
src_libbreakpad_a_SOURCES = \
src_libbreakpad_la_SOURCES = \
src/google_breakpad/common/breakpad_types.h \
src/google_breakpad/common/minidump_format.h \
src/google_breakpad/common/minidump_size.h \
@@ -111,13 +57,10 @@ src_libbreakpad_a_SOURCES = \
src/google_breakpad/processor/call_stack.h \
src/google_breakpad/processor/code_module.h \
src/google_breakpad/processor/code_modules.h \
src/google_breakpad/processor/exploitability.h \
src/google_breakpad/processor/fast_source_line_resolver.h \
src/google_breakpad/processor/memory_region.h \
src/google_breakpad/processor/minidump.h \
src/google_breakpad/processor/minidump_processor.h \
src/google_breakpad/processor/process_state.h \
src/google_breakpad/processor/source_line_resolver_base.h \
src/google_breakpad/processor/source_line_resolver_interface.h \
src/google_breakpad/processor/stack_frame.h \
src/google_breakpad/processor/stack_frame_cpu.h \
@@ -129,34 +72,15 @@ src_libbreakpad_a_SOURCES = \
src/processor/basic_code_module.h \
src/processor/basic_code_modules.cc \
src/processor/basic_code_modules.h \
src/processor/basic_source_line_resolver_types.h \
src/processor/basic_source_line_resolver.cc \
src/processor/binarystream.h \
src/processor/binarystream.cc \
src/processor/call_stack.cc \
src/processor/cfi_frame_info.cc \
src/processor/cfi_frame_info.h \
src/processor/contained_range_map-inl.h \
src/processor/contained_range_map.h \
src/processor/disassembler_x86.h \
src/processor/disassembler_x86.cc \
src/processor/exploitability.cc \
src/processor/exploitability_win.h \
src/processor/exploitability_win.cc \
src/processor/fast_source_line_resolver_types.h \
src/processor/fast_source_line_resolver.cc \
src/processor/linked_ptr.h \
src/processor/logging.h \
src/processor/logging.cc \
src/processor/map_serializers-inl.h \
src/processor/map_serializers.h \
src/processor/minidump.cc \
src/processor/minidump_processor.cc \
src/processor/module_comparer.cc \
src/processor/module_comparer.h \
src/processor/module_factory.h \
src/processor/module_serializer.cc \
src/processor/module_serializer.h \
src/processor/pathname_stripper.cc \
src/processor/pathname_stripper.h \
src/processor/postfix_evaluator-inl.h \
@@ -165,685 +89,123 @@ src_libbreakpad_a_SOURCES = \
src/processor/range_map-inl.h \
src/processor/range_map.h \
src/processor/scoped_ptr.h \
src/processor/simple_serializer-inl.h \
src/processor/simple_serializer.h \
src/processor/simple_symbol_supplier.cc \
src/processor/simple_symbol_supplier.h \
src/processor/windows_frame_info.h \
src/processor/source_line_resolver_base_types.h \
src/processor/source_line_resolver_base.cc \
src/processor/stack_frame_info.h \
src/processor/stackwalker.cc \
src/processor/stackwalker_amd64.cc \
src/processor/stackwalker_amd64.h \
src/processor/stackwalker_arm.cc \
src/processor/stackwalker_arm.h \
src/processor/stackwalker_ppc.cc \
src/processor/stackwalker_ppc.h \
src/processor/stackwalker_sparc.cc \
src/processor/stackwalker_sparc.h \
src/processor/stackwalker_x86.cc \
src/processor/stackwalker_x86.h \
src/processor/static_address_map-inl.h \
src/processor/static_address_map.h \
src/processor/static_contained_range_map-inl.h \
src/processor/static_contained_range_map.h \
src/processor/static_map_iterator-inl.h \
src/processor/static_map_iterator.h \
src/processor/static_map-inl.h \
src/processor/static_map.h \
src/processor/static_range_map-inl.h \
src/processor/static_range_map.h \
src/processor/tokenize.cc \
src/processor/tokenize.h
src/processor/stackwalker_x86.h
src_libbreakpad_a_LIBADD = src/third_party/libdisasm/libdisasm.a
src_third_party_libdisasm_libdisasm_a_SOURCES = \
src/third_party/libdisasm/ia32_implicit.c \
src/third_party/libdisasm/ia32_implicit.h \
src/third_party/libdisasm/ia32_insn.c \
src/third_party/libdisasm/ia32_insn.h \
src/third_party/libdisasm/ia32_invariant.c \
src/third_party/libdisasm/ia32_invariant.h \
src/third_party/libdisasm/ia32_modrm.c \
src/third_party/libdisasm/ia32_modrm.h \
src/third_party/libdisasm/ia32_opcode_tables.c \
src/third_party/libdisasm/ia32_opcode_tables.h \
src/third_party/libdisasm/ia32_operand.c \
src/third_party/libdisasm/ia32_operand.h \
src/third_party/libdisasm/ia32_reg.c \
src/third_party/libdisasm/ia32_reg.h \
src/third_party/libdisasm/ia32_settings.c \
src/third_party/libdisasm/ia32_settings.h \
src/third_party/libdisasm/libdis.h \
src/third_party/libdisasm/qword.h \
src/third_party/libdisasm/x86_disasm.c \
src/third_party/libdisasm/x86_format.c \
src/third_party/libdisasm/x86_imm.c \
src/third_party/libdisasm/x86_imm.h \
src/third_party/libdisasm/x86_insn.c \
src/third_party/libdisasm/x86_misc.c \
src/third_party/libdisasm/x86_operand_list.c \
src/third_party/libdisasm/x86_operand_list.h
## Programs
bin_PROGRAMS += \
bin_PROGRAMS = \
src/processor/minidump_dump \
src/processor/minidump_stackwalk
endif !DISABLE_PROCESSOR
if LINUX_HOST
bin_PROGRAMS += \
src/client/linux/linux_dumper_unittest_helper
if !DISABLE_TOOLS
bin_PROGRAMS += \
src/tools/linux/core2md/core2md \
src/tools/linux/dump_syms/dump_syms \
src/tools/linux/md2core/minidump-2-core \
src/tools/linux/symupload/minidump_upload \
src/tools/linux/symupload/sym_upload
endif
endif LINUX_HOST
## Tests
if !DISABLE_PROCESSOR
check_PROGRAMS += \
src/common/test_assembler_unittest \
check_PROGRAMS = \
src/processor/address_map_unittest \
src/processor/binarystream_unittest \
src/processor/basic_source_line_resolver_unittest \
src/processor/cfi_frame_info_unittest \
src/processor/contained_range_map_unittest \
src/processor/disassembler_x86_unittest \
src/processor/exploitability_unittest \
src/processor/fast_source_line_resolver_unittest \
src/processor/map_serializers_unittest \
src/processor/minidump_processor_unittest \
src/processor/minidump_unittest \
src/processor/static_address_map_unittest \
src/processor/static_contained_range_map_unittest \
src/processor/static_map_unittest \
src/processor/static_range_map_unittest \
src/processor/pathname_stripper_unittest \
src/processor/postfix_evaluator_unittest \
src/processor/range_map_unittest \
src/processor/stackwalker_amd64_unittest \
src/processor/stackwalker_arm_unittest \
src/processor/stackwalker_x86_unittest \
src/processor/synth_minidump_unittest
endif
src/processor/range_map_unittest
if LINUX_HOST
check_PROGRAMS += \
src/client/linux/linux_client_unittest
if !DISABLE_TOOLS
check_PROGRAMS += \
src/common/dumper_unittest \
src/tools/linux/md2core/minidump_2_core_unittest
endif
endif LINUX_HOST
if !DISABLE_PROCESSOR
if SELFTEST
check_PROGRAMS += \
src/processor/stackwalker_selftest
endif SELFTEST
endif !DISABLE_PROCESSOR
if !DISABLE_PROCESSOR
check_SCRIPTS = \
src/processor/minidump_dump_test \
src/processor/minidump_stackwalk_test \
src/processor/minidump_stackwalk_machine_readable_test
endif
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
if LINUX_HOST
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS)
src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
src_client_linux_linux_client_unittest_SOURCES = \
src/client/linux/handler/exception_handler_unittest.cc \
src/client/linux/minidump_writer/directory_reader_unittest.cc \
src/client/linux/minidump_writer/line_reader_unittest.cc \
src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \
src/common/linux/linux_libc_support_unittest.cc \
src/common/linux/tests/crash_generator.cc \
src/common/memory_unittest.cc \
src/common/tests/file_utils.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc \
src/processor/basic_code_modules.cc \
src/processor/logging.cc \
src/processor/minidump.cc \
src/processor/pathname_stripper.cc
src_client_linux_linux_client_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_client_linux_linux_client_unittest_LDADD = \
src/client/linux/handler/exception_handler.o \
src/client/linux/handler/minidump_descriptor.o \
src/client/linux/log/log.o \
src/client/linux/crash_generation/crash_generation_client.o \
src/client/linux/minidump_writer/linux_dumper.o \
src/client/linux/minidump_writer/linux_core_dumper.o \
src/client/linux/minidump_writer/linux_ptrace_dumper.o \
src/client/linux/minidump_writer/minidump_writer.o \
src/client/minidump_file_writer.o \
src/common/convert_UTF.o \
src/common/md5.o \
src/common/linux/elf_core_dump.o \
src/common/linux/elfutils.o \
src/common/linux/file_id.o \
src/common/linux/guid_creator.o \
src/common/linux/linux_libc_support.o \
src/common/linux/memory_mapped_file.o \
src/common/linux/safe_readlink.o \
src/common/string_conversion.o \
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
if ANDROID_HOST
src_client_linux_linux_client_unittest_LDFLAGS = \
-llog
endif
src_client_linux_linux_client_unittest_DEPENDENCIES = \
src/client/linux/linux_dumper_unittest_helper \
src/client/linux/libbreakpad_client.a \
src/libbreakpad.a
if !DISABLE_TOOLS
src_tools_linux_core2md_core2md_SOURCES = \
src/tools/linux/core2md/core2md.cc \
src/client/linux/minidump_writer/linux_core_dumper.cc \
src/common/linux/elf_core_dump.cc
src_tools_linux_core2md_core2md_LDADD = \
src/client/linux/libbreakpad_client.a
src_tools_linux_dump_syms_dump_syms_SOURCES = \
src/common/dwarf_cfi_to_module.cc \
src/common/dwarf_cu_to_module.cc \
src/common/dwarf_line_to_module.cc \
src/common/language.cc \
src/common/module.cc \
src/common/stabs_reader.cc \
src/common/stabs_to_module.cc \
src/common/dwarf/bytereader.cc \
src/common/dwarf/dwarf2diehandler.cc \
src/common/dwarf/dwarf2reader.cc \
src/common/linux/dump_symbols.cc \
src/common/linux/elf_symbols_to_module.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc \
src/tools/linux/dump_syms/dump_syms.cc
src_tools_linux_md2core_minidump_2_core_SOURCES = \
src/common/linux/memory_mapped_file.cc \
src/tools/linux/md2core/minidump-2-core.cc
src_tools_linux_symupload_minidump_upload_SOURCES = \
src/common/linux/http_upload.cc \
src/tools/linux/symupload/minidump_upload.cc
src_tools_linux_symupload_minidump_upload_LDADD = -ldl
src_tools_linux_symupload_sym_upload_SOURCES = \
src/common/linux/http_upload.cc \
src/tools/linux/symupload/sym_upload.cc
src_tools_linux_symupload_sym_upload_LDADD = -ldl
src_common_dumper_unittest_SOURCES = \
src/common/byte_cursor_unittest.cc \
src/common/dwarf_cfi_to_module.cc \
src/common/dwarf_cfi_to_module_unittest.cc \
src/common/dwarf_cu_to_module.cc \
src/common/dwarf_cu_to_module_unittest.cc \
src/common/dwarf_line_to_module.cc \
src/common/dwarf_line_to_module_unittest.cc \
src/common/language.cc \
src/common/memory_range_unittest.cc \
src/common/module.cc \
src/common/module_unittest.cc \
src/common/stabs_reader.cc \
src/common/stabs_reader_unittest.cc \
src/common/stabs_to_module.cc \
src/common/stabs_to_module_unittest.cc \
src/common/test_assembler.cc \
src/common/dwarf/bytereader.cc \
src/common/dwarf/bytereader_unittest.cc \
src/common/dwarf/cfi_assembler.cc \
src/common/dwarf/dwarf2diehandler.cc \
src/common/dwarf/dwarf2diehandler_unittest.cc \
src/common/dwarf/dwarf2reader.cc \
src/common/dwarf/dwarf2reader_cfi_unittest.cc \
src/common/dwarf/dwarf2reader_die_unittest.cc \
src/common/linux/dump_symbols.cc \
src/common/linux/dump_symbols_unittest.cc \
src/common/linux/elf_core_dump.cc \
src/common/linux/elf_core_dump_unittest.cc \
src/common/linux/elf_symbols_to_module.cc \
src/common/linux/elf_symbols_to_module_unittest.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/file_id_unittest.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/memory_mapped_file_unittest.cc \
src/common/linux/safe_readlink.cc \
src/common/linux/safe_readlink_unittest.cc \
src/common/linux/synth_elf.cc \
src/common/linux/synth_elf_unittest.cc \
src/common/linux/tests/crash_generator.cc \
src/common/tests/file_utils.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_common_dumper_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing \
$(PTHREAD_CFLAGS)
src_common_dumper_unittest_LDADD = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
endif
src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc \
src/tools/linux/md2core/minidump_memory_range_unittest.cc
src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
endif LINUX_HOST
if !DISABLE_PROCESSOR
src_processor_address_map_unittest_SOURCES = \
src/processor/address_map_unittest.cc
src_processor_address_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_binarystream_unittest_SOURCES = \
src/processor/binarystream_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_binarystream_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_binarystream_unittest_LDADD = \
src/processor/binarystream.o
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_basic_source_line_resolver_unittest_SOURCES = \
src/processor/basic_source_line_resolver_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_basic_source_line_resolver_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src/processor/basic_source_line_resolver_unittest.cc
src_processor_basic_source_line_resolver_unittest_LDADD = \
src/processor/basic_source_line_resolver.o \
src/processor/cfi_frame_info.o \
src/processor/pathname_stripper.o \
src/processor/logging.o \
src/processor/source_line_resolver_base.o \
src/processor/tokenize.o
src_processor_cfi_frame_info_unittest_SOURCES = \
src/processor/cfi_frame_info_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_cfi_frame_info_unittest_LDADD = \
src/processor/cfi_frame_info.o \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_cfi_frame_info_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src/processor/basic_source_line_resolver.lo \
src/processor/pathname_stripper.lo \
src/processor/logging.lo
src_processor_contained_range_map_unittest_SOURCES = \
src/processor/contained_range_map_unittest.cc
src_processor_contained_range_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_exploitability_unittest_SOURCES = \
src/processor/exploitability_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_exploitability_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_exploitability_unittest_LDADD = \
src/processor/minidump_processor.o \
src/processor/process_state.o \
src/processor/disassembler_x86.o \
src/processor/exploitability.o \
src/processor/exploitability_win.o \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/call_stack.o \
src/processor/cfi_frame_info.o \
src/processor/logging.o \
src/processor/minidump.o \
src/processor/pathname_stripper.o \
src/processor/source_line_resolver_base.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_sparc.o \
src/processor/stackwalker_x86.o \
src/processor/tokenize.o \
src/third_party/libdisasm/libdisasm.a
src_processor_disassembler_x86_unittest_SOURCES = \
src/processor/disassembler_x86_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_disassembler_x86_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_disassembler_x86_unittest_LDADD = \
src/processor/disassembler_x86.o \
src/third_party/libdisasm/libdisasm.a
src_processor_fast_source_line_resolver_unittest_SOURCES = \
src/processor/fast_source_line_resolver_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_fast_source_line_resolver_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_fast_source_line_resolver_unittest_LDADD = \
src/processor/fast_source_line_resolver.o \
src/processor/basic_source_line_resolver.o \
src/processor/cfi_frame_info.o \
src/processor/module_comparer.o \
src/processor/module_serializer.o \
src/processor/pathname_stripper.o \
src/processor/logging.o \
src/processor/source_line_resolver_base.o \
src/processor/tokenize.o
src_processor_map_serializers_unittest_SOURCES = \
src/processor/map_serializers_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_map_serializers_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_map_serializers_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_minidump_processor_unittest_SOURCES = \
src/processor/minidump_processor_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_minidump_processor_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_minidump_processor_unittest_LDADD = \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/call_stack.o \
src/processor/cfi_frame_info.o \
src/processor/disassembler_x86.o \
src/processor/exploitability.o \
src/processor/exploitability_win.o \
src/processor/logging.o \
src/processor/minidump_processor.o \
src/processor/minidump.o \
src/processor/pathname_stripper.o \
src/processor/process_state.o \
src/processor/source_line_resolver_base.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_sparc.o \
src/processor/stackwalker_x86.o \
src/processor/tokenize.o \
src/third_party/libdisasm/libdisasm.a
src_processor_minidump_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/minidump_unittest.cc \
src/processor/synth_minidump.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_minidump_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_minidump_unittest_LDADD = \
src/processor/basic_code_modules.o \
src/processor/logging.o \
src/processor/minidump.o \
src/processor/pathname_stripper.o
src_processor_static_address_map_unittest_SOURCES = \
src/processor/static_address_map_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_static_address_map_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_static_address_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_static_contained_range_map_unittest_SOURCES = \
src/processor/static_contained_range_map_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_static_contained_range_map_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_static_contained_range_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_static_map_unittest_SOURCES = \
src/processor/static_map_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_static_map_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_static_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src_processor_static_range_map_unittest_SOURCES = \
src/processor/static_range_map_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_static_range_map_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_static_range_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/call_stack.lo \
src/processor/logging.lo \
src/processor/minidump_processor.lo \
src/processor/minidump.lo \
src/processor/pathname_stripper.lo \
src/processor/process_state.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_amd64.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
src_processor_pathname_stripper_unittest_SOURCES = \
src/processor/pathname_stripper_unittest.cc
src_processor_pathname_stripper_unittest_LDADD = \
src/processor/pathname_stripper.o
src/processor/pathname_stripper.lo
src_processor_postfix_evaluator_unittest_SOURCES = \
src/processor/postfix_evaluator_unittest.cc
src_processor_postfix_evaluator_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_range_map_unittest_SOURCES = \
src/processor/range_map_unittest.cc
src_processor_range_map_unittest_LDADD = \
src/processor/logging.o \
src/processor/pathname_stripper.o
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_stackwalker_selftest_SOURCES = \
src/processor/stackwalker_selftest.cc
src_processor_stackwalker_selftest_LDADD = \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/call_stack.o \
src/processor/disassembler_x86.o \
src/processor/exploitability.o \
src/processor/exploitability_win.o \
src/processor/logging.o \
src/processor/minidump.o \
src/processor/pathname_stripper.o \
src/processor/source_line_resolver_base.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_sparc.o \
src/processor/stackwalker_x86.o \
src/processor/tokenize.o
src_processor_stackwalker_amd64_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_amd64_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_amd64_unittest_LDADD = \
src/libbreakpad.a
src_processor_stackwalker_amd64_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_arm_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_arm_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_arm_unittest_LDADD = \
src/libbreakpad.a
src_processor_stackwalker_arm_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_x86_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_x86_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_x86_unittest_LDADD = \
src/libbreakpad.a
src_processor_stackwalker_x86_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_synth_minidump_unittest_SOURCES = \
src/common/test_assembler.cc \
src/common/test_assembler.h \
src/processor/synth_minidump_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc \
src/processor/synth_minidump.cc \
src/processor/synth_minidump.h
src_processor_synth_minidump_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_common_test_assembler_unittest_SOURCES = \
src/common/test_assembler.cc \
src/common/test_assembler.h \
src/common/test_assembler_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_common_test_assembler_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/call_stack.lo \
src/processor/logging.lo \
src/processor/minidump.lo \
src/processor/pathname_stripper.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_amd64.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
## Non-installables
noinst_PROGRAMS =
@@ -852,39 +214,29 @@ noinst_SCRIPTS = $(check_SCRIPTS)
src_processor_minidump_dump_SOURCES = \
src/processor/minidump_dump.cc
src_processor_minidump_dump_LDADD = \
src/processor/basic_code_modules.o \
src/processor/logging.o \
src/processor/minidump.o \
src/processor/pathname_stripper.o
src/processor/basic_code_modules.lo \
src/processor/logging.lo \
src/processor/minidump.lo \
src/processor/pathname_stripper.lo
src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/binarystream.o \
src/processor/call_stack.o \
src/processor/cfi_frame_info.o \
src/processor/disassembler_x86.o \
src/processor/exploitability.o \
src/processor/exploitability_win.o \
src/processor/logging.o \
src/processor/minidump.o \
src/processor/minidump_processor.o \
src/processor/pathname_stripper.o \
src/processor/process_state.o \
src/processor/simple_symbol_supplier.o \
src/processor/source_line_resolver_base.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_sparc.o \
src/processor/stackwalker_x86.o \
src/processor/tokenize.o \
src/third_party/libdisasm/libdisasm.a
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/call_stack.lo \
src/processor/logging.lo \
src/processor/minidump.lo \
src/processor/minidump_processor.lo \
src/processor/pathname_stripper.lo \
src/processor/process_state.lo \
src/processor/simple_symbol_supplier.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_amd64.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
endif !DISABLE_PROCESSOR
## Additional files to be included in a source distribution
##
@@ -897,8 +249,6 @@ EXTRA_DIST = \
src/client/linux/handler/Makefile \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/exception_handler.h \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/handler/minidump_descriptor.h \
src/client/linux/handler/exception_handler_test.cc \
src/client/linux/handler/linux_thread.cc \
src/client/linux/handler/linux_thread.h \
@@ -941,10 +291,6 @@ EXTRA_DIST = \
src/common/convert_UTF.h \
src/common/linux/dump_symbols.cc \
src/common/linux/dump_symbols.h \
src/common/linux/elf_symbols_to_module.cc \
src/common/linux/elf_symbols_to_module.h \
src/common/linux/elfutils.cc \
src/common/linux/elfutils.h \
src/common/linux/file_id.cc \
src/common/linux/file_id.h \
src/common/linux/guid_creator.cc \
@@ -965,7 +311,7 @@ EXTRA_DIST = \
src/common/mac/macho_walker.h \
src/common/mac/string_utilities.cc \
src/common/mac/string_utilities.h \
src/common/md5.cc \
src/common/md5.c \
src/common/md5.h \
src/common/solaris/dump_symbols.cc \
src/common/solaris/dump_symbols.h \
@@ -1027,3 +373,8 @@ EXTRA_DIST = \
src/tools/windows/dump_syms/testdata/dump_syms_regtest.sym \
src/tools/windows/symupload/symupload.cc \
src/tools/windows/symupload/symupload.vcproj
## Additional rules
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck

File diff suppressed because it is too large Load Diff

41
README
View File

@@ -1,43 +1,2 @@
Breakpad is a set of client and server components which implement a
crash-reporting system.
-----
Getting started in 32-bit mode (from trunk)
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
Build: make
Test: make check
Install: make install
If you need to reconfigure your build be sure to run "make distclean" first.
-----
To request change review:
0. Get access to a read-write copy of source.
Owners at http://code.google.com/p/google-breakpad/ are able to grant
this access.
1. Check out a read-write copy of source using instructions at
http://code.google.com/p/google-breakpad/source/checkout
2. Make changes. Build and test your changes.
For core code like processor use methods above.
For linux/mac/windows, there are test targets in each project file.
3. Download http://codereview.appspot.com/static/upload.py
4. Run upload.py from the 'src' directory:
upload.py --server=breakpad.appspot.com
You will be prompted for credential and a description.
5. At http://breakpad.appspot.com you'll find your issue listed; click on it,
and select Publish+Mail, and enter in the code reviewer and CC
google-breakpad-dev@googlegroups.com
6. When applying code review feedback, specify the '-i' option when running
upload.py again and pass the issue number so it updates the existing issue,
rather than creating a new one.
Be sure to rerun upload.py from the same directory as you did for previous
uploads to allow for proper diff calculations.

View File

@@ -1,134 +0,0 @@
Google Breakpad for Android
===========================
This document explains how to use the Google Breakpad client library
on Android, and later generate valid stack traces from the minidumps
it generates.
Note that this release only supports ARM-based Android systems.
We're working on adding support for x86 and MIPS, but that might
require an udpated NDK release.
I. Building the client library:
===============================
The Android client is built as a static library that you can
link into your own Android native code. There are two ways to
build it:
I.1. Building with ndk-build:
-----------------------------
If you're using the ndk-build build system, you can follow
these simple steps:
1/ Include android/google_breakpad/Android.mk from your own
project's Android.mk
This can be done either directly, or using ndk-build's
import-module feature.
2/ Link the library to one of your modules by using:
LOCAL_STATIC_LIBRARIES += breakpad_client
NOTE: The client library requires a C++ STL implementation,
which you can select with APP_STL in your Application.mk
It has been tested succesfully with both STLport and GNU libstdc++
II.1. Building with a standalone Android toolchain:
---------------------------------------------------
All you need to do is configure your build with the right 'host'
value, and disable the processor and tools, as in:
$GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \
--disable-processor \
--disable-tools
make -j4
The library will be under src/client/linux/libbreakpad_client.a
II. Using the client library in Android:
========================================
The usage instructions are very similar to the Linux ones that are
found at http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide
1/ You need to include "client/linux/handler/exception_handler.h" from a C++
source file.
2/ If you're not using ndk-build, you also need to:
- add $GOOGLE_BREAKPAD_PATH to your compiler include path
- add -llog to your linker flags
Note that ndk-build does that for your automatically.
3/ Keep in mind that there is no /tmp directory on Android.
If you use the library from a regular Android applications, specify a
path under your app-specific storage directory. An alternative is to
store them on the SDCard, but this requires a specific permission.
For a concrete example, see the sample test application under
android/sample_app. See its README for more information.
III. Getting a stack trace on the host:
=======================================
This process is similar to other platforms, but here's a quick example:
1/ Retrieve the minidumps on your development machine.
2/ Dump the symbols for your native libraries with the 'dump_syms' tool.
This first requires building the host version of Google Breakpad, then
calling:
dump_syms $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym
3/ Create the symbol directory hierarchy.
The first line of the generated libfoo.so.sym will have a "MODULE"
entry that carries a hexadecimal version number, e.g.:
MODULE Linux arm D51B4A5504974FA6ECC1869CAEE3603B0 test_google_breakpad
Note: The second field could be either 'Linux' or 'Android'.
Extract the version number, and a 'symbol' directory, for example:
$PROJECT_PATH/symbols/libfoo.so/$VERSION/
Copy/Move your libfoo.sym file there.
4/ Invoke minidump_stackwalk to create the stack trace:
minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols
Note that various helper scripts can be found on the web to automate these
steps.
IV. Verifying the Android build library:
========================================
If you modify Google Breakpad and want to check that it still works correctly
on Android, please run the android/run-test-program.sh script which will do all
necessary verifications for you. This includes:
- Rebuilding the full host package
- Rebuilding the client library with configure/make
- Rebuilding the client library with ndk-build
- Building, installing and running a test crasher program on a device
- Extracting the corresponding minidump, dumping the test program symbols
and generating a stack trace.
- Checking the stack trace for valid source locations.
For more details, please run:
android/run-test-program.sh --help-all

6984
aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,103 +0,0 @@
# 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.
# ndk-build module definition for the Google Breakpad client library
#
# To use this file, do the following:
#
# 1/ Include this file from your own Android.mk, either directly
# or with through the NDK's import-module function.
#
# 2/ Use the client static library in your project with:
#
# LOCAL_STATIC_LIBRARIES += breakpad_client
#
# 3/ In your source code, include "src/client/linux/exception_handler.h"
# and use the Linux instructions to use it.
#
# This module works with either the STLport or GNU libstdc++, but you need
# to select one in your Application.mk
#
# Sanity check. We can only build for ARM for now.
ifneq (,$(filter-out armeabi armeabi-v7a x86,$(TARGET_ARCH_ABI)))
$(error Sorry, Google Breakpad only works on Android ARM and x86 for now!)
endif
# The top Google Breakpad directory.
# We assume this Android.mk to be under 'android/google_breakpad'
LOCAL_PATH := $(call my-dir)/../..
# Defube the client library module, as a simple static library that
# exports the right include path / linker flags to its users.
include $(CLEAR_VARS)
LOCAL_MODULE := breakpad_client
LOCAL_CPP_EXTENSION := .cc
# Breakpad uses inline ARM assembly that requires the library
# to be built in ARM mode. Otherwise, the build will fail with
# cryptic assembler messages like:
# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc
# /tmp/cc8aMSoD.s: Assembler messages:
# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range
# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range
LOCAL_ARM_MODE := arm
# List of client source files, directly taken from Makefile.am
LOCAL_SRC_FILES := \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/log/log.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/minidump_file_writer.cc \
src/common/convert_UTF.c \
src/common/md5.cc src/common/string_conversion.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
$(LOCAL_PATH)/src
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)
# Done.

View File

@@ -1,653 +0,0 @@
#!/bin/sh
# 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.
# Sanitize the environment
export LANG=C
export LC_ALL=C
if [ "$BASH_VERSION" ]; then
set -o posix
fi
PROGDIR=$(dirname "$0")
PROGDIR=$(cd "$PROGDIR" && pwd)
PROGNAME=$(basename "$0")
# Utility functions
TMPDIR=
# Used to exit the program after removing the temporary directory.
clean_exit () {
if [ "$TMPDIR" ]; then
if [ -z "$NO_CLEANUP" ]; then
log "Cleaning up: $TMPDIR"
rm -rf "$TMPDIR"
else
dump "Temporary directory contents preserved: $TMPDIR"
fi
fi
exit "$@"
}
# Dump a panic message then exit.
panic () {
echo "ERROR: $@"
clean_exit 1;
}
# If the previous command failed, dump a panic message then exit.
fail_panic () {
if [ $? != 0 ]; then
panic "$@"
fi;
}
# Extract number of cores to speed up the builds
get_core_count () {
case $(uname -s) in
Linux)
grep -c -e '^processor' /proc/cpuinfo
;;
Darwin)
sysctl -n hw.ncpu
;;
CYGWIN*|*_NT-*)
echo $NUMBER_OF_PROCESSORS
;;
*)
echo 1
;;
esac
}
DEFAULT_ABI="armeabi"
VALID_ABIS="armeabi armeabi-v7a x86 mips"
ABI=
ADB=
ENABLE_M32=
HELP=
HELP_ALL=
NDK_DIR=
NO_CLEANUP=
NO_DEVICE=
NUM_JOBS=$(get_core_count)
TMPDIR=
VERBOSE=0
for opt do
# The following extracts the value if the option is like --name=<value>.
optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$')
case $opt in
--abi=*) ABI=$optarg;;
--adb=*) ADB=$optarg;;
--enable-m32) ENABLE_M32=true;;
--help|-h|-?) HELP=TRUE;;
--help-all) HELP_ALL=true;;
--jobs=*) NUM_JOBS=$optarg;;
--ndk-dir=*) NDK_DIR=$optarg;;
--tmp-dir=*) TMPDIR=$optarg;;
--no-cleanup) NO_CLEANUP=true;;
--no-device) NO_DEVICE=true;;
--quiet) VERBOSE=$(( $VERBOSE - 1 ));;
--verbose) VERBOSE=$(( $VERBOSE + 1 ));;
-*) panic "Invalid option '$opt', see --help for details.";;
*) panic "This script doesn't take any parameters. See --help for details."
;;
esac
done
if [ "$HELP" -o "$HELP_ALL" ]; then
echo "\
Usage: $PROGNAME [options]
This script is used to check that your Google Breakpad source tree can
be properly built for Android, and that the client library and host tools
work properly together.
"
if [ "$HELP_ALL" ]; then
echo "\
In more details, this script will:
- Rebuild the host version of Google Breakpad in a temporary
directory (with the Auto-tools based build system).
- Rebuild the Android client library with the Google Breakpad build
system (using autotools/configure). This requires that you define
ANDROID_NDK_ROOT in your environment to point to a valid Android NDK
installation directory, or use the --ndk-dir=<path> option.
- Rebuild the Android client library and a test crashing program with the
Android NDK build system (ndk-build).
- Require an Android device connected to your machine, and the 'adb'
tool in your path. They are used to:
- Install and run a test crashing program.
- Extract the corresponding minidump from the device.
- Dump the symbols from the test program on the host with 'dump_syms'
- Generate a stack trace with 'minidump_stackwalk'
- Check the stack trace content for valid source file locations.
You can however skip this requirement and only test the builds by using
the --no-device flag.
By default, all generated files will be created in a temporary directory
that is removed when the script completion. If you want to inspect the
files, use the --no-cleanup option.
Finally, use --verbose to increase the verbosity level, this will help
you see which exact commands are being issues and their result. Use the
flag twice for even more output. Use --quiet to decrease verbosity
instead and run the script silently.
If you have a device connected, the script will probe it to determine
its primary CPU ABI, and build the test program for it. You can however
use the --abi=<name> option to override this (this can be useful to check
the secondary ABI, e.g. using --abi=armeabi to check that such a program
works correctly on an ARMv7-A device).
If you don't have a device connected, the test program will be built (but
not run) with the default '$DEFAULT_ABI' ABI. Again, you can use
--abi=<name> to override this. Valid ABI names are:
$VALID_ABIS
"
fi # HELP_ALL
echo "\
Valid options:
--help|-h|-? Display this message.
--help-all Display extended help.
--enable-m32 Build 32-bit version of host tools.
--abi=<name> Specify target CPU ABI [auto-detected].
--jobs=<count> Run <count> build tasks in parallel [$NUM_JOBS].
--ndk-dir=<path> Specify NDK installation directory.
--tmp-dir=<path> Specify temporary directory (will be wiped-out).
--adb=<path> Specify adb program path.
--no-cleanup Don't remove temporary directory after completion.
--no-device Do not try to detect devices, nor run crash test.
--verbose Increase verbosity.
--quiet Decrease verbosity."
clean_exit 0
fi
# Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called
dump () {
if [ "$VERBOSE" -ge 0 ]; then
echo "$@"
fi
}
# If --verbose was used, dump a message to stdout.
log () {
if [ "$VERBOSE" -ge 1 ]; then
echo "$@"
fi
}
# Run a command. Output depends on $VERBOSE:
# $VERBOSE <= 0: Run command, store output into the run log
# $VERBOSE >= 1: Dump command, run it, output goest to stdout
# Note: Ideally, the command's output would go to the run log for $VERBOSE >= 1
# but the 'tee' tool doesn't preserve the status code of its input pipe
# in case of error.
run () {
local LOGILE
if [ "$RUN_LOG" ]; then
LOGFILE=$RUN_LOG
else
LOGFILE=/dev/null
fi
if [ "$VERBOSE" -ge 1 ]; then
echo "COMMAND: $@"
"$@"
else
"$@" >>$LOGFILE 2>&1
fi
}
# Same as run(), but only dump command output for $VERBOSE >= 2
run2 () {
local LOGILE
if [ "$RUN_LOG" ]; then
LOGFILE=$RUN_LOG
else
LOGFILE=/dev/null
fi
if [ "$VERBOSE" -ge 1 ]; then
echo "COMMAND: $@"
fi
if [ "$VERBOSE" -ge 2 ]; then
"$@"
else
"$@" >>$LOGFILE 2>&1
fi
}
TESTAPP_DIR=$PROGDIR/sample_app
# Select NDK install directory.
if [ -z "$NDK_DIR" ]; then
if [ -z "$ANDROID_NDK_ROOT" ]; then
panic "Please define ANDROID_NDK_ROOT in your environment, or use \
--ndk-dir=<path>."
fi
NDK_DIR="$ANDROID_NDK_ROOT"
log "Found NDK directory: $NDK_DIR"
else
log "Using NDK directory: $NDK_DIR"
fi
# Small sanity check.
NDK_BUILD="$NDK_DIR/ndk-build"
if [ ! -f "$NDK_BUILD" ]; then
panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR"
fi
# If --tmp-dir=<path> is not used, create a temporary directory.
# Otherwise, start by cleaning up the user-provided path.
if [ -z "$TMPDIR" ]; then
TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX)
fail_panic "Can't create temporary directory!"
log "Using temporary directory: $TMPDIR"
else
if [ ! -d "$TMPDIR" ]; then
mkdir -p "$TMPDIR"
fail_panic "Can't create temporary directory: $TMPDIR"
else
log "Cleaning up temporary directory: $TMPDIR"
rm -rf "$TMPDIR"/*
fail_panic "Cannot cleanup temporary directory!"
fi
fi
# Ensure a clean exit when the script is:
# - Interrupted by Ctrl-C (INT)
# - Interrupted by log out (HUP)
# - Being asked to quit nicely (TERM)
# - Being asked to quit and dump core (QUIT)
trap "clean_exit 1" INT HUP TERM QUIT
# The 'adb shell' command is pretty hopeless, try to make sense of it by:
# 1/ Removing trailing \r from line endings.
# 2/ Ensuring the function returns the command's status code.
#
adb_shell () {
local RET ADB_LOG
ADB_LOG=$(mktemp "$TMPDIR/adb-XXXXXXXX")
"$ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1
sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r.
RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code.
sed -e '$d' "$ADB_LOG" # Print everything except last line.
rm -f "$ADB_LOG"
return $RET
}
check_for_adb () {
local ADB_VERSION ADB_DEVICES NUM_DEVICES FINGERPRINT
# Auto-detect ADB in current path when needed.
if [ -z "$ADB" ]; then
ADB=$(which adb 2>/dev/null)
if [ -z "$ADB" ]; then
panic "The 'adb' tool is not in your path! Use either --no-device to\
only check the builds, or --adb=<path> to specify the tool path."
fi
log "Found ADB path: $ADB"
else
log "Using ADB path: $ADB"
fi
# Check that it works.
ADB_VERSION=$("$ADB" version 2>/dev/null)
case $ADB_VERSION in
"Android Debug Bridge "*) # Pass.
log "Found ADB version: $ADB_VERSION"
;;
*) # Fail.
panic "Your ADB binary does not seem to work: $ADB"
;;
esac
# Count the number of connected devices.
ADB_DEVICES=$("$ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }')
if [ "$ADB_DEVICES" ]; then
NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l)
else
NUM_DEVICES=0
fi
case $NUM_DEVICES in
0)
panic "No Android device connected! Connect one, or use --no-device \
to only check the builds."
;;
1)
export ANDROID_SERIAL=$ADB_DEVICES
;;
*)
if [ "$ANDROID_SERIAL" ]; then
ADB_DEVICES=$ANDROID_SERIAL
NUM_DEVICES=1
else
dump "ERROR: More than one Android device connected. Please define \
ANDROID_SERIAL"
dump " in your environment, or use --no-device to only check \
the builds."
clean_exit 1
fi
;;
esac
FINGERPRINT=$(adb_shell getprop ro.build.fingerprint)
dump "Using device: $ANDROID_SERIAL ($FINGERPRINT)"
}
if [ -z "$NO_DEVICE" ]; then
check_for_adb
fi
BUILD_LOG="$TMPDIR/build.log"
RUN_LOG="$TMPDIR/run.log"
CRASH_LOG="$TMPDIR/crash.log"
TMPHOST="$TMPDIR/host-local"
cd "$TMPDIR"
# Build host version of the tools
dump "Building host tools."
CONFIGURE_FLAGS=
if [ "$ENABLE_M32" ]; then
CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32"
fi
(
run mkdir "$TMPDIR/build-host" &&
run cd "$TMPDIR/build-host" &&
run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS &&
run2 make -j$NUM_JOBS install
)
fail_panic "Can't build host-tools!"
TMPBIN=$TMPHOST/bin
# Generate a stand-alone NDK toolchain
# Extract CPU ABI and architecture from device, if any.
if [ "$ADB" ]; then
DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi)
DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2)
if [ -z "$DEVICE_ABI" ]; then
panic "Can't extract ABI from connected device!"
fi
if [ "$DEVICE_ABI2" ]; then
dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2"
else
dump "Found device ABI: $DEVICE_ABI"
DEVICE_ABI2=$DEVICE_ABI
fi
# If --abi=<name> is used, check that the device supports it.
if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then
dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!"
panic "Please use --no-device to skip device tests."
fi
if [ -z "$ABI" ]; then
ABI=$DEVICE_ABI
dump "Using CPU ABI: $ABI (device)"
else
dump "Using CPU ABI: $ABI (command-line)"
fi
else
if [ -z "$ABI" ]; then
# No device connected, choose default ABI
ABI=$DEFAULT_ABI
dump "Using CPU ABI: $ABI (default)"
else
dump "Using CPU ABI: $ABI (command-line)"
fi
fi
# Check the ABI value
VALID=
for VALID_ABI in $VALID_ABIS; do
if [ "$ABI" = "$VALID_ABI" ]; then
VALID=true
break
fi
done
if [ -z "$VALID" ]; then
panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS"
fi
# Extract architecture name from ABI
case $ABI in
armeabi*) ARCH=arm;;
*) ARCH=$ABI;;
esac
# Extract GNU configuration name
case $ARCH in
arm)
GNU_CONFIG=arm-linux-androideabi
;;
x86)
GNU_CONFIG=i686-linux-android
;;
*)
GNU_CONFIG="$ARCH-linux-android"
;;
esac
# Generate standalone NDK toolchain installation
NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain"
echo "Generating NDK standalone toolchain installation"
mkdir -p "$NDK_STANDALONE"
run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \
--arch="$ARCH" \
--install-dir="$NDK_STANDALONE"
fail_panic "Can't generate standalone NDK toolchain installation!"
# Rebuild the client library, processor and tools with the auto-tools based
# build system. Even though it's not going to be used, this checks that this
# still works correctly.
echo "Building Android binaries with configure/make"
TMPTARGET="$TMPDIR/target-local"
(
PATH="$NDK_STANDALONE/bin:$PATH"
run mkdir "$TMPTARGET" &&
run mkdir "$TMPDIR"/build-target &&
run cd "$TMPDIR"/build-target &&
run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \
--host="$GNU_CONFIG" &&
run2 make -j$NUM_JOBS install
)
fail_panic "Could not rebuild Android binaries!"
# Copy sources to temporary directory
PROJECT_DIR=$TMPDIR/project
dump "Copying test program sources to: $PROJECT_DIR"
run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" &&
run rm -rf "$PROJECT_DIR/obj" &&
run rm -rf "$PROJECT_DIR/libs"
fail_panic "Could not copy test program sources to: $PROJECT_DIR"
# Build the test program with ndk-build.
dump "Building test program with ndk-build"
export NDK_MODULE_PATH="$PROGDIR"
NDK_BUILD_FLAGS="-j$NUM_JOBS"
if [ "$VERBOSE" -ge 2 ]; then
NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1"
fi
run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI
fail_panic "Can't build test program!"
# Unless --no-device was used, stop right here if ADB isn't in the path,
# or there is no connected device.
if [ "$NO_DEVICE" ]; then
dump "Done. Please connect a device to run all tests!"
clean_exit 0
fi
# Push the program to the device.
TESTAPP=test_google_breakpad
TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad"
if [ ! -f "$TESTAPP_FILE" ]; then
panic "Device requires '$ABI' binaries. None found!"
fi
# Run the program there
dump "Installing test program on device"
DEVICE_TMP=/data/local/tmp
run "$ADB" push "$TESTAPP_FILE" "$DEVICE_TMP/"
fail_panic "Cannot push test program to device!"
dump "Running test program on device"
adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null
if [ $? = 0 ]; then
panic "Test program did *not* crash as expected!"
fi
if [ "$VERBOSE" -ge 1 ]; then
echo -n "Crash log: "
cat "$CRASH_LOG"
fi
# Extract minidump from device
MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG")
MINIDUMP_NAME=$(basename "$MINIDUMP_NAME")
if [ -z "$MINIDUMP_NAME" ]; then
panic "Test program didn't write minidump properly!"
fi
dump "Extracting minidump: $MINIDUMP_NAME"
run "$ADB" pull "$DEVICE_TMP/$MINIDUMP_NAME" .
fail_panic "Can't extract minidump!"
dump "Parsing test program symbols"
if [ "$VERBOSE" -ge 1 ]; then
log "COMMAND: $TMPBIN/dump_syms \
$PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym"
fi
"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym
fail_panic "dump_syms doesn't work!"
VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym)
dump "Found module version: $VERSION"
if [ -z "$VERSION" ]; then
echo "ERROR: Can't find proper module version from symbol dump!"
head -n5 $TESTAPP.sym
clean_exit 1
fi
run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION"
run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/"
dump "Generating stack trace"
# Don't use 'run' to be able to send stdout and stderr to two different files.
log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols"
"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \
"$TMPDIR/symbols" \
> "$BUILD_LOG" 2>>"$RUN_LOG"
fail_panic "minidump_stackwalk doesn't work!"
dump "Checking stack trace content"
if [ "$VERBOSE" -ge 1 ]; then
cat "$BUILD_LOG"
fi
# The generated stack trace should look like the following:
#
# Thread 0 (crashed)
# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4]
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84
# Found by: given as instruction pointer in context
# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3]
# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
# sp = 0xbea2cb50 pc = 0x00009025
# Found by: call frame info
# 2 libc.so + 0x164e5
# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c
# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000
# sp = 0xbea2cc18 pc = 0x400c34e7
# Found by: call frame info
# ...
#
# The most important part for us is ensuring that the source location could
# be extracted, so look at the 'test_breakpad.cpp' references here.
#
# First, extract all the lines with test_google_breakpad! in them, and
# dump the corresponding crash location.
#
# Note that if the source location can't be extracted, the second field
# will only be 'test_google_breakpad' without the exclamation mark.
#
LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG")
if [ -z "$LOCATIONS" ]; then
if [ "$VERBOSE" -lt 1 ]; then
cat "$BUILD_LOG"
fi
panic "No source location found in stack trace!"
fi
# Now check that they all match "[<source file>"
BAD_LOCATIONS=
for LOCATION in $LOCATIONS; do
case $LOCATION in
# Escape the opening bracket, or some shells like Dash will not
# match them properly.
\[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable
;;
*) # Everything else is not!
BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION"
;;
esac
done
if [ "$BAD_LOCATIONS" ]; then
dump "ERROR: Generated stack trace doesn't contain valid source locations:"
cat "$BUILD_LOG"
echo "Bad locations are: $BAD_LOCATIONS"
clean_exit 1
fi
echo "All clear! Congratulations."
clean_exit 0

View File

@@ -1,32 +0,0 @@
This is a sample Android executable that can be used to test the
Google Breakpad client library on Android.
Its purpose is simply to crash and generate a minidump under /data/local/tmp.
Build instructions:
cd android/sample_app
$NDK/ndk-build
Where $NDK points to a valid Android NDK installation.
Usage instructions:
After buildind the test program, send it to a device, then run it as
the shell UID:
adb push libs/armeabi/test_google_breakpad /data/local/tmp
adb shell /data/local/tmp/test_google_breakpad
This will simply crash after dumping the name of the generated minidump
file.
See jni/test_breakpad.cpp for details.
Use 'armeabi-v7a' instead of 'armeabi' above to test the ARMv7-A version
of the binary.
Note:
If you plan to use the library in a regular Android application, store
the minidump files either to your app-specific directory, or to the SDCard
(the latter requiring a specific permission).

View File

@@ -1,44 +0,0 @@
# 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test_google_breakpad
LOCAL_SRC_FILES := test_breakpad.cpp
LOCAL_STATIC_LIBRARIES += breakpad_client
include $(BUILD_EXECUTABLE)
# If NDK_MODULE_PATH is defined, import the module, otherwise do a direct
# includes. This allows us to build in all scenarios easily.
ifneq ($(NDK_MODULE_PATH),)
$(call import-module,google_breakpad)
else
include $(LOCAL_PATH)/../../google_breakpad/Android.mk
endif

View File

@@ -1,31 +0,0 @@
# 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.
APP_STL := stlport_static
APP_ABI := armeabi armeabi-v7a

View File

@@ -1 +0,0 @@
/usr/share/automake-1.11/compile

532
autotools/config.guess vendored
View File

@@ -1,10 +1,10 @@
#! /bin/sh
# Attempt to guess a canonical system name.
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012 Free Software Foundation, Inc.
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
# Inc.
timestamp='2012-06-17'
timestamp='2006-07-02'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -17,7 +17,9 @@ timestamp='2012-06-17'
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -25,16 +27,16 @@ timestamp='2012-06-17'
# the same distribution terms that you use for the rest of that program.
# Originally written by Per Bothner. Please send patches (context
# diff format) to <config-patches@gnu.org> and include a ChangeLog
# entry.
# Originally written by Per Bothner <per@bothner.com>.
# Please send patches to <config-patches@gnu.org>. Submit a context
# diff and a properly formatted ChangeLog entry.
#
# This script attempts to guess a canonical system name similar to
# config.sub. If it succeeds, it prints the system name on stdout, and
# exits with 0. Otherwise, it exits with 1.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
# The plan is that this can be called by configure scripts if you
# don't specify an explicit build system type.
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -54,8 +56,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@@ -143,7 +144,7 @@ UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -160,7 +161,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
esac
# The Operating System including object format, if it has switched
@@ -169,7 +169,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
eval $set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
| grep __ELF__ >/dev/null
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
@@ -179,7 +179,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
os=netbsd
os=netbsd
;;
esac
# The OS release
@@ -200,10 +200,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
*:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
@@ -226,7 +222,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -272,10 +268,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
exitcode=$?
trap '' 0
exit $exitcode ;;
exit ;;
Alpha\ *:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# Should we change UNAME_MACHINE based on the output of uname instead
@@ -301,7 +294,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
echo powerpc-ibm-os400
echo powerpc-ibm-os400
exit ;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
echo arm-acorn-riscix${UNAME_RELEASE}
@@ -330,33 +323,14 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
s390x:SunOS:*:*)
echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4H:SunOS:5.*:*)
echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
echo i386-pc-auroraux${UNAME_RELEASE}
exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
eval $set_cc_for_build
SUN_ARCH="i386"
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH="x86_64"
fi
fi
echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
i86pc:SunOS:5.*:*)
echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
@@ -400,23 +374,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
echo m68k-milan-mint${UNAME_RELEASE}
exit ;;
echo m68k-milan-mint${UNAME_RELEASE}
exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
echo m68k-hades-mint${UNAME_RELEASE}
exit ;;
echo m68k-hades-mint${UNAME_RELEASE}
exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
echo m68k-unknown-mint${UNAME_RELEASE}
exit ;;
echo m68k-unknown-mint${UNAME_RELEASE}
exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -486,8 +460,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p`
# DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p`
if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
then
if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
@@ -500,7 +474,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
exit ;;
exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -557,7 +531,7 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
*:AIX:*:[4567])
*:AIX:*:[45])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
@@ -600,52 +574,52 @@ EOF
9000/[678][0-9][0-9])
if [ -x /usr/bin/getconf ]; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case "${sc_cpu_version}" in
523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
case "${sc_kernel_bits}" in
32) HP_ARCH="hppa2.0n" ;;
64) HP_ARCH="hppa2.0w" ;;
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case "${sc_cpu_version}" in
523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
case "${sc_kernel_bits}" in
32) HP_ARCH="hppa2.0n" ;;
64) HP_ARCH="hppa2.0w" ;;
'') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
esac ;;
esac
esac ;;
esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
sed 's/^ //' << EOF >$dummy.c
#define _HPUX_SOURCE
#include <stdlib.h>
#include <unistd.h>
#define _HPUX_SOURCE
#include <stdlib.h>
#include <unistd.h>
int main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
#endif
long cpu = sysconf (_SC_CPU_VERSION);
int main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
#endif
long cpu = sysconf (_SC_CPU_VERSION);
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
case CPU_PA_RISC2_0:
#if defined(_SC_KERNEL_BITS)
switch (bits)
{
case 64: puts ("hppa2.0w"); break;
case 32: puts ("hppa2.0n"); break;
default: puts ("hppa2.0"); break;
} break;
#else /* !defined(_SC_KERNEL_BITS) */
puts ("hppa2.0"); break;
#endif
default: puts ("hppa1.0"); break;
}
exit (0);
}
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
case CPU_PA_RISC2_0:
#if defined(_SC_KERNEL_BITS)
switch (bits)
{
case 64: puts ("hppa2.0w"); break;
case 32: puts ("hppa2.0n"); break;
default: puts ("hppa2.0"); break;
} break;
#else /* !defined(_SC_KERNEL_BITS) */
puts ("hppa2.0"); break;
#endif
default: puts ("hppa1.0"); break;
}
exit (0);
}
EOF
(CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
test -z "$HP_ARCH" && HP_ARCH=hppa
@@ -665,7 +639,7 @@ EOF
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
grep -q __LP64__
grep __LP64__ >/dev/null
then
HP_ARCH="hppa2.0w"
else
@@ -736,22 +710,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
exit ;;
exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
exit ;;
exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
exit ;;
exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
exit ;;
exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
exit ;;
exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -775,14 +749,14 @@ EOF
exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
5000:UNIX_System_V:4.*:*)
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
@@ -794,48 +768,37 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p`
case ${UNAME_PROCESSOR} in
case ${UNAME_MACHINE} in
pc98)
echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
*:MINGW*:*)
i*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
i*:MSYS*:*)
echo ${UNAME_MACHINE}-pc-msys
exit ;;
i*:windows32*:*)
# uname -m includes "-pc" on this system.
echo ${UNAME_MACHINE}-mingw32
# uname -m includes "-pc" on this system.
echo ${UNAME_MACHINE}-mingw32
exit ;;
i*:PW*:*)
echo ${UNAME_MACHINE}-pc-pw32
exit ;;
*:Interix*:*)
case ${UNAME_MACHINE} in
x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
IA64)
echo ia64-unknown-interix${UNAME_RELEASE}
exit ;;
esac ;;
x86:Interix*:[3456]*)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
EM64T:Interix*:[3456]*)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
[345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
echo i${UNAME_MACHINE}-pc-mks
exit ;;
8664:Windows_NT:*)
echo x86_64-pc-mks
exit ;;
i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
@@ -865,68 +828,20 @@ EOF
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
aarch64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
PCA57) UNAME_MACHINE=alphapca56 ;;
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
esac
objdump --private-headers /bin/sh | grep -q ld.so.1
if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
exit ;;
arm*:Linux:*:*)
eval $set_cc_for_build
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_EABI__
then
echo ${UNAME_MACHINE}-unknown-linux-gnu
else
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
echo ${UNAME_MACHINE}-unknown-linux-gnueabi
else
echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
fi
fi
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
cris:Linux:*:*)
echo ${UNAME_MACHINE}-axis-linux-gnu
echo cris-axis-linux-gnu
exit ;;
crisv32:Linux:*:*)
echo ${UNAME_MACHINE}-axis-linux-gnu
echo crisv32-axis-linux-gnu
exit ;;
frv:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
hexagon:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
i*86:Linux:*:*)
LIBC=gnu
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#ifdef __dietlibc__
LIBC=dietlibc
#endif
EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
echo frv-unknown-linux-gnu
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -937,33 +852,74 @@ EOF
m68*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
mips:Linux:*:* | mips64:Linux:*:*)
mips:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
#undef ${UNAME_MACHINE}
#undef ${UNAME_MACHINE}el
#undef mips
#undef mipsel
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
CPU=${UNAME_MACHINE}el
CPU=mipsel
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
CPU=${UNAME_MACHINE}
CPU=mips
#else
CPU=
#endif
#endif
EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
/^CPU/{
s: ::g
p
}'`"
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
#undef mips64
#undef mips64el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
CPU=mips64el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
CPU=mips64
#else
CPU=
#endif
#endif
EOF
eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
/^CPU/{
s: ::g
p
}'`"
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
;;
or32:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
echo or32-unknown-linux-gnu
exit ;;
padre:Linux:*:*)
echo sparc-unknown-linux-gnu
ppc:Linux:*:*)
echo powerpc-unknown-linux-gnu
exit ;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
echo hppa64-unknown-linux-gnu
ppc64:Linux:*:*)
echo powerpc64-unknown-linux-gnu
exit ;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
PCA57) UNAME_MACHINE=alphapca56 ;;
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
esac
objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
@@ -973,17 +929,14 @@ EOF
*) echo hppa-unknown-linux-gnu ;;
esac
exit ;;
ppc64:Linux:*:*)
echo powerpc64-unknown-linux-gnu
exit ;;
ppc:Linux:*:*)
echo powerpc-unknown-linux-gnu
parisc64:Linux:*:* | hppa64:Linux:*:*)
echo hppa64-unknown-linux-gnu
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux
exit ;;
sh64*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
@@ -991,18 +944,75 @@ EOF
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
tile*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-gnu
exit ;;
x86_64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
exit ;;
xtensa*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-gnu
echo x86_64-unknown-linux-gnu
exit ;;
i*86:Linux:*:*)
# The BFD linker knows what the default object file format is, so
# first see if it will tell us. cd to the root directory to prevent
# problems with other programs or directories called `ld' in the path.
# Set LC_ALL=C to ensure ld outputs messages in English.
ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
| sed -ne '/supported targets:/!d
s/[ ][ ]*/ /g
s/.*supported targets: *//
s/ .*//
p'`
case "$ld_supported_targets" in
elf32-i386)
TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
;;
a.out-i386-linux)
echo "${UNAME_MACHINE}-pc-linux-gnuaout"
exit ;;
coff-i386)
echo "${UNAME_MACHINE}-pc-linux-gnucoff"
exit ;;
"")
# Either a pre-BFD a.out linker (linux-gnuoldld) or
# one that does not give us useful --help.
echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
exit ;;
esac
# Determine whether the default compiler is a.out or elf
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#include <features.h>
#ifdef __ELF__
# ifdef __GLIBC__
# if __GLIBC__ >= 2
LIBC=gnu
# else
LIBC=gnulibc1
# endif
# else
LIBC=gnulibc1
# endif
#else
#if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
LIBC=gnu
#else
LIBC=gnuaout
#endif
#endif
#ifdef __dietlibc__
LIBC=dietlibc
#endif
EOF
eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
/^LIBC/{
s: ::g
p
}'`"
test x"${LIBC}" != x && {
echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
exit
}
test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
@@ -1010,11 +1020,11 @@ EOF
echo i386-sequent-sysv4
exit ;;
i*86:UNIX_SV:4.2MP:2.*)
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
# Use sysv4.2uw... so that sysv4* matches it.
# Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1031,7 +1041,7 @@ EOF
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
@@ -1046,7 +1056,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6.
# UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1074,13 +1084,10 @@ EOF
exit ;;
pc:*:*:*)
# Left here for compatibility:
# uname -m prints for DJGPP always 'pc', but it prints nothing about
# the processor, so we play safe by assuming i586.
# Note: whatever this is, it MUST be the same as what config.sub
# prints for the "djgpp" host, or else GDB configury will decide that
# this is a cross-build.
echo i586-pc-msdosdjgpp
exit ;;
# uname -m prints for DJGPP always 'pc', but it prints nothing about
# the processor, so we play safe by assuming i386.
echo i386-pc-msdosdjgpp
exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1115,18 +1122,8 @@ EOF
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4; exit; } ;;
NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3'
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3${OS_REL}; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; }
/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
@@ -1139,7 +1136,7 @@ EOF
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
@@ -1159,10 +1156,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
echo i586-unisys-sysv4
exit ;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
echo i586-unisys-sysv4
exit ;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes <hewes@openmarket.com>.
# How about differentiating between stratus architectures? -djm
@@ -1188,11 +1185,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
echo mips-nec-sysv${UNAME_RELEASE}
echo mips-nec-sysv${UNAME_RELEASE}
else
echo mips-unknown-sysv${UNAME_RELEASE}
echo mips-unknown-sysv${UNAME_RELEASE}
fi
exit ;;
exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1202,9 +1199,6 @@ EOF
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
echo i586-pc-haiku
exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1214,15 +1208,6 @@ EOF
SX-6:SUPER-UX:*:*)
echo sx6-nec-superux${UNAME_RELEASE}
exit ;;
SX-7:SUPER-UX:*:*)
echo sx7-nec-superux${UNAME_RELEASE}
exit ;;
SX-8:SUPER-UX:*:*)
echo sx8-nec-superux${UNAME_RELEASE}
exit ;;
SX-8R:SUPER-UX:*:*)
echo sx8r-nec-superux${UNAME_RELEASE}
exit ;;
Power*:Rhapsody:*:*)
echo powerpc-apple-rhapsody${UNAME_RELEASE}
exit ;;
@@ -1232,16 +1217,6 @@ EOF
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
case $UNAME_PROCESSOR in
i386)
eval $set_cc_for_build
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
UNAME_PROCESSOR="x86_64"
fi
fi ;;
unknown) UNAME_PROCESSOR=powerpc ;;
esac
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
@@ -1257,10 +1232,7 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
NEO-?:NONSTOP_KERNEL:*:*)
echo neo-tandem-nsk${UNAME_RELEASE}
exit ;;
NSE-*:NONSTOP_KERNEL:*:*)
NSE-?:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
@@ -1305,13 +1277,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
echo mips-sei-seiux${UNAME_RELEASE}
echo mips-sei-seiux${UNAME_RELEASE}
exit ;;
*:DragonFly:*:*)
echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
exit ;;
*:*VMS:*:*)
UNAME_MACHINE=`(uname -p) 2>/dev/null`
UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1326,12 +1298,6 @@ EOF
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
i*86:AROS:*:*)
echo ${UNAME_MACHINE}-pc-aros
exit ;;
x86_64:VMkernel:*:*)
echo ${UNAME_MACHINE}-unknown-esx
exit ;;
esac
#echo '(No uname command or uname output not recognized.)' 1>&2
@@ -1354,11 +1320,11 @@ main ()
#include <sys/param.h>
printf ("m68k-sony-newsos%s\n",
#ifdef NEWSOS4
"4"
"4"
#else
""
""
#endif
); exit (0);
); exit (0);
#endif
#endif
@@ -1492,9 +1458,9 @@ This script, last modified $timestamp, has failed to recognize
the operating system you are using. It is advised that you
download the most up to date version of the config scripts from
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
and
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
If the version you run ($0) is already up to date, please
send the following data and any information you think might be

322
autotools/config.sub vendored
View File

@@ -1,10 +1,10 @@
#! /bin/sh
# Configuration validation subroutine script.
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012 Free Software Foundation, Inc.
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
# Inc.
timestamp='2012-06-17'
timestamp='2006-09-20'
# This file is (in principle) common to ALL GNU software.
# The presence of a machine in this file suggests that SOME GNU software
@@ -21,7 +21,9 @@ timestamp='2012-06-17'
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
# 02110-1301, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
@@ -30,16 +32,13 @@ timestamp='2012-06-17'
# Please send patches to <config-patches@gnu.org>. Submit a context
# diff and a properly formatted GNU ChangeLog entry.
# diff and a properly formatted ChangeLog entry.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
# If it is invalid, we print an error message on stderr and exit with code 1.
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
# that are meaningful with *any* GNU software.
@@ -73,8 +72,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
@@ -122,18 +120,12 @@ esac
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
knetbsd*-gnu* | netbsd*-gnu* | \
kopensolaris*-gnu* | \
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
android-linux)
os=-linux-android
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -156,13 +148,10 @@ case $os in
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-apple | -axis | -knuth | -cray | -microblaze)
-apple | -axis | -knuth | -cray)
os=
basic_machine=$1
;;
-bluegene*)
os=-cnk
;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
@@ -177,10 +166,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
-chorusrdb)
os=-chorusrdb
-chorusrdb)
os=-chorusrdb
basic_machine=$1
;;
;;
-hiux*)
os=-hiuxwe2
;;
@@ -225,12 +214,6 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-lynx*178)
os=-lynxos178
;;
-lynx*5)
os=-lynxos5
;;
-lynx*)
os=-lynxos
;;
@@ -255,32 +238,24 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
| aarch64 | aarch64_be \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
| be32 | be64 \
| bfin \
| c4x | clipper \
| d10v | d30v | dlx | dsp16xx \
| epiphany \
| fido | fr30 | frv \
| fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
| le32 | le64 \
| lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
| maxq | mb | microblaze | mcore | mep | metag \
| maxq | mb | microblaze | mcore \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
| mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
| mips64r5900 | mips64r5900el \
| mips64vr | mips64vrel \
| mips64orion | mips64orionel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
@@ -293,42 +268,29 @@ case $basic_machine in
| mipsisa64sr71k | mipsisa64sr71kel \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
| moxie \
| mt \
| msp430 \
| nds32 | nds32le | nds32be \
| nios | nios2 \
| ns16k | ns32k \
| open8 \
| or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
| pyramid \
| rl78 | rx \
| score \
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
| spu \
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
| ubicom32 \
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| spu | strongarm \
| tahoe | thumb | tic4x | tic80 | tron \
| v850 | v850e \
| we32k \
| x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
| z8k)
basic_machine=$basic_machine-unknown
;;
c54x)
basic_machine=tic54x-unknown
;;
c55x)
basic_machine=tic55x-unknown
;;
c6x)
basic_machine=tic6x-unknown
;;
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
m6811 | m68hc11 | m6812 | m68hc12)
# Motorola 68HC11/12.
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -338,21 +300,6 @@ case $basic_machine in
basic_machine=mt-unknown
;;
strongarm | thumb | xscale)
basic_machine=arm-unknown
;;
xgate)
basic_machine=$basic_machine-unknown
os=-none
;;
xscaleeb)
basic_machine=armeb-unknown
;;
xscaleel)
basic_machine=armel-unknown
;;
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
# (2) the word "unknown" tends to confuse beginning users.
@@ -367,36 +314,29 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
| aarch64-* | aarch64_be-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* | avr32-* \
| be32-* | be64-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
| clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
| hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
| le32-* | le64-* \
| lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
| m88110-* | m88k-* | maxq-* | mcore-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
| mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
| mips64r5900-* | mips64r5900el-* \
| mips64vr-* | mips64vrel-* \
| mips64orion-* | mips64orionel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
@@ -411,36 +351,27 @@ case $basic_machine in
| mmix-* \
| mt-* \
| msp430-* \
| nds32-* | nds32le-* | nds32be-* \
| nios-* | nios2-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| open8-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
| pyramid-* \
| rl78-* | romp-* | rs6000-* | rx-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| romp-* | rs6000-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
| sparclite-* \
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
| tahoe-* \
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
| tahoe-* | thumb-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
| tile*-* \
| tron-* \
| ubicom32-* \
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
| vax-* \
| v850-* | v850e-* | vax-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
| xstormy16-* | xtensa-* \
| ymp-* \
| z8k-* | z80-*)
;;
# Recognize the basic CPU types without company name, with glob match.
xtensa*)
basic_machine=$basic_machine-unknown
| z8k-*)
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
@@ -458,7 +389,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
abacus)
abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -504,10 +435,6 @@ case $basic_machine in
basic_machine=m68k-apollo
os=-bsd
;;
aros)
basic_machine=i386-pc
os=-aros
;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -516,35 +443,10 @@ case $basic_machine in
basic_machine=ns32k-sequent
os=-dynix
;;
blackfin)
basic_machine=bfin-unknown
os=-linux
;;
blackfin-*)
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
bluegene*)
basic_machine=powerpc-ibm
os=-cnk
;;
c54x-*)
basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c55x-*)
basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c6x-*)
basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c90)
basic_machine=c90-cray
os=-unicos
;;
cegcc)
basic_machine=arm-unknown
os=-cegcc
;;
convex-c1)
basic_machine=c1-convex
os=-bsd
@@ -573,8 +475,8 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
cr16 | cr16-*)
basic_machine=cr16-unknown
cr16c)
basic_machine=cr16c-unknown
os=-elf
;;
crds | unos)
@@ -612,10 +514,6 @@ case $basic_machine in
basic_machine=m88k-motorola
os=-sysv3
;;
dicos)
basic_machine=i686-pc
os=-dicos
;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
@@ -731,6 +629,7 @@ case $basic_machine in
i370-ibm* | ibm*)
basic_machine=i370-ibm
;;
# I'm not sure what "Sysv32" means. Should this be sysv3.2?
i*86v32)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv32
@@ -769,14 +668,6 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
m68knommu)
basic_machine=m68k-unknown
os=-linux
;;
m68knommu-*)
basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
m88k-omron*)
basic_machine=m88k-omron
;;
@@ -788,17 +679,10 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
microblaze)
basic_machine=microblaze-xilinx
;;
mingw32)
basic_machine=i386-pc
os=-mingw32
;;
mingw32ce)
basic_machine=arm-unknown
os=-mingw32ce
;;
miniframe)
basic_machine=m68000-convergent
;;
@@ -827,18 +711,10 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
msys)
basic_machine=i386-pc
os=-msys
;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
nacl)
basic_machine=le32-unknown
os=-nacl
;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -903,12 +779,6 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
neo-tandem)
basic_machine=neo-tandem
;;
nse-tandem)
basic_machine=nse-tandem
;;
nsr-tandem)
basic_machine=nsr-tandem
;;
@@ -939,14 +809,6 @@ case $basic_machine in
basic_machine=i860-intel
os=-osf
;;
parisc)
basic_machine=hppa-unknown
os=-linux
;;
parisc-*)
basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
pbd)
basic_machine=sparc-tti
;;
@@ -991,10 +853,9 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
ppc | ppcbe) basic_machine=powerpc-unknown
ppc) basic_machine=powerpc-unknown
;;
ppc-* | ppcbe-*)
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
@@ -1064,9 +925,6 @@ case $basic_machine in
basic_machine=sh-hitachi
os=-hms
;;
sh5el)
basic_machine=sh5le-unknown
;;
sh64)
basic_machine=sh64-unknown
;;
@@ -1088,9 +946,6 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
strongarm-* | thumb-*)
basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
sun2)
basic_machine=m68000-sun
;;
@@ -1147,9 +1002,17 @@ case $basic_machine in
basic_machine=t90-cray
os=-unicos
;;
tile*)
basic_machine=$basic_machine-unknown
os=-linux-gnu
tic54x | c54x*)
basic_machine=tic54x-unknown
os=-coff
;;
tic55x | c55x*)
basic_machine=tic55x-unknown
os=-coff
;;
tic6x | c6x*)
basic_machine=tic6x-unknown
os=-coff
;;
tx39)
basic_machine=mipstx39-unknown
@@ -1218,9 +1081,6 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
xscale-* | xscalee[bl]-*)
basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1229,10 +1089,6 @@ case $basic_machine in
basic_machine=z8k-unknown
os=-sim
;;
z80-*-coff)
basic_machine=z80-unknown
os=-sim
;;
none)
basic_machine=none-none
os=-none
@@ -1271,7 +1127,7 @@ case $basic_machine in
we32k)
basic_machine=we32k-att
;;
sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
@@ -1318,12 +1174,9 @@ esac
if [ x"$os" != x"" ]
then
case $os in
# First match some system type aliases
# that might get confused with valid system types.
# First match some system type aliases
# that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
-auroraux)
os=-auroraux
;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
@@ -1344,23 +1197,21 @@ case $os in
# Each alternative MUST END IN A *, to match a version number.
# -sysv* is not here because it comes later, after sysvr4.
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
| -sym* | -kopensolaris* \
| -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
| -aos* | -aros* \
| -aos* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
| -bitrig* | -openbsd* | -solidbsd* \
| -openbsd* | -solidbsd* \
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
| -chorusos* | -chorusrdb* | -cegcc* \
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -linux-gnu* | -linux-android* \
| -linux-newlib* | -linux-uclibc* \
| -chorusos* | -chorusrdb* \
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
@@ -1368,7 +1219,7 @@ case $os in
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
| -skyos* | -haiku* | -rdos* | -toppers*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1407,7 +1258,7 @@ case $os in
-opened*)
os=-openedition
;;
-os400*)
-os400*)
os=-os400
;;
-wince*)
@@ -1456,7 +1307,7 @@ case $os in
-sinix*)
os=-sysv4
;;
-tpf*)
-tpf*)
os=-tpf
;;
-triton*)
@@ -1498,11 +1349,6 @@ case $os in
-zvmoe)
os=-zvmoe
;;
-dicos*)
os=-dicos
;;
-nacl*)
;;
-none)
;;
*)
@@ -1525,10 +1371,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
score-*)
score-*)
os=-elf
;;
spu-*)
spu-*)
os=-elf
;;
*-acorn)
@@ -1540,20 +1386,8 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
c4x-* | tic4x-*)
os=-coff
;;
hexagon-*)
os=-elf
;;
tic54x-*)
os=-coff
;;
tic55x-*)
os=-coff
;;
tic6x-*)
os=-coff
c4x-* | tic4x-*)
os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1573,13 +1407,13 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
# This also exists in the configure program, but was not the
# default.
# os=-sunos4
;;
m68*-cisco)
os=-aout
;;
mep-*)
os=-elf
;;
mips*-cisco)
os=-elf
;;
@@ -1604,7 +1438,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
*-knuth)
*-knuth)
os=-mmixware
;;
*-wec)
@@ -1709,7 +1543,7 @@ case $basic_machine in
-sunos*)
vendor=sun
;;
-cnk*|-aix*)
-aix*)
vendor=ibm
;;
-beos*)

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
# This file is used by gcl to get repository specific information.
CODE_REVIEW_SERVER: breakpad.appspot.com
CC_LIST: google-breakpad-dev@googlegroups.com
TRY_ON_UPLOAD: False
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=

20529
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -30,102 +30,22 @@
AC_PREREQ(2.57)
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
AC_INIT(breakpad, 0.1, opensource@google.com)
dnl Sanity check: the argument is just a file that should exist.
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(autotools)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
AM_INIT_AUTOMAKE(subdir-objects tar-ustar)
AM_CONFIG_HEADER(src/config.h)
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_RANLIB
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL_DEPS)
AC_HEADER_STDC
m4_include(m4/ax_pthread.m4)
AX_PTHREAD
AC_CHECK_HEADERS([a.out.h])
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux* | *-android* )
LINUX_HOST=true
;;
esac
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue)
# Only use Android support headers when compiling for Android
case $host in
*-android*)
ANDROID_HOST=true
;;
esac
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
AC_ARG_ENABLE(m32,
AS_HELP_STRING([--enable-m32],
[Compile/build with -m32]
[(default is no)]),
[case "${enableval}" in
yes)
CFLAGS="${CFLAGS} -m32"
CXXFLAGS="${CXXFLAGS} -m32"
usem32=true
;;
no)
usem32=false
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --enable-m32)
;;
esac],
[usem32=false])
AC_ARG_ENABLE(processor,
AS_HELP_STRING([--disable-processor],
[Don't build processor library]
[(default is no)]),
[case "${enableval}" in
yes)
disable_processor=false
;;
no)
disable_processor=true
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --disable-processor)
;;
esac],
[disable_processor=false])
AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue)
AC_ARG_ENABLE(tools,
AS_HELP_STRING([--disable-tools],
[Don't build tool binaries]
[(default is no)]),
[case "${enableval}" in
yes)
disable_tools=false
;;
no)
disable_tools=true
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --disable-tools)
;;
esac],
[disable_tools=false])
AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue)
if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
fi
AC_ARG_ENABLE(selftest,
AS_HELP_STRING([--enable-selftest],

View File

@@ -1,283 +0,0 @@
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
*-darwin*)
acx_pthread_flags="-pthread $acx_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>
static void routine(void* a) {a=0;}
static void* start_routine(void* a) {return a;}],
[pthread_t th; pthread_attr_t attr;
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_create(&th,0,start_routine,0);
pthread_cleanup_pop(0); ],
[ax_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
[attr_name=$attr; break])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl AX_PTHREAD

7377
m4/libtool.m4 vendored

File diff suppressed because it is too large Load Diff

368
m4/ltoptions.m4 vendored
View File

@@ -1,368 +0,0 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option `$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl `shared' nor `disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [0], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the `shared' and
# `disable-shared' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the `static' and
# `disable-static' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the `fast-install'
# and `disable-fast-install' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
# LT_INIT options.
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[pic_mode="$withval"],
[pic_mode=default])
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

123
m4/ltsugar.m4 vendored
View File

@@ -1,123 +0,0 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59 which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

23
m4/ltversion.m4 vendored
View File

@@ -1,23 +0,0 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# Generated from ltversion.in.
# serial 3017 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
m4_define([LT_PACKAGE_REVISION], [1.3017])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.2.6b'
macro_revision='1.3017'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

92
m4/lt~obsolete.m4 vendored
View File

@@ -1,92 +0,0 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 4 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])

View File

@@ -1,72 +0,0 @@
// 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"

View File

@@ -1,219 +0,0 @@
// 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.
// Framework to provide a simple C API to crash reporting for
// applications. By default, if any machine-level exception (e.g.,
// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
// object as follows:
//
// 1. Create a minidump file (see Breakpad for details)
// 2. Create a config file.
//
// These files can then be uploaded to a server.
typedef void *BreakpadRef;
#ifdef __cplusplus
extern "C" {
#endif
#include <Foundation/Foundation.h>
#include <client/apple/Framework/BreakpadDefines.h>
// The keys in the dictionary returned by |BreakpadGenerateReport|.
#define BREAKPAD_OUTPUT_DUMP_FILE "BreakpadDumpFile"
#define BREAKPAD_OUTPUT_CONFIG_FILE "BreakpadConfigFile"
// Optional user-defined function to decide if we should handle this crash or
// forward it along.
// Return true if you want Breakpad to handle it.
// Return false if you want Breakpad to skip it
// The exception handler always returns false, as if SEND_AND_EXIT were false
// (which means the next exception handler will take the exception)
typedef bool (*BreakpadFilterCallback)(int exception_type,
int exception_code,
mach_port_t crashing_thread,
void *context);
// Create a new BreakpadRef object and install it as an exception
// handler. The |parameters| will typically be the contents of your
// bundle's Info.plist.
//
// You can also specify these additional keys for customizable behavior:
// Key: Value:
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
// This one is used as the key to identify
// the product when uploading. Falls back to
// CFBundleName if not specified.
// REQUIRED
//
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
// name for the product when the crash_sender
// pops up UI for the user. Falls back first to
// CFBundleDisplayName and then to
// BREAKPAD_PRODUCT if not specified.
//
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
// as metadata for crash report. Falls back to
// CFBundleVersion if not specified.
// REQUIRED
//
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
// been created that you can send to <vendor>")
//
// BREAKPAD_URL URL destination for reporting
// REQUIRED
//
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
// in. By default, we use
// ~/Library/Cache/Breakpad/<BREAKPAD_PRODUCT>
// The path you specify here is tilde-expanded.
//
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
// rewrite the upload parameters for a specific
// server type. The currently valid values are
// 'socorro' or 'google'. If you want to add
// other types, see the function in
// crash_report_sender.m that maps parameters to
// URL parameters. Defaults to 'google'.
//
// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
// parameters that are uploaded to the
// server. The parameters are sent as
// is to the crash server. Their
// content isn't added to the minidump
// but pass as URL parameters when
// uploading theminidump to the crash
// server.
//=============================================================================
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
// will be the CFBundleName and the BREAKPAD_VERSION will be the
// CFBundleVersion when these keys are present in the bundle's
// Info.plist, which is usually passed in to BreakpadCreate() as an
// NSDictionary (you could also pass in another dictionary that had
// the same keys configured). If the BREAKPAD_PRODUCT or
// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
// fail. You have been warned.
//
// If you are running in a debugger, Breakpad will not install, unless the
// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
//
//=============================================================================
// The following are NOT user-supplied but are documented here for
// completeness. They are calculated by Breakpad during initialization &
// crash-dump generation, or entered in by the user.
//
// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
// process started
//
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
// process crashed.
//
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
// has been running. This parameter is not
// set until the crash-dump-generation phase.
//
// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
// internally, because Breakpad uses
// the same dictionary internally to
// track both its internal
// configuration parameters and
// parameters meant to be uploaded
// to the server. This string is
// used internally by Breakpad to
// prefix user-supplied parameter
// names so those can be sent to the
// server without leaking Breakpad's
// internal values.
// Returns a new BreakpadRef object on success, NULL otherwise.
BreakpadRef BreakpadCreate(NSDictionary *parameters);
// Uninstall and release the data associated with |ref|.
void BreakpadRelease(BreakpadRef ref);
// User defined key and value string storage. Generally this is used
// to configure Breakpad's internal operation, such as whether the
// crash_sender should prompt the user, or the filesystem location for
// the minidump file. See Breakpad.h for some parameters that can be
// set. Anything longer than 255 bytes will be truncated. Note that
// the string is converted to UTF8 before truncation, so any multibyte
// character that straddles the 255(256 - 1 for terminator) byte limit
// will be mangled.
//
// A maximum number of 64 key/value pairs are supported. An assert()
// will fire if more than this number are set. Unfortunately, right
// now, the same dictionary is used for both Breakpad's parameters AND
// the Upload parameters.
//
// TODO (nealsid): Investigate how necessary this is if we don't
// automatically upload parameters to the server anymore.
// TODO (nealsid): separate server parameter dictionary from the
// dictionary used to configure Breakpad, and document limits for each
// independently.
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
// You can use this method to specify parameters that will be uploaded
// to the crash server. They will be automatically encoded as
// necessary. Note that as mentioned above there are limits on both
// the number of keys and their length.
void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
NSString *value);
// This method will remove a previously-added parameter from the
// upload parameter set.
void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
// Method to handle uploading data to the server
// Returns if there is some report to send to the server.
bool BreakpadHasCrashReportToUpload(BreakpadRef ref);
// Upload next report to the server.
void BreakpadUploadNextReport(BreakpadRef ref);
// Upload a file to the server. |data| is the content of the file to sent.
// |server_parameters| is additional server parameters to send.
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
NSDictionary *server_parameters);
// Generate a breakpad minidump and configuration file in the dump directory.
// The report will be available for uploading. The paths of the created files
// are returned in the dictionary. |server_parameters| is additional server
// parameters to add in the config file.
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
NSDictionary *server_parameters);
#ifdef __cplusplus
}
#endif

View File

@@ -1,828 +0,0 @@
// 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.
#define VERBOSE 0
#if VERBOSE
static bool gDebugLog = true;
#else
static bool gDebugLog = false;
#endif
#define DEBUGLOG if (gDebugLog) fprintf
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
#import "common/mac/SimpleStringDictionary.h"
#import "client/mac/crash_generation/ConfigFile.h"
#import "client/mac/sender/uploader.h"
#import "client/mac/handler/exception_handler.h"
#import "client/mac/handler/minidump_generator.h"
#import "client/ios/Breakpad.h"
#import "client/ios/handler/ios_exception_minidump_generator.h"
#import "client/mac/handler/protected_memory_allocator.h"
#import <sys/stat.h>
#import <sys/sysctl.h>
#import <Foundation/Foundation.h>
using google_breakpad::ConfigFile;
using google_breakpad::EnsureDirectoryPathExists;
using google_breakpad::KeyValueEntry;
using google_breakpad::SimpleStringDictionary;
using google_breakpad::SimpleStringDictionaryIterator;
//=============================================================================
// We want any memory allocations which are used by breakpad during the
// exception handling process (after a crash has happened) to be read-only
// to prevent them from being smashed before a crash occurs. Unfortunately
// we cannot protect against smashes to our exception handling thread's
// stack.
//
// NOTE: Any memory allocations which are not used during the exception
// handling process may be allocated in the normal ways.
//
// The ProtectedMemoryAllocator class provides an Allocate() method which
// we'll using in conjunction with placement operator new() to control
// allocation of C++ objects. Note that we don't use operator delete()
// but instead call the objects destructor directly: object->~ClassName();
//
ProtectedMemoryAllocator *gMasterAllocator = NULL;
ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
// Mutex for thread-safe access to the key/value dictionary used by breakpad.
// It's a global instead of an instance variable of Breakpad
// since it can't live in a protected memory area.
pthread_mutex_t gDictionaryMutex;
//=============================================================================
// Stack-based object for thread-safe access to a memory-protected region.
// It's assumed that normally the memory block (allocated by the allocator)
// is protected (read-only). Creating a stack-based instance of
// ProtectedMemoryLocker will unprotect this block after taking the lock.
// Its destructor will first re-protect the memory then release the lock.
class ProtectedMemoryLocker {
public:
// allocator may be NULL, in which case no Protect() or Unprotect() calls
// will be made, but a lock will still be taken
ProtectedMemoryLocker(pthread_mutex_t *mutex,
ProtectedMemoryAllocator *allocator)
: mutex_(mutex), allocator_(allocator) {
// Lock the mutex
assert(pthread_mutex_lock(mutex_) == 0);
// Unprotect the memory
if (allocator_ ) {
allocator_->Unprotect();
}
}
~ProtectedMemoryLocker() {
// First protect the memory
if (allocator_) {
allocator_->Protect();
}
// Then unlock the mutex
assert(pthread_mutex_unlock(mutex_) == 0);
};
private:
// Keep anybody from ever creating one of these things not on the stack.
ProtectedMemoryLocker() { }
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
pthread_mutex_t *mutex_;
ProtectedMemoryAllocator *allocator_;
};
//=============================================================================
class Breakpad {
public:
// factory method
static Breakpad *Create(NSDictionary *parameters) {
// Allocate from our special allocation pool
Breakpad *breakpad =
new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
Breakpad();
if (!breakpad)
return NULL;
if (!breakpad->Initialize(parameters)) {
// Don't use operator delete() here since we allocated from special pool
breakpad->~Breakpad();
return NULL;
}
return breakpad;
}
~Breakpad();
void SetKeyValue(NSString *key, NSString *value);
NSString *KeyValue(NSString *key);
void RemoveKeyValue(NSString *key);
NSString *NextCrashReportToUpload();
void UploadNextReport();
void UploadData(NSData *data, NSString *name,
NSDictionary *server_parameters);
NSDictionary *GenerateReport(NSDictionary *server_parameters);
private:
Breakpad()
: handler_(NULL),
config_params_(NULL) {}
bool Initialize(NSDictionary *parameters);
bool ExtractParameters(NSDictionary *parameters);
// Dispatches to HandleMinidump()
static bool HandleMinidumpCallback(const char *dump_dir,
const char *minidump_id,
void *context, bool succeeded);
bool HandleMinidump(const char *dump_dir,
const char *minidump_id);
// NSException handler
static void UncaughtExceptionHandler(NSException *exception);
// Handle an uncaught NSException.
void HandleUncaughtException(NSException *exception);
// Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
// MachineExceptions.h, we have to explicitly name the handler.
google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
SimpleStringDictionary *config_params_; // Create parameters (STRONG)
ConfigFile config_file_;
// A static reference to the current Breakpad instance. Used for handling
// NSException.
static Breakpad *current_breakpad_;
};
Breakpad *Breakpad::current_breakpad_ = NULL;
#pragma mark -
#pragma mark Helper functions
//=============================================================================
// Helper functions
//=============================================================================
static BOOL IsDebuggerActive() {
BOOL result = NO;
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
// We check both defaults and the environment variable here
BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
if (!ignoreDebugger) {
char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
ignoreDebugger =
(ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
}
if (!ignoreDebugger) {
pid_t pid = getpid();
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
int mibSize = sizeof(mib) / sizeof(int);
size_t actualSize;
if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
if (info) {
// This comes from looking at the Darwin xnu Kernel
if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
free(info);
}
}
}
return result;
}
//=============================================================================
bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
const char *minidump_id,
void *context, bool succeeded) {
Breakpad *breakpad = (Breakpad *)context;
// If our context is damaged or something, just return false to indicate that
// the handler should continue without us.
if (!breakpad || !succeeded)
return false;
return breakpad->HandleMinidump(dump_dir, minidump_id);
}
//=============================================================================
void Breakpad::UncaughtExceptionHandler(NSException *exception) {
NSSetUncaughtExceptionHandler(NULL);
if (current_breakpad_) {
current_breakpad_->HandleUncaughtException(exception);
}
}
//=============================================================================
#pragma mark -
//=============================================================================
bool Breakpad::Initialize(NSDictionary *parameters) {
// Initialize
current_breakpad_ = this;
config_params_ = NULL;
handler_ = NULL;
// Gather any user specified parameters
if (!ExtractParameters(parameters)) {
return false;
}
// Check for debugger
if (IsDebuggerActive()) {
DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
return true;
}
// Create the handler (allocating it in our special protected pool)
handler_ =
new (gBreakpadAllocator->Allocate(
sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
0, &HandleMinidumpCallback, this, true, 0);
NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler);
return true;
}
//=============================================================================
Breakpad::~Breakpad() {
NSSetUncaughtExceptionHandler(NULL);
current_breakpad_ = NULL;
// Note that we don't use operator delete() on these pointers,
// since they were allocated by ProtectedMemoryAllocator objects.
//
if (config_params_) {
config_params_->~SimpleStringDictionary();
}
if (handler_)
handler_->~ExceptionHandler();
}
//=============================================================================
bool Breakpad::ExtractParameters(NSDictionary *parameters) {
NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
NSDictionary *serverParameters =
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
if (!product)
product = [parameters objectForKey:@"CFBundleName"];
if (!display) {
display = [parameters objectForKey:@"CFBundleDisplayName"];
if (!display) {
display = product;
}
}
if (!version)
version = [parameters objectForKey:@"CFBundleVersion"];
if (!vendor) {
vendor = @"Vendor not specified";
}
if (!dumpSubdirectory) {
NSString *cachePath =
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
NSUserDomainMask,
YES)
objectAtIndex:0];
dumpSubdirectory =
[cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
EnsureDirectoryPathExists(dumpSubdirectory);
}
// The product, version, and URL are required values.
if (![product length]) {
DEBUGLOG(stderr, "Missing required product key.\n");
return false;
}
if (![version length]) {
DEBUGLOG(stderr, "Missing required version key.\n");
return false;
}
if (![urlStr length]) {
DEBUGLOG(stderr, "Missing required URL key.\n");
return false;
}
config_params_ =
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
SimpleStringDictionary();
SimpleStringDictionary &dictionary = *config_params_;
dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);
struct timeval tv;
gettimeofday(&tv, NULL);
char timeStartedString[32];
sprintf(timeStartedString, "%zd", tv.tv_sec);
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
if (serverParameters) {
// For each key-value pair, call BreakpadAddUploadParameter()
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
NSString *aParameter;
while ((aParameter = [keyEnumerator nextObject])) {
BreakpadAddUploadParameter(this, aParameter,
[serverParameters objectForKey:aParameter]);
}
}
return true;
}
//=============================================================================
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
// We allow nil values. This is the same as removing the keyvalue.
if (!config_params_ || !key)
return;
config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
}
//=============================================================================
NSString *Breakpad::KeyValue(NSString *key) {
if (!config_params_ || !key)
return nil;
const char *value = config_params_->GetValueForKey([key UTF8String]);
return value ? [NSString stringWithUTF8String:value] : nil;
}
//=============================================================================
void Breakpad::RemoveKeyValue(NSString *key) {
if (!config_params_ || !key) return;
config_params_->RemoveKey([key UTF8String]);
}
//=============================================================================
NSString *Breakpad::NextCrashReportToUpload() {
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
if (!directory)
return nil;
NSArray *dirContents = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:directory error:nil];
NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
NSString *config = [configs lastObject];
if (!config)
return nil;
return [NSString stringWithFormat:@"%@/%@", directory, config];
}
//=============================================================================
void Breakpad::UploadNextReport() {
NSString *configFile = NextCrashReportToUpload();
if (configFile) {
Uploader *uploader = [[[Uploader alloc]
initWithConfigFile:[configFile UTF8String]] autorelease];
if (uploader)
[uploader report];
}
}
//=============================================================================
void Breakpad::UploadData(NSData *data, NSString *name,
NSDictionary *server_parameters) {
NSMutableDictionary *config = [NSMutableDictionary dictionary];
SimpleStringDictionaryIterator it(*config_params_);
while (const KeyValueEntry *next = it.Next()) {
[config setValue:[NSString stringWithUTF8String:next->GetValue()]
forKey:[NSString stringWithUTF8String:next->GetKey()]];
}
Uploader *uploader =
[[[Uploader alloc] initWithConfig:config] autorelease];
for (NSString *key in server_parameters) {
[uploader addServerParameter:[server_parameters objectForKey:key]
forKey:key];
}
[uploader uploadData:data name:name];
}
//=============================================================================
NSDictionary *Breakpad::GenerateReport(NSDictionary *server_parameters) {
NSString *dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
if (!dumpDirAsNSString)
return nil;
const char *dumpDir = [dumpDirAsNSString UTF8String];
google_breakpad::MinidumpGenerator generator(mach_task_self(),
MACH_PORT_NULL);
std::string dumpId;
std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId);
bool success = generator.Write(dumpFilename.c_str());
if (!success)
return nil;
SimpleStringDictionary params = *config_params_;
for (NSString *key in server_parameters) {
params.SetKeyValue([key UTF8String],
[[server_parameters objectForKey:key] UTF8String]);
}
ConfigFile config_file;
config_file.WriteFile(dumpDir, &params, dumpDir, dumpId.c_str());
// Handle results.
NSMutableDictionary *result = [NSMutableDictionary dictionary];
NSString *dumpFullPath = [dumpDirAsNSString stringByAppendingPathComponent:
[NSString stringWithUTF8String:dumpFilename.c_str()]];
[result setValue:dumpFullPath
forKey:@BREAKPAD_OUTPUT_DUMP_FILE];
[result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()]
forKey:@BREAKPAD_OUTPUT_CONFIG_FILE];
return result;
}
//=============================================================================
bool Breakpad::HandleMinidump(const char *dump_dir,
const char *minidump_id) {
DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n");
config_file_.WriteFile(dump_dir,
config_params_,
dump_dir,
minidump_id);
// Return true here to indicate that we've processed things as much as we
// want.
return true;
}
//=============================================================================
void Breakpad::HandleUncaughtException(NSException *exception) {
// Generate the minidump.
google_breakpad::IosExceptionMinidumpGenerator generator(exception);
const char *minidump_path =
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
std::string minidump_id;
std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path,
&minidump_id);
generator.Write(minidump_filename.c_str());
// Copy the config params and our custom parameter. This is necessary for 2
// reasons:
// 1- config_params_ is protected.
// 2- If the application crash while trying to handle this exception, a usual
// report will be generated. This report must not contain these special
// keys.
SimpleStringDictionary params = *config_params_;
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception");
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName",
[[exception name] UTF8String]);
params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason",
[[exception reason] UTF8String]);
// And finally write the config file.
ConfigFile config_file;
config_file.WriteFile(minidump_path,
&params,
minidump_path,
minidump_id.c_str());
}
//=============================================================================
#pragma mark -
#pragma mark Public API
//=============================================================================
BreakpadRef BreakpadCreate(NSDictionary *parameters) {
try {
// This is confusing. Our two main allocators for breakpad memory are:
// - gKeyValueAllocator for the key/value memory
// - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
// breakpad allocations which are accessed at exception handling time.
//
// But in order to avoid these two allocators themselves from being smashed,
// we'll protect them as well by allocating them with gMasterAllocator.
//
// gMasterAllocator itself will NOT be protected, but this doesn't matter,
// since once it does its allocations and locks the memory, smashes to
// itself don't affect anything we care about.
gMasterAllocator =
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
gKeyValueAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
// Create a mutex for use in accessing the SimpleStringDictionary
int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
if (mutexResult == 0) {
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
// Let's round up to the nearest page size.
//
int breakpad_pool_size = 4096;
/*
sizeof(Breakpad)
+ sizeof(google_breakpad::ExceptionHandler)
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
*/
gBreakpadAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(breakpad_pool_size);
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Breakpad *breakpad = Breakpad::Create(parameters);
if (breakpad) {
// Make read-only to protect against memory smashers
gMasterAllocator->Protect();
gKeyValueAllocator->Protect();
gBreakpadAllocator->Protect();
// Can uncomment this line to figure out how much space was actually
// allocated using this allocator
// printf("gBreakpadAllocator allocated size = %d\n",
// gBreakpadAllocator->GetAllocatedSize() );
[pool release];
return (BreakpadRef)breakpad;
}
[pool release];
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadCreate() : error\n");
}
if (gKeyValueAllocator) {
gKeyValueAllocator->~ProtectedMemoryAllocator();
gKeyValueAllocator = NULL;
}
if (gBreakpadAllocator) {
gBreakpadAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator = NULL;
}
delete gMasterAllocator;
gMasterAllocator = NULL;
return NULL;
}
//=============================================================================
void BreakpadRelease(BreakpadRef ref) {
try {
Breakpad *breakpad = (Breakpad *)ref;
if (gMasterAllocator) {
gMasterAllocator->Unprotect();
gKeyValueAllocator->Unprotect();
gBreakpadAllocator->Unprotect();
breakpad->~Breakpad();
// Unfortunately, it's not possible to deallocate this stuff
// because the exception handling thread is still finishing up
// asynchronously at this point... OK, it could be done with
// locks, etc. But since BreakpadRelease() should usually only
// be called right before the process exits, it's not worth
// deallocating this stuff.
#if 0
gKeyValueAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator->~ProtectedMemoryAllocator();
delete gMasterAllocator;
gMasterAllocator = NULL;
gKeyValueAllocator = NULL;
gBreakpadAllocator = NULL;
#endif
pthread_mutex_destroy(&gDictionaryMutex);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRelease() : error\n");
}
}
//=============================================================================
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
breakpad->SetKeyValue(key, value);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
}
}
void BreakpadAddUploadParameter(BreakpadRef ref,
NSString *key,
NSString *value) {
// The only difference, internally, between an upload parameter and
// a key value one that is set with BreakpadSetKeyValue is that we
// prepend the keyname with a special prefix. This informs the
// crash sender that the parameter should be sent along with the
// POST of the crash dump upload.
try {
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
stringByAppendingString:key];
breakpad->SetKeyValue(prefixedKey, value);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
}
}
void BreakpadRemoveUploadParameter(BreakpadRef ref,
NSString *key) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
@BREAKPAD_SERVER_PARAMETER_PREFIX, key];
breakpad->RemoveKeyValue(prefixedKey);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
//=============================================================================
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
NSString *value = nil;
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (!breakpad || !key || !gKeyValueAllocator)
return nil;
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
value = breakpad->KeyValue(key);
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadKeyValue() : error\n");
}
return value;
}
//=============================================================================
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad && key && gKeyValueAllocator) {
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
breakpad->RemoveKeyValue(key);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
}
}
//=============================================================================
bool BreakpadHasCrashReportToUpload(BreakpadRef ref) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad) {
return breakpad->NextCrashReportToUpload() != 0;
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadHasCrashReportToUpload() : error\n");
}
return false;
}
//=============================================================================
void BreakpadUploadNextReport(BreakpadRef ref) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad) {
breakpad->UploadNextReport();
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadUploadNextReport() : error\n");
}
}
//=============================================================================
void BreakpadUploadData(BreakpadRef ref, NSData *data, NSString *name,
NSDictionary *server_parameters) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad) {
breakpad->UploadData(data, name, server_parameters);
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadUploadData() : error\n");
}
}
//=============================================================================
NSDictionary *BreakpadGenerateReport(BreakpadRef ref,
NSDictionary *server_parameters) {
try {
// Not called at exception time
Breakpad *breakpad = (Breakpad *)ref;
if (breakpad) {
return breakpad->GenerateReport(server_parameters);
} else {
return nil;
}
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadGenerateReport() : error\n");
return nil;
}
}

View File

@@ -1,550 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */; };
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */; };
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C968147D4A4200776EAD /* BreakpadDefines.h */; };
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C96A147D4A4200776EAD /* Breakpad.h */; };
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7C96B147D4A4200776EAD /* Breakpad.mm */; };
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CB9E147D4A4300776EAD /* ConfigFile.h */; };
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */; };
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */; };
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */; };
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */; };
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB0147D4A4300776EAD /* dynamic_images.h */; };
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB1147D4A4300776EAD /* exception_handler.cc */; };
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB2147D4A4300776EAD /* exception_handler.h */; };
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */; };
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB5147D4A4300776EAD /* minidump_generator.h */; };
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */; };
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */; };
16C7CE08147D4A4300776EAD /* uploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBEA147D4A4300776EAD /* uploader.h */; };
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBEB147D4A4300776EAD /* uploader.mm */; };
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */; };
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */; };
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */; };
16C7CE1B147D4A4300776EAD /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */; };
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC4A147D4A4300776EAD /* convert_UTF.c */; };
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC4B147D4A4300776EAD /* convert_UTF.h */; };
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC88147D4A4300776EAD /* GTMLogger.h */; };
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC89147D4A4300776EAD /* GTMLogger.m */; };
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */; };
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */; };
16C7CE7E147D4A4300776EAD /* SimpleStringDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8E147D4A4300776EAD /* SimpleStringDictionary.h */; };
16C7CE7F147D4A4300776EAD /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8F147D4A4300776EAD /* SimpleStringDictionary.mm */; };
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC93147D4A4300776EAD /* file_id.cc */; };
16C7CE84147D4A4300776EAD /* file_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC94147D4A4300776EAD /* file_id.h */; };
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC95147D4A4300776EAD /* macho_id.cc */; };
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC96147D4A4300776EAD /* macho_id.h */; };
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */; };
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9B147D4A4300776EAD /* macho_utilities.h */; };
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9C147D4A4300776EAD /* macho_walker.cc */; };
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9D147D4A4300776EAD /* macho_walker.h */; };
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9F147D4A4300776EAD /* string_utilities.cc */; };
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA0147D4A4300776EAD /* string_utilities.h */; };
16C7CE93147D4A4300776EAD /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCA4147D4A4300776EAD /* md5.cc */; };
16C7CE94147D4A4300776EAD /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA5147D4A4300776EAD /* md5.h */; };
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCB9147D4A4300776EAD /* string_conversion.cc */; };
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCBA147D4A4300776EAD /* string_conversion.h */; };
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C92FAB150DF8330053D7BA /* BreakpadController.h */; };
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C92FAC150DF8330053D7BA /* BreakpadController.mm */; };
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; };
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios_exception_minidump_generator.h; sourceTree = "<group>"; };
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_exception_minidump_generator.mm; sourceTree = "<group>"; };
16C7C968147D4A4200776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = "<group>"; };
16C7C96A147D4A4200776EAD /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad.h; sourceTree = "<group>"; };
16C7C96B147D4A4200776EAD /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Breakpad.mm; sourceTree = "<group>"; };
16C7CB9E147D4A4300776EAD /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConfigFile.mm; sourceTree = "<group>"; };
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
16C7CBB0147D4A4300776EAD /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
16C7CBB1147D4A4300776EAD /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = "<group>"; };
16C7CBB2147D4A4300776EAD /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = "<group>"; };
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = "<group>"; };
16C7CBB5147D4A4300776EAD /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = "<group>"; };
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protected_memory_allocator.cc; sourceTree = "<group>"; };
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protected_memory_allocator.h; sourceTree = "<group>"; };
16C7CBEA147D4A4300776EAD /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = "<group>"; };
16C7CBEB147D4A4300776EAD /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = uploader.mm; sourceTree = "<group>"; };
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "minidump_file_writer-inl.h"; sourceTree = "<group>"; };
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer.cc; sourceTree = "<group>"; };
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_file_writer.h; sourceTree = "<group>"; };
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
16C7CC4A147D4A4300776EAD /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = convert_UTF.c; sourceTree = "<group>"; };
16C7CC4B147D4A4300776EAD /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert_UTF.h; sourceTree = "<group>"; };
16C7CC88147D4A4300776EAD /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = "<group>"; };
16C7CC89147D4A4300776EAD /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = "<group>"; };
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPMultipartUpload.h; sourceTree = "<group>"; };
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPMultipartUpload.m; sourceTree = "<group>"; };
16C7CC8E147D4A4300776EAD /* SimpleStringDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SimpleStringDictionary.h; sourceTree = "<group>"; };
16C7CC8F147D4A4300776EAD /* SimpleStringDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SimpleStringDictionary.mm; sourceTree = "<group>"; };
16C7CC93147D4A4300776EAD /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_id.cc; sourceTree = "<group>"; };
16C7CC94147D4A4300776EAD /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_id.h; sourceTree = "<group>"; };
16C7CC95147D4A4300776EAD /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_id.cc; sourceTree = "<group>"; };
16C7CC96147D4A4300776EAD /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_id.h; sourceTree = "<group>"; };
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_utilities.cc; sourceTree = "<group>"; };
16C7CC9B147D4A4300776EAD /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_utilities.h; sourceTree = "<group>"; };
16C7CC9C147D4A4300776EAD /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_walker.cc; sourceTree = "<group>"; };
16C7CC9D147D4A4300776EAD /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_walker.h; sourceTree = "<group>"; };
16C7CC9F147D4A4300776EAD /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utilities.cc; sourceTree = "<group>"; };
16C7CCA0147D4A4300776EAD /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utilities.h; sourceTree = "<group>"; };
16C7CCA4147D4A4300776EAD /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
16C7CCA5147D4A4300776EAD /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
16C7CCB9147D4A4300776EAD /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conversion.cc; sourceTree = "<group>"; };
16C7CCBA147D4A4300776EAD /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conversion.h; sourceTree = "<group>"; };
16C92FAB150DF8330053D7BA /* BreakpadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadController.h; sourceTree = "<group>"; };
16C92FAC150DF8330053D7BA /* BreakpadController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BreakpadController.mm; sourceTree = "<group>"; };
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; };
AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
D2AAC07C0554694100DB518D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
034768DFFF38A50411DB9C8B /* Products */ = {
isa = PBXGroup;
children = (
D2AAC07E0554694100DB518D /* libBreakpad.a */,
);
name = Products;
sourceTree = "<group>";
};
0867D691FE84028FC02AAC07 /* Breakpad */ = {
isa = PBXGroup;
children = (
08FB77AEFE84172EC02AAC07 /* Classes */,
32C88DFF0371C24200C91783 /* Other Sources */,
0867D69AFE84028FC02AAC07 /* Frameworks */,
034768DFFF38A50411DB9C8B /* Products */,
);
name = Breakpad;
sourceTree = "<group>";
};
0867D69AFE84028FC02AAC07 /* Frameworks */ = {
isa = PBXGroup;
children = (
AACBBE490F95108600F1A2B1 /* Foundation.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
08FB77AEFE84172EC02AAC07 /* Classes */ = {
isa = PBXGroup;
children = (
16C7C965147D4A4200776EAD /* client */,
16C7CC47147D4A4300776EAD /* common */,
);
name = Classes;
sourceTree = "<group>";
};
16BFA66A14E195E9009704F8 /* handler */ = {
isa = PBXGroup;
children = (
16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */,
16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */,
);
path = handler;
sourceTree = "<group>";
};
16C7C965147D4A4200776EAD /* client */ = {
isa = PBXGroup;
children = (
16C7C966147D4A4200776EAD /* apple */,
16C7C969147D4A4200776EAD /* ios */,
16C7C99E147D4A4200776EAD /* mac */,
16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */,
16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */,
16C7CC06147D4A4300776EAD /* minidump_file_writer.h */,
16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */,
);
name = client;
path = ..;
sourceTree = SOURCE_ROOT;
};
16C7C966147D4A4200776EAD /* apple */ = {
isa = PBXGroup;
children = (
16C7C967147D4A4200776EAD /* Framework */,
);
path = apple;
sourceTree = "<group>";
};
16C7C967147D4A4200776EAD /* Framework */ = {
isa = PBXGroup;
children = (
16C7C968147D4A4200776EAD /* BreakpadDefines.h */,
);
path = Framework;
sourceTree = "<group>";
};
16C7C969147D4A4200776EAD /* ios */ = {
isa = PBXGroup;
children = (
16C92FAB150DF8330053D7BA /* BreakpadController.h */,
16C92FAC150DF8330053D7BA /* BreakpadController.mm */,
16BFA66A14E195E9009704F8 /* handler */,
16C7C96A147D4A4200776EAD /* Breakpad.h */,
16C7C96B147D4A4200776EAD /* Breakpad.mm */,
);
path = ios;
sourceTree = "<group>";
};
16C7C99E147D4A4200776EAD /* mac */ = {
isa = PBXGroup;
children = (
16C7CB9D147D4A4300776EAD /* crash_generation */,
16C7CBAA147D4A4300776EAD /* handler */,
16C7CBC8147D4A4300776EAD /* sender */,
);
path = mac;
sourceTree = "<group>";
};
16C7CB9D147D4A4300776EAD /* crash_generation */ = {
isa = PBXGroup;
children = (
16C7CB9E147D4A4300776EAD /* ConfigFile.h */,
16C7CB9F147D4A4300776EAD /* ConfigFile.mm */,
);
path = crash_generation;
sourceTree = "<group>";
};
16C7CBAA147D4A4300776EAD /* handler */ = {
isa = PBXGroup;
children = (
16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */,
16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */,
16C7CBAF147D4A4300776EAD /* dynamic_images.cc */,
16C7CBB0147D4A4300776EAD /* dynamic_images.h */,
16C7CBB1147D4A4300776EAD /* exception_handler.cc */,
16C7CBB2147D4A4300776EAD /* exception_handler.h */,
16C7CBB4147D4A4300776EAD /* minidump_generator.cc */,
16C7CBB5147D4A4300776EAD /* minidump_generator.h */,
16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */,
16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */,
);
path = handler;
sourceTree = "<group>";
};
16C7CBC8147D4A4300776EAD /* sender */ = {
isa = PBXGroup;
children = (
16C7CBEA147D4A4300776EAD /* uploader.h */,
16C7CBEB147D4A4300776EAD /* uploader.mm */,
);
path = sender;
sourceTree = "<group>";
};
16C7CC47147D4A4300776EAD /* common */ = {
isa = PBXGroup;
children = (
16C7CC4A147D4A4300776EAD /* convert_UTF.c */,
16C7CC4B147D4A4300776EAD /* convert_UTF.h */,
16C7CC82147D4A4300776EAD /* mac */,
16C7CCA4147D4A4300776EAD /* md5.cc */,
16C7CCA5147D4A4300776EAD /* md5.h */,
16C7CCB9147D4A4300776EAD /* string_conversion.cc */,
16C7CCBA147D4A4300776EAD /* string_conversion.h */,
);
name = common;
path = ../../common;
sourceTree = SOURCE_ROOT;
};
16C7CC82147D4A4300776EAD /* mac */ = {
isa = PBXGroup;
children = (
16C7CC88147D4A4300776EAD /* GTMLogger.h */,
16C7CC89147D4A4300776EAD /* GTMLogger.m */,
16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */,
16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */,
16C7CC8E147D4A4300776EAD /* SimpleStringDictionary.h */,
16C7CC8F147D4A4300776EAD /* SimpleStringDictionary.mm */,
16C7CC93147D4A4300776EAD /* file_id.cc */,
16C7CC94147D4A4300776EAD /* file_id.h */,
16C7CC95147D4A4300776EAD /* macho_id.cc */,
16C7CC96147D4A4300776EAD /* macho_id.h */,
16C7CC9A147D4A4300776EAD /* macho_utilities.cc */,
16C7CC9B147D4A4300776EAD /* macho_utilities.h */,
16C7CC9C147D4A4300776EAD /* macho_walker.cc */,
16C7CC9D147D4A4300776EAD /* macho_walker.h */,
16C7CC9F147D4A4300776EAD /* string_utilities.cc */,
16C7CCA0147D4A4300776EAD /* string_utilities.h */,
);
path = mac;
sourceTree = "<group>";
};
32C88DFF0371C24200C91783 /* Other Sources */ = {
isa = PBXGroup;
children = (
AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */,
);
name = "Other Sources";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
D2AAC07A0554694100DB518D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */,
16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */,
16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */,
16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */,
16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */,
16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */,
16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */,
16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */,
16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */,
16C7CE08147D4A4300776EAD /* uploader.h in Headers */,
16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */,
16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */,
16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */,
16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */,
16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */,
16C7CE7E147D4A4300776EAD /* SimpleStringDictionary.h in Headers */,
16C7CE84147D4A4300776EAD /* file_id.h in Headers */,
16C7CE86147D4A4300776EAD /* macho_id.h in Headers */,
16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */,
16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */,
16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */,
16C7CE94147D4A4300776EAD /* md5.h in Headers */,
16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */,
16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */,
16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
D2AAC07D0554694100DB518D /* Breakpad */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
buildPhases = (
D2AAC07A0554694100DB518D /* Headers */,
D2AAC07B0554694100DB518D /* Sources */,
D2AAC07C0554694100DB518D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Breakpad;
productName = Breakpad;
productReference = D2AAC07E0554694100DB518D /* libBreakpad.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
da,
de,
es,
fr,
it,
ja,
nl,
no,
sl,
sv,
tr,
);
mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */;
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
D2AAC07D0554694100DB518D /* Breakpad */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
D2AAC07B0554694100DB518D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */,
16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */,
16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */,
16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */,
16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */,
16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */,
16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */,
16C7CE09147D4A4300776EAD /* uploader.mm in Sources */,
16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */,
16C7CE1B147D4A4300776EAD /* minidump_file_writer_unittest.cc in Sources */,
16C7CE40147D4A4300776EAD /* convert_UTF.c in Sources */,
16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */,
16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */,
16C7CE7F147D4A4300776EAD /* SimpleStringDictionary.mm in Sources */,
16C7CE83147D4A4300776EAD /* file_id.cc in Sources */,
16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */,
16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */,
16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */,
16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */,
16C7CE93147D4A4300776EAD /* md5.cc in Sources */,
16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */,
16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */,
16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB921F08733DC00010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
COPY_PHASE_STRIP = NO;
DSTROOT = /tmp/Breakpad.dst;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../mac/build/Debug\"",
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
INSTALL_PATH = /usr/local/lib;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Debug\"",
"\"$(SRCROOT)/../mac/gcov\"",
);
PRODUCT_NAME = Breakpad;
};
name = Debug;
};
1DEB922008733DC00010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
DSTROOT = /tmp/Breakpad.dst;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../mac/build/Debug\"",
);
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Breakpad_Prefix.pch;
INSTALL_PATH = /usr/local/lib;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"",
"\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"",
"\"$(SRCROOT)/../mac/build/Debug\"",
"\"$(SRCROOT)/../mac/gcov\"",
);
PRODUCT_NAME = Breakpad;
};
name = Release;
};
1DEB922308733DC00010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
../../,
../../client/apple/Framework,
../../common/mac,
);
OTHER_LDFLAGS = "-ObjC";
SDKROOT = iphoneos;
};
name = Debug;
};
1DEB922408733DC00010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
../../,
../../client/apple/Framework,
../../common/mac,
);
OTHER_LDFLAGS = "-ObjC";
SDKROOT = iphoneos;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB921F08733DC00010E9CD /* Debug */,
1DEB922008733DC00010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB922308733DC00010E9CD /* Debug */,
1DEB922408733DC00010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
}

View File

@@ -1,106 +0,0 @@
// 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_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
#define CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_
#import <Foundation/Foundation.h>
#import "client/ios/Breakpad.h"
// This class is used to offer a higher level API around BreakpadRef. It
// configures it, ensures thread-safety, and sends crash reports back to the
// collecting server. By default, no crash reports are sent, the user must call
// |setUploadingEnabled:YES| to start the uploading.
@interface BreakpadController : NSObject {
@private
// The dispatch queue that will own the breakpad reference.
dispatch_queue_t queue_;
// Instance of Breakpad crash reporter. This is owned by the queue, but can
// be created on the main thread at startup.
BreakpadRef breakpadRef_;
// The dictionary that contains configuration for breakpad. Modifying it
// should only happen when the controller is not started. The initial value
// is the infoDictionary of the bundle of the application.
NSMutableDictionary* configuration_;
// Whether or not crash reports should be uploaded.
BOOL enableUploads_;
// The interval to wait between two uploads. Value is 0 if no upload must be
// done.
int uploadIntervalInSeconds_;
}
// Singleton.
+ (BreakpadController*)sharedInstance;
// Update the controller configuration. Merges its old configuration with the
// new one. Merge is done by replacing the old values by the new values.
- (void)updateConfiguration:(NSDictionary*)configuration;
// Configure the URL to upload the report to. This must be called at least once
// if the URL is not in the bundle information.
- (void)setUploadingURL:(NSString*)url;
// Set the minimal interval between two uploads in seconds. This must be called
// at least once if the interval is not in the bundle information. A value of 0
// will prevent uploads.
- (void)setUploadInterval:(int)intervalInSeconds;
// Specify a parameter that will be uploaded to the crash server. See
// |BreakpadAddUploadParameter|.
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key;
// Remove a previously-added parameter from the upload parameter set. See
// |BreakpadRemoveUploadParameter|.
- (void)removeUploadParameterForKey:(NSString*)key;
// Access the underlying BreakpadRef. This method is asynchronous, and will be
// executed on the thread owning the BreakpadRef variable. Moreover, if the
// controller is not started, the block will be called with a NULL parameter.
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback;
// Starts the BreakpadController by registering crash handlers. If
// |onCurrentThread| is YES, all setup is done on the current thread, otherwise
// it is done on a private queue.
- (void)start:(BOOL)onCurrentThread;
// Unregisters the crash handlers.
- (void)stop;
// Enables or disables uploading of crash reports, but does not stop the
// BreakpadController.
- (void)setUploadingEnabled:(BOOL)enabled;
@end
#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_

View File

@@ -1,266 +0,0 @@
// 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.
#import "BreakpadController.h"
#import <UIKit/UIKit.h>
#include <asl.h>
#include <execinfo.h>
#include <signal.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <processor/scoped_ptr.h>
#pragma mark -
#pragma mark Private Methods
@interface BreakpadController ()
// Init the singleton instance.
- (id)initSingleton;
// Load a crash report and send it to the server.
- (void)sendStoredCrashReports;
// Returns when a report can be sent. |-1| means never, |0| means that a report
// can be sent immediately, a positive number is the number of seconds to wait
// before being allowed to upload a report.
- (int)sendDelay;
// Notifies that a report will be sent, and update the last sending time
// accordingly.
- (void)reportWillBeSent;
@end
#pragma mark -
#pragma mark Anonymous namespace
namespace {
// The name of the user defaults key for the last submission to the crash
// server.
NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission";
// Returns a NSString describing the current platform.
NSString* GetPlatform() {
// Name of the system call for getting the platform.
static const char kHwMachineSysctlName[] = "hw.machine";
NSString* result = nil;
size_t size = 0;
if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0)
return nil;
google_breakpad::scoped_array<char> machine(new char[size]);
if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0)
result = [NSString stringWithUTF8String:machine.get()];
return result;
}
} // namespace
#pragma mark -
#pragma mark BreakpadController Implementation
@implementation BreakpadController
+ (BreakpadController*)sharedInstance {
@synchronized(self) {
static BreakpadController* sharedInstance_ =
[[BreakpadController alloc] initSingleton];
return sharedInstance_;
}
}
- (id)init {
return nil;
}
- (id)initSingleton {
self = [super init];
if (self) {
queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL);
configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy];
enableUploads_ = NO;
NSString* uploadInterval =
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
[self setUploadInterval:[uploadInterval intValue]];
}
return self;
}
// Since this class is a singleton, this method is not expected to be called.
- (void)dealloc {
assert(!breakpadRef_);
dispatch_release(queue_);
[configuration_ release];
[super dealloc];
}
#pragma mark -
- (void)start:(BOOL)onCurrentThread {
void(^startBlock)() = ^{
assert(!breakpadRef_);
breakpadRef_ = BreakpadCreate(configuration_);
if (breakpadRef_) {
BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform());
}
};
if (onCurrentThread)
startBlock();
else
dispatch_async(queue_, startBlock);
}
- (void)stop {
dispatch_sync(queue_, ^{
if (breakpadRef_) {
BreakpadRelease(breakpadRef_);
breakpadRef_ = NULL;
}
});
}
- (void)setUploadingEnabled:(BOOL)enabled {
dispatch_async(queue_, ^{
if (enabled == enableUploads_)
return;
if (enabled) {
// Set this before calling doSendStoredCrashReport, because that
// calls sendDelay, which in turn checks this flag.
enableUploads_ = YES;
[self sendStoredCrashReports];
} else {
enableUploads_ = NO;
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:@selector(sendStoredCrashReports)
object:nil];
}
});
}
- (void)updateConfiguration:(NSDictionary*)configuration {
[configuration_ addEntriesFromDictionary:configuration];
NSString* uploadInterval =
[configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL];
if (uploadInterval)
[self setUploadInterval:[uploadInterval intValue]];
}
- (void)setUploadingURL:(NSString*)url {
[configuration_ setValue:url forKey:@BREAKPAD_URL];
}
- (void)setUploadInterval:(int)intervalInSeconds {
[configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL];
uploadIntervalInSeconds_ = intervalInSeconds;
if (uploadIntervalInSeconds_ < 0)
uploadIntervalInSeconds_ = 0;
}
- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key {
dispatch_async(queue_, ^{
if (breakpadRef_)
BreakpadAddUploadParameter(breakpadRef_, key, value);
});
}
- (void)removeUploadParameterForKey:(NSString*)key {
dispatch_async(queue_, ^{
if (breakpadRef_)
BreakpadRemoveUploadParameter(breakpadRef_, key);
});
}
- (void)withBreakpadRef:(void(^)(BreakpadRef))callback {
dispatch_async(queue_, ^{
callback(breakpadRef_);
});
}
#pragma mark -
- (int)sendDelay {
if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_)
return -1;
// To prevent overloading the crash server, crashes are not sent than one
// report every |uploadIntervalInSeconds_|. A value in the user defaults is
// used to keep the time of the last upload.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission];
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime;
if (spanSeconds >= uploadIntervalInSeconds_)
return 0;
return uploadIntervalInSeconds_ - spanSeconds;
}
- (void)reportWillBeSent {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()]
forKey:kLastSubmission];
[userDefaults synchronize];
}
- (void)sendStoredCrashReports {
dispatch_async(queue_, ^{
if (!BreakpadHasCrashReportToUpload(breakpadRef_))
return;
int timeToWait = [self sendDelay];
// Unable to ever send report.
if (timeToWait == -1)
return;
// A report can be sent now.
if (timeToWait == 0) {
[self reportWillBeSent];
BreakpadUploadNextReport(breakpadRef_);
// If more reports must be sent, make sure this method is called again.
if (BreakpadHasCrashReportToUpload(breakpadRef_))
timeToWait = uploadIntervalInSeconds_;
}
// A report must be sent later.
if (timeToWait > 0)
[self performSelector:@selector(sendStoredCrashReports)
withObject:nil
afterDelay:timeToWait];
});
}
@end

View File

@@ -1,7 +0,0 @@
//
// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project.
//
#ifdef __OBJC__
#import <Foundation/Foundation.h>
#endif

View File

@@ -1,64 +0,0 @@
// 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.
// ios_exception_minidump_generator.h: Create a fake minidump from a
// NSException.
#ifndef CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
#define CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_
#include <Foundation/Foundation.h>
#include "client/mac/handler/minidump_generator.h"
namespace google_breakpad {
class IosExceptionMinidumpGenerator : public MinidumpGenerator {
public:
explicit IosExceptionMinidumpGenerator(NSException *exception);
virtual ~IosExceptionMinidumpGenerator();
protected:
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
private:
// Get the crashing program counter from the exception.
uint32_t GetPCFromException();
// Write a virtual thread context for the crashing site.
bool WriteCrashingContext(MDLocationDescriptor *register_location);
NSArray *return_addresses_;
};
} // namespace google_breakpad
#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_

View File

@@ -1,168 +0,0 @@
// 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/ios/handler/ios_exception_minidump_generator.h"
#include "google_breakpad/common/minidump_exception_mac.h"
#include "client/minidump_file_writer-inl.h"
#include "processor/scoped_ptr.h"
namespace {
const uint32_t kExpectedFinalFp = 4;
const uint32_t kExpectedFinalSp = 0;
const int kExceptionType = EXC_SOFTWARE;
const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION;
#ifdef HAS_ARM_SUPPORT
// Append the given 4 bytes value to the sp position of the stack represented
// by memory.
void AppendToMemory(uint8_t *memory, uint32_t sp, uint32_t data) {
assert(sizeof(data) == 4);
memcpy(memory + sp, &data, sizeof(data));
}
#endif
} // namespace
namespace google_breakpad {
IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator(
NSException *exception)
: MinidumpGenerator(mach_task_self(), 0) {
return_addresses_ = [[exception callStackReturnAddresses] retain];
SetExceptionInformation(kExceptionType,
kExceptionCode,
0,
pthread_mach_thread_np(pthread_self()));
}
IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() {
[return_addresses_ release];
}
bool IosExceptionMinidumpGenerator::WriteCrashingContext(
MDLocationDescriptor *register_location) {
#ifdef HAS_ARM_SUPPORT
TypedMDRVA<MDRawContextARM> context(&writer_);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextARM *context_ptr = context.get();
memset(context_ptr, 0, sizeof(MDRawContextARM));
context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
context_ptr->iregs[7] = kExpectedFinalFp; // FP
context_ptr->iregs[13] = kExpectedFinalSp; // SP
uint32_t pc = GetPCFromException();
context_ptr->iregs[14] = pc; // LR
context_ptr->iregs[15] = pc; // PC
return true;
#else
assert(false);
return false;
#endif
}
uint32_t IosExceptionMinidumpGenerator::GetPCFromException() {
return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue];
}
bool IosExceptionMinidumpGenerator::WriteExceptionStream(
MDRawDirectory *exception_stream) {
#ifdef HAS_ARM_SUPPORT
TypedMDRVA<MDRawExceptionStream> exception(&writer_);
if (!exception.Allocate())
return false;
exception_stream->stream_type = MD_EXCEPTION_STREAM;
exception_stream->location = exception.location();
MDRawExceptionStream *exception_ptr = exception.get();
exception_ptr->thread_id = pthread_mach_thread_np(pthread_self());
// This naming is confusing, but it is the proper translation from
// mach naming to minidump naming.
exception_ptr->exception_record.exception_code = kExceptionType;
exception_ptr->exception_record.exception_flags = kExceptionCode;
if (!WriteCrashingContext(&exception_ptr->thread_context))
return false;
exception_ptr->exception_record.exception_address = GetPCFromException();
return true;
#else
return MinidumpGenerator::WriteExceptionStream(exception_stream);
#endif
}
bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
MDRawThread *thread) {
#ifdef HAS_ARM_SUPPORT
if (pthread_mach_thread_np(pthread_self()) != thread_id)
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
size_t frame_count = [return_addresses_ count];
UntypedMDRVA memory(&writer_);
size_t size = 8 * (frame_count - 1) + 4;
if (!memory.Allocate(size))
return false;
scoped_array<uint8_t> stack_memory(new uint8_t[size]);
uint32_t sp = size - 4;
uint32_t fp = 0;
uint32_t lr = [[return_addresses_ lastObject] unsignedIntegerValue];
for (int current_frame = frame_count - 2;
current_frame >= 0;
--current_frame) {
AppendToMemory(stack_memory.get(), sp, fp);
sp -= 4;
fp = sp;
AppendToMemory(stack_memory.get(), sp, lr);
sp -= 4;
lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue];
}
if (!memory.Copy(stack_memory.get(), size))
return false;
assert(sp == kExpectedFinalSp);
assert(fp == kExpectedFinalFp);
assert(lr == GetPCFromException());
thread->stack.start_of_memory_range = sp;
thread->stack.memory = memory.location();
memory_blocks_.push_back(thread->stack);
if (!WriteCrashingContext(&thread->thread_context))
return false;
thread->thread_id = thread_id;
return true;
#else
return MinidumpGenerator::WriteThreadStream(thread_id, thread);
#endif
}
} // namespace google_breakpad

View File

@@ -1,44 +0,0 @@
// Copyright (c) 2010 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_CRASH_GENERATION_CLIENT_INFO_H_
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
namespace google_breakpad {
class CrashGenerationServer;
struct ClientInfo {
CrashGenerationServer* crash_server_;
pid_t pid_;
};
}
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_

View File

@@ -1,92 +0,0 @@
// Copyright (c) 2010 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 <sys/socket.h>
#include <sys/types.h>
#include <algorithm>
#include "client/linux/crash_generation/crash_generation_client.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
bool
CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
{
int fds[2];
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
struct kernel_msghdr msg;
my_memset(&msg, 0, sizeof(struct kernel_msghdr));
struct kernel_iovec iov[1];
iov[0].iov_base = const_cast<void*>(blob);
iov[0].iov_len = blob_size;
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
char cmsg[kControlMsgSize];
my_memset(cmsg, 0, kControlMsgSize);
msg.msg_control = cmsg;
msg.msg_controllen = sizeof(cmsg);
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
hdr->cmsg_len = CMSG_LEN(sizeof(int));
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
*p = fds[1];
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
sys_close(fds[1]);
if (ret <= 0)
return false;
// wait for an ACK from the server
char b;
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
return true;
}
//static
CrashGenerationClient*
CrashGenerationClient::TryCreate(int server_fd)
{
if (0 > server_fd)
return NULL;
return new CrashGenerationClient(server_fd);
}
}

View File

@@ -1,69 +0,0 @@
// Copyright (c) 2010 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_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include <stddef.h>
namespace google_breakpad {
class CrashGenerationClient {
public:
~CrashGenerationClient()
{
}
// Request the crash server to generate a dump. |blob| is a hack,
// see exception_handler.h and minidump_writer.h
//
// Return true if the dump was successful; false otherwise.
bool RequestDump(const void* blob, size_t blob_size);
// Return a new CrashGenerationClient if |server_fd| is valid and
// connects to a CrashGenerationServer. Otherwise, return NULL.
// The returned CrashGenerationClient* is owned by the caller of
// this function.
static CrashGenerationClient* TryCreate(int server_fd);
private:
CrashGenerationClient(int server_fd) : server_fd_(server_fd)
{
}
int server_fd_;
// prevent copy construction and assignment
CrashGenerationClient(const CrashGenerationClient&);
CrashGenerationClient& operator=(const CrashGenerationClient&);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@@ -1,467 +0,0 @@
// Copyright (c) 2010 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 <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "client/linux/crash_generation/crash_generation_server.h"
#include "client/linux/crash_generation/client_info.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include "common/linux/safe_readlink.h"
static const char kCommandQuit = 'x';
static bool
GetInodeForFileDescriptor(ino_t* inode_out, int fd)
{
assert(inode_out);
struct stat buf;
if (fstat(fd, &buf) < 0)
return false;
if (!S_ISSOCK(buf.st_mode))
return false;
*inode_out = buf.st_ino;
return true;
}
// expected prefix of the target of the /proc/self/fd/%d link for a socket
static const char kSocketLinkPrefix[] = "socket:[";
// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
// socket.
// inode_out: (output) set to the inode number on success
// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
static bool
GetInodeForProcPath(ino_t* inode_out, const char* path)
{
assert(inode_out);
assert(path);
char buf[PATH_MAX];
if (!SafeReadLink(path, buf)) {
return false;
}
if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
return false;
}
char* endptr;
const u_int64_t inode_ul =
strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
if (*endptr != ']')
return false;
if (inode_ul == ULLONG_MAX) {
return false;
}
*inode_out = inode_ul;
return true;
}
static bool
FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode)
{
assert(pid_out);
bool already_found = false;
DIR* proc = opendir("/proc");
if (!proc) {
return false;
}
std::vector<pid_t> pids;
struct dirent* dent;
while ((dent = readdir(proc))) {
char* endptr;
const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
if (pid_ul == ULONG_MAX || '\0' != *endptr)
continue;
pids.push_back(pid_ul);
}
closedir(proc);
for (std::vector<pid_t>::const_iterator
i = pids.begin(); i != pids.end(); ++i) {
const pid_t current_pid = *i;
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
DIR* fd = opendir(buf);
if (!fd)
continue;
while ((dent = readdir(fd))) {
if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
dent->d_name) >= static_cast<int>(sizeof(buf))) {
continue;
}
ino_t fd_inode;
if (GetInodeForProcPath(&fd_inode, buf)
&& fd_inode == socket_inode) {
if (already_found) {
closedir(fd);
return false;
}
already_found = true;
*pid_out = current_pid;
break;
}
}
closedir(fd);
}
return already_found;
}
namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path) :
server_fd_(listen_fd),
dump_callback_(dump_callback),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
generate_dumps_(generate_dumps),
started_(false)
{
if (dump_path)
dump_dir_ = *dump_path;
else
dump_dir_ = "/tmp";
}
CrashGenerationServer::~CrashGenerationServer()
{
if (started_)
Stop();
}
bool
CrashGenerationServer::Start()
{
if (started_ || 0 > server_fd_)
return false;
int control_pipe[2];
if (pipe(control_pipe))
return false;
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
return false;
control_pipe_in_ = control_pipe[0];
control_pipe_out_ = control_pipe[1];
if (pthread_create(&thread_, NULL,
ThreadMain, reinterpret_cast<void*>(this)))
return false;
started_ = true;
return true;
}
void
CrashGenerationServer::Stop()
{
assert(pthread_self() != thread_);
if (!started_)
return;
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
void* dummy;
pthread_join(thread_, &dummy);
started_ = false;
}
//static
bool
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
{
int fds[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
return false;
static const int on = 1;
// Enable passcred on the server end of the socket
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
return false;
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
return false;
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
return false;
*client_fd = fds[0];
*server_fd = fds[1];
return true;
}
// The following methods/functions execute on the server thread
void
CrashGenerationServer::Run()
{
struct pollfd pollfds[2];
memset(&pollfds, 0, sizeof(pollfds));
pollfds[0].fd = server_fd_;
pollfds[0].events = POLLIN;
pollfds[1].fd = control_pipe_in_;
pollfds[1].events = POLLIN;
while (true) {
// infinite timeout
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
if (-1 == nevents) {
if (EINTR == errno) {
continue;
} else {
return;
}
}
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
return;
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
return;
}
}
bool
CrashGenerationServer::ClientEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
// A process has crashed and has signaled us by writing a datagram
// to the death signal socket. The datagram contains the crash context needed
// for writing the minidump as well as a file descriptor and a credentials
// block so that they can't lie about their pid.
// The length of the control message:
static const unsigned kControlMsgSize =
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
// The length of the regular payload:
static const unsigned kCrashContextSize =
sizeof(google_breakpad::ExceptionHandler::CrashContext);
struct msghdr msg = {0};
struct iovec iov[1];
char crash_context[kCrashContextSize];
char control[kControlMsgSize];
const ssize_t expected_msg_size = sizeof(crash_context);
iov[0].iov_base = crash_context;
iov[0].iov_len = sizeof(crash_context);
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
msg.msg_control = control;
msg.msg_controllen = kControlMsgSize;
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
if (msg_size != expected_msg_size)
return true;
if (msg.msg_controllen != kControlMsgSize ||
msg.msg_flags & ~MSG_TRUNC)
return true;
// Walk the control payload and extract the file descriptor and validated pid.
pid_t crashing_pid = -1;
int signal_fd = -1;
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
hdr = CMSG_NXTHDR(&msg, hdr)) {
if (hdr->cmsg_level != SOL_SOCKET)
continue;
if (hdr->cmsg_type == SCM_RIGHTS) {
const unsigned len = hdr->cmsg_len -
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
assert(len % sizeof(int) == 0u);
const unsigned num_fds = len / sizeof(int);
if (num_fds > 1 || num_fds == 0) {
// A nasty process could try and send us too many descriptors and
// force a leak.
for (unsigned i = 0; i < num_fds; ++i)
HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
return true;
} else {
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
}
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
const struct ucred *cred =
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
crashing_pid = cred->pid;
}
}
if (crashing_pid == -1 || signal_fd == -1) {
if (signal_fd)
HANDLE_EINTR(close(signal_fd));
return true;
}
// Kernel bug workaround (broken in 2.6.30 at least):
// The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
// namespaces. Thus |crashing_pid| might be garbage from our point of view.
// In the future we can remove this workaround, but we have to wait a couple
// of years to be sure that it's worked its way out into the world.
ino_t inode_number;
if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
string minidump_filename;
if (!MakeMinidumpFilename(minidump_filename))
return true;
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
crashing_pid, crash_context,
kCrashContextSize)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
if (dump_callback_) {
ClientInfo info;
info.crash_server_ = this;
info.pid_ = crashing_pid;
dump_callback_(dump_context_, &info, &minidump_filename);
}
// Send the done signal to the process: it can exit now.
memset(&msg, 0, sizeof(msg));
struct iovec done_iov;
done_iov.iov_base = const_cast<char*>("\x42");
done_iov.iov_len = 1;
msg.msg_iov = &done_iov;
msg.msg_iovlen = 1;
HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
HANDLE_EINTR(close(signal_fd));
return true;
}
bool
CrashGenerationServer::ControlEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
char command;
if (read(control_pipe_in_, &command, 1))
return false;
switch (command) {
case kCommandQuit:
return false;
default:
assert(0);
}
return true;
}
bool
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
{
GUID guid;
char guidString[kGUIDStringLength+1];
if (!(CreateGUID(&guid)
&& GUIDToString(&guid, guidString, sizeof(guidString))))
return false;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
outFilename = path;
return true;
}
// static
void*
CrashGenerationServer::ThreadMain(void *arg)
{
reinterpret_cast<CrashGenerationServer*>(arg)->Run();
return NULL;
}
} // namespace google_breakpad

View File

@@ -1,135 +0,0 @@
// Copyright (c) 2010 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_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#include <pthread.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
class ClientInfo;
class CrashGenerationServer {
public:
// WARNING: callbacks may be invoked on a different thread
// than that which creates the CrashGenerationServer. They must
// be thread safe.
typedef void (*OnClientDumpRequestCallback)(void* context,
const ClientInfo* client_info,
const string* file_path);
typedef void (*OnClientExitingCallback)(void* context,
const ClientInfo* client_info);
// Create an instance with the given parameters.
//
// Parameter listen_fd: The server fd created by CreateReportChannel().
// Parameter dump_callback: Callback for a client crash dump request.
// Parameter dump_context: Context for client crash dump request callback.
// Parameter exit_callback: Callback for client process exit.
// Parameter exit_context: Context for client exit callback.
// Parameter generate_dumps: Whether to automatically generate dumps.
// Client code of this class might want to generate dumps explicitly
// in the crash dump request callback. In that case, false can be
// passed for this parameter.
// Parameter dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path);
~CrashGenerationServer();
// Perform initialization steps needed to start listening to clients.
//
// Return true if initialization is successful; false otherwise.
bool Start();
// Stop the server.
void Stop();
// Create a "channel" that can be used by clients to report crashes
// to a CrashGenerationServer. |*server_fd| should be passed to
// this class's constructor, and |*client_fd| should be passed to
// the ExceptionHandler constructor in the client process.
static bool CreateReportChannel(int* server_fd, int* client_fd);
private:
// Run the server's event loop
void Run();
// Invoked when an child process (client) event occurs
// Returning true => "keep running", false => "exit loop"
bool ClientEvent(short revents);
// Invoked when the controlling thread (main) event occurs
// Returning true => "keep running", false => "exit loop"
bool ControlEvent(short revents);
// Return a unique filename at which a minidump can be written
bool MakeMinidumpFilename(string& outFilename);
// Trampoline to |Run()|
static void* ThreadMain(void* arg);
int server_fd_;
OnClientDumpRequestCallback dump_callback_;
void* dump_context_;
OnClientExitingCallback exit_callback_;
void* exit_context_;
bool generate_dumps_;
string dump_dir_;
bool started_;
pthread_t thread_;
int control_pipe_in_;
int control_pipe_out_;
// disable these
CrashGenerationServer(const CrashGenerationServer&);
CrashGenerationServer& operator=(const CrashGenerationServer&);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_

View File

@@ -0,0 +1,45 @@
CXX=g++
CC=gcc
CXXFLAGS=-gstabs+ -I../../../ -I../../../testing/gtest/include -I../../../testing/include -I../../../testing/gtest -D_REENTRANT -m32
CFLAGS=$(CXXFLAGS)
LDFLAGS=-lpthread
OBJ_DIR=.
BIN_DIR=.
TEST_CC_SRC=exception_handler_unittest.cc \
exception_handler.cc \
../../../testing/gtest/src/gtest-all.cc \
../../../common/linux/guid_creator.cc \
../minidump_writer/minidump_writer.cc \
../../minidump_file_writer.cc \
../minidump_writer/linux_dumper.cc \
../../../testing/gtest/src/gtest_main.cc \
../../../common/string_conversion.cc \
../minidump_writer/directory_reader_unittest.cc \
../minidump_writer/line_reader_unittest.cc \
../minidump_writer/linux_dumper_unittest.cc \
../minidump_writer/minidump_writer_unittest.cc
TEST_C_SRC = ../../../common/convert_UTF.c
TEST_CC_OBJ=$(patsubst %.cc, $(OBJ_DIR)/%.o,$(TEST_CC_SRC))
TEST_C_OBJ=$(patsubst %.c, $(OBJ_DIR)/%.o, $(TEST_C_SRC))
LINUX_CLIENT_BIN=$(BIN_DIR)/linux_client_test
BIN=$(LINUX_CLIENT_BIN)
.PHONY:all clean
check:$(BIN)
$(LINUX_CLIENT_BIN)
all:$(BIN)
$(BIN_DIR)/linux_client_test:$(TEST_CC_OBJ) $(TEST_C_OBJ)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@
clean:
rm -f $(BIN) $(TEST_CC_OBJ) $(TEST_C_OBJ)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010 Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -72,38 +72,22 @@
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/signal.h>
#include <sys/syscall.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <ucontext.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "common/linux/linux_libc_support.h"
#include "common/memory.h"
#include "client/linux/log/log.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "third_party/lss/linux_syscall_support.h"
#include "linux/sched.h"
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
#include "common/linux/linux_syscall_support.h"
#include "common/linux/memory.h"
#include "client/linux/minidump_writer//minidump_writer.h"
#include "common/linux/guid_creator.h"
// A wrapper for the tgkill syscall: send a signal to a specific thread.
static int tgkill(pid_t tgid, pid_t tid, int sig) {
return syscall(__NR_tgkill, tgid, tid, sig);
return 0;
syscall(__NR_tgkill, tgid, tid, sig);
}
namespace google_breakpad {
@@ -123,31 +107,28 @@ pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
PTHREAD_MUTEX_INITIALIZER;
// Runs before crashing: normal context.
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd)
void *callback_context,
bool install_handler)
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
minidump_descriptor_(descriptor),
dump_path_(),
handler_installed_(install_handler),
crash_handler_(NULL) {
if (server_fd >= 0)
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
set_dump_path(dump_path);
if (install_handler)
if (install_handler) {
InstallHandlers();
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD())
minidump_descriptor_.UpdatePath();
pthread_mutex_lock(&handler_stack_mutex_);
if (!handler_stack_)
handler_stack_ = new std::vector<ExceptionHandler*>;
handler_stack_->push_back(this);
pthread_mutex_unlock(&handler_stack_mutex_);
pthread_mutex_lock(&handler_stack_mutex_);
if (handler_stack_ == NULL)
handler_stack_ = new std::vector<ExceptionHandler *>;
handler_stack_->push_back(this);
pthread_mutex_unlock(&handler_stack_mutex_);
}
}
// Runs before crashing: normal context.
@@ -164,18 +145,14 @@ bool ExceptionHandler::InstallHandlers() {
// such a small stack.
static const unsigned kSigStackSize = 8192;
signal_stack = malloc(kSigStackSize);
stack_t stack;
// Only set an alternative stack if there isn't already one, or if the current
// one is too small.
if (sys_sigaltstack(NULL, &stack) == -1 || !stack.ss_sp ||
stack.ss_size < kSigStackSize) {
memset(&stack, 0, sizeof(stack));
stack.ss_sp = malloc(kSigStackSize);
stack.ss_size = kSigStackSize;
memset(&stack, 0, sizeof(stack));
stack.ss_sp = signal_stack;
stack.ss_size = kSigStackSize;
if (sys_sigaltstack(&stack, NULL) == -1)
return false;
}
if (sigaltstack(&stack, NULL) == -1)
return false;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
@@ -194,7 +171,6 @@ bool ExceptionHandler::InstallHandlers() {
return false;
old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
}
return true;
}
// Runs before crashing: normal context.
@@ -205,23 +181,34 @@ void ExceptionHandler::UninstallHandlers() {
sigaction(old_handlers_[i].first, action, NULL);
delete action;
}
pthread_mutex_lock(&handler_stack_mutex_);
std::vector<ExceptionHandler*>::iterator handler =
std::find(handler_stack_->begin(), handler_stack_->end(), this);
handler_stack_->erase(handler);
pthread_mutex_unlock(&handler_stack_mutex_);
old_handlers_.clear();
}
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
// crash_handler_ = callback;
// }
// Runs before crashing: normal context.
void ExceptionHandler::UpdateNextID() {
GUID guid;
char guid_str[kGUIDStringLength + 1];
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
next_minidump_id_ = guid_str;
next_minidump_id_c_ = next_minidump_id_.c_str();
char minidump_path[PATH_MAX];
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
dump_path_c_,
guid_str);
next_minidump_path_ = minidump_path;
next_minidump_path_c_ = next_minidump_path_.c_str();
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// All the exception signals are blocked at this point.
pthread_mutex_lock(&handler_stack_mutex_);
if (!handler_stack_->size()) {
@@ -240,33 +227,17 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
pthread_mutex_unlock(&handler_stack_mutex_);
if (info->si_pid) {
// This signal was triggered by somebody sending us the signal with kill().
// In order to retrigger it, we have to queue a new signal by calling
// kill() ourselves.
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
// If we failed to kill ourselves (e.g. because a sandbox disallows us
// to do so), we instead resort to terminating our process. This will
// result in an incorrect exit code.
_exit(1);
}
} else {
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
// No need to reissue the signal. It will automatically trigger again,
// when we return from the signal handler.
}
// As soon as we return from the signal handler, our signal will become
// unmasked. At that time, we will get terminated with the same signal that
// was triggered originally. This allows our parent to know that we crashed.
// The default action for all the signals which we catch is Core, so
// Terminate ourselves with the same signal so that our parent knows 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);
tgkill(getpid(), sys_gettid(), sig);
// not reached.
}
struct ThreadArgument {
pid_t pid; // the crashing process
const MinidumpDescriptor* minidump_descriptor;
ExceptionHandler* handler;
const void* context; // a CrashContext structure
size_t context_size;
@@ -277,11 +248,6 @@ struct ThreadArgument {
// static
int ExceptionHandler::ThreadEntry(void *arg) {
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
// Block here until the crashing process unblocks us when
// we're allowed to use ptrace
thread_arg->handler->WaitForContinueSignal();
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
thread_arg->context_size) == false;
}
@@ -292,54 +258,19 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
if (filter_ && !filter_(callback_context_))
return false;
// Allow ourselves to be dumped if the signal is trusted.
bool signal_trusted = info->si_code > 0;
bool signal_pid_trusted = info->si_code == SI_USER ||
info->si_code == SI_TKILL;
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
sys_prctl(PR_SET_DUMPABLE, 1);
}
// Allow ourselves to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));
#if !defined(__ARM_EABI__)
// FP state is not part of user ABI on ARM Linux.
struct ucontext *uc_ptr = (struct ucontext*)uc;
if (uc_ptr->uc_mcontext.fpregs) {
memcpy(&context.float_state,
uc_ptr->uc_mcontext.fpregs,
sizeof(context.float_state));
}
#endif
context.tid = syscall(__NR_gettid);
if (crash_handler_ != NULL) {
if (crash_handler_(&context, sizeof(context),
callback_context_)) {
return true;
}
}
return GenerateDump(&context);
}
memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
sizeof(context.float_state));
context.tid = sys_gettid();
// 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) {
#ifdef __ANDROID__
// Android doesn't provide getcontext().
return false;
#else
siginfo_t siginfo;
my_memset(&siginfo, 0, sizeof(siginfo_t));
struct ucontext context;
getcontext(&context);
return HandleSignal(sig, &siginfo, &context);
#endif
}
// This function may run in a compromised context: see the top of the file.
bool ExceptionHandler::GenerateDump(CrashContext *context) {
if (IsOutOfProcess())
return crash_generation_client_->RequestDump(context, sizeof(*context));
if (crash_handler_ && crash_handler_(&context, sizeof(context),
callback_context_))
return true;
static const unsigned kChildStackSize = 8000;
PageAllocator allocator;
@@ -352,158 +283,40 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
ThreadArgument thread_arg;
thread_arg.handler = this;
thread_arg.minidump_descriptor = &minidump_descriptor_;
thread_arg.pid = getpid();
thread_arg.context = context;
thread_arg.context_size = sizeof(*context);
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// can do this. Create a pipe here which we can use to block the
// cloned process after creating it, until we have explicitly enabled ptrace
if(sys_pipe(fdes) == -1) {
// Creating the pipe failed. We'll log an error but carry on anyway,
// as we'll probably still get a useful crash report. All that will happen
// is the write() and read() calls will fail with EBADF
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
sys_pipe failed:";
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
thread_arg.context = &context;
thread_arg.context_size = sizeof(context);
const pid_t child = sys_clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg, NULL, NULL, NULL);
int r, status;
// Allow the child to ptrace us
sys_prctl(PR_SET_PTRACER, child);
SendContinueSignalToChild();
do {
r = sys_waitpid(child, &status, __WALL);
} while (r == -1 && errno == EINTR);
sys_close(fdes[0]);
sys_close(fdes[1]);
if (r == -1) {
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:";
sys_write(2, msg, sizeof(msg) - 1);
sys_write(2, strerror(errno), strlen(strerror(errno)));
sys_write(2, "\n", 1);
}
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
if (callback_)
success = callback_(minidump_descriptor_, callback_context_, success);
success = callback_(dump_path_c_, next_minidump_id_c_,
callback_context_, success);
return success;
}
// This function runs in a compromised context: see the top of the file.
void ExceptionHandler::SendContinueSignalToChild() {
static const char okToContinueMessage = 'a';
int r;
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
if(r == -1) {
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
sys_write failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
void ExceptionHandler::WaitForContinueSignal() {
int r;
char receivedMessage;
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
if(r == -1) {
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
sys_read failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
size_t context_size) {
if (minidump_descriptor_.IsFD()) {
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
crashing_process,
context,
context_size,
mapping_list_);
}
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
crashing_process,
context,
context_size,
mapping_list_);
}
// static
bool ExceptionHandler::WriteMinidump(const string& dump_path,
MinidumpCallback callback,
void* callback_context) {
MinidumpDescriptor descriptor(dump_path);
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
return eh.WriteMinidump();
}
bool ExceptionHandler::WriteMinidump() {
#if !defined(__ARM_EABI__) && !defined(__ANDROID__)
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
// Update the path of the minidump so that this can be called multiple times
// and new files are created for each minidump. This is done before the
// generation happens, as clients may want to access the MinidumpDescriptor
// after this call to find the exact path to the minidump file.
minidump_descriptor_.UpdatePath();
} else if (minidump_descriptor_.IsFD()) {
// Reposition the FD to its beginning and resize it to get rid of the
// previous minidump info.
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
ftruncate(minidump_descriptor_.fd(), 0);
}
// Allow ourselves to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
int getcontext_result = getcontext(&context.context);
if (getcontext_result)
return false;
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
sizeof(context.float_state));
context.tid = sys_gettid();
return GenerateDump(&context);
#else
return false;
#endif // !defined(__ARM_EABI__) && !defined(__ANDROID__)
}
void ExceptionHandler::AddMappingInfo(const string& name,
const u_int8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset) {
MappingInfo info;
info.start_addr = start_address;
info.size = mapping_size;
info.offset = file_offset;
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
info.name[sizeof(info.name) - 1] = '\0';
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, identifier, sizeof(MDGUID));
mapping_list_.push_back(mapping);
return google_breakpad::WriteMinidump(
next_minidump_path_c_, crashing_process, context, context_size);
}
} // namespace google_breakpad

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010 Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -30,23 +30,10 @@
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#include <string>
#include <vector>
#include <string>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/ucontext.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 "common/using_std_string.h"
#include "google_breakpad/common/minidump_format.h"
#include "processor/scoped_ptr.h"
struct sigaction;
namespace google_breakpad {
@@ -68,18 +55,17 @@ namespace google_breakpad {
// use different minidump callbacks for different call sites.
//
// In either case, a callback function is called when a minidump is written,
// which receives the full path or file descriptor of the minidump. The
// caller can collect and write additional application state to that minidump,
// and launch an external crash-reporting application.
// which receives the unqiue id of the minidump. The caller can use this
// id to collect and write additional application state, and to launch an
// external crash-reporting application.
//
// Caller should try to make the callbacks as crash-friendly as possible,
// it should avoid use heap memory allocation as much as possible.
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
// processing of an exception. A FilterCallback is called before writing
// a minidump. |context| is the parameter supplied by the user as
// a minidump. context is the parameter supplied by the user as
// callback_context when the handler was created.
//
// If a FilterCallback returns true, Breakpad will continue processing,
@@ -89,10 +75,10 @@ class ExceptionHandler {
typedef bool (*FilterCallback)(void *context);
// A callback function to run after the minidump has been written.
// |descriptor| contains the file descriptor or file path containing the
// minidump. |context| is the parameter supplied by the user as
// callback_context when the handler was created. |succeeded| indicates
// whether a minidump file was successfully written.
// minidump_id is a unique id for the dump, so the minidump
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
// by the user as callback_context when the handler was created. succeeded
// indicates whether a minidump file was successfully written.
//
// If an exception occurred and the callback returns true, Breakpad will
// treat the exception as fully-handled, suppressing any other handlers from
@@ -104,13 +90,14 @@ class ExceptionHandler {
// should normally return the value of |succeeded|, or when they wish to
// not report an exception of handled, false. Callbacks will rarely want to
// return true directly (unless |succeeded| is true).
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
void* context,
typedef bool (*MinidumpCallback)(const char *dump_path,
const char *minidump_id,
void *context,
bool succeeded);
// In certain cases, a user may wish to handle the generation of the minidump
// themselves. In this case, they can install a handler callback which is
// called when a crash has occurred. If this function returns true, no other
// called when a crash has occured. If this function returns true, no other
// processing of occurs and the process will shortly be crashed. If this
// returns false, the normal processing continues.
typedef bool (*HandlerCallback)(const void* crash_context,
@@ -118,52 +105,40 @@ class ExceptionHandler {
void* context);
// Creates a new ExceptionHandler instance to handle writing minidumps.
// Before writing a minidump, the optional |filter| callback will be called.
// Before writing a minidump, the optional filter callback will be called.
// Its return value determines whether or not Breakpad should write a
// minidump. The minidump content will be written to the file path or file
// descriptor from |descriptor|, and the optional |callback| is called after
// writing the dump file, as described above.
// minidump. Minidump files will be written to dump_path, and the optional
// callback is called after writing the dump file, as described above.
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
// If |server_fd| is valid, the minidump is generated out-of-process. If it
// is -1, in-process generation will always be used.
ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
ExceptionHandler(const std::string &dump_path,
FilterCallback filter, MinidumpCallback callback,
void *callback_context,
bool install_handler,
const int server_fd);
bool install_handler);
~ExceptionHandler();
const MinidumpDescriptor& minidump_descriptor() const {
return minidump_descriptor_;
// Get and set the minidump path.
std::string dump_path() const { return dump_path_; }
void set_dump_path(const std::string &dump_path) {
dump_path_ = dump_path;
dump_path_c_ = dump_path_.c_str();
UpdateNextID();
}
void set_crash_handler(HandlerCallback callback) {
crash_handler_ = callback;
}
// Writes a minidump immediately. This can be used to capture the execution
// state independently of a crash.
// Returns true on success.
// 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.
// Writes a minidump immediately. This can be used to capture the
// execution state independently of a crash. Returns true on success.
bool WriteMinidump();
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string& dump_path,
static bool WriteMinidump(const std::string &dump_path,
MinidumpCallback callback,
void* callback_context);
void *callback_context);
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
// blob. It shouldn't be needed in any user code.
@@ -171,36 +146,15 @@ class ExceptionHandler {
siginfo_t siginfo;
pid_t tid; // the crashing thread.
struct ucontext context;
#if !defined(__ARM_EABI__)
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
struct _libc_fpstate float_state;
#endif
};
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
return crash_generation_client_.get() != NULL;
}
// Add information about a memory mapping. This can be used if
// a custom library loader is used that maps things in a way
// that the linux dumper can't handle by reading the maps file.
void AddMappingInfo(const string& name,
const u_int8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset);
// Force signal handling for the specified signal.
bool SimulateSignalDelivery(int sig);
private:
bool InstallHandlers();
void UninstallHandlers();
void PreresolveSymbols();
bool GenerateDump(CrashContext *context);
void SendContinueSignalToChild();
void WaitForContinueSignal();
void UpdateNextID();
static void SignalHandler(int sig, siginfo_t* info, void* uc);
bool HandleSignal(int sig, siginfo_t* info, void* uc);
static int ThreadEntry(void* arg);
@@ -211,10 +165,19 @@ class ExceptionHandler {
const MinidumpCallback callback_;
void* const callback_context_;
scoped_ptr<CrashGenerationClient> crash_generation_client_;
std::string dump_path_;
std::string next_minidump_path_;
std::string next_minidump_id_;
MinidumpDescriptor minidump_descriptor_;
// Pointers to C-string representations of the above. These are set
// when the above are set so we can avoid calling c_str during
// an exception.
const char* dump_path_c_;
const char* next_minidump_path_c_;
const char* next_minidump_id_c_;
const bool handler_installed_;
void* signal_stack; // the handler stack.
HandlerCallback crash_handler_;
// The global exception handler stack. This is need becuase there may exist
@@ -225,19 +188,9 @@ class ExceptionHandler {
static unsigned handler_stack_index_;
static pthread_mutex_t handler_stack_mutex_;
// A vector of the old signal handlers.
std::vector<std::pair<int, struct sigaction *> > old_handlers_;
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// can do this. We create a pipe which we can use to block the
// cloned process after creating it, until we have explicitly enabled
// ptrace. This is used to store the file descriptors for the pipe
int fdes[2];
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
MappingList mapping_list_;
// A vector of the old signal handlers. The void* is a pointer to a newly
// allocated sigaction structure to avoid pulling in too many includes.
std::vector<std::pair<int, void *> > old_handlers_;
};
} // namespace google_breakpad

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010 Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,50 +27,34 @@
// (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 <string>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler//exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/linux_libc_support.h"
#include "common/tests/auto_tempdir.h"
#include "common/using_std_string.h"
#include "third_party/lss/linux_syscall_support.h"
#include "google_breakpad/processor/minidump.h"
#include "common/linux/linux_syscall_support.h"
#include "breakpad_googletest_includes.h"
// This provides a wrapper around system calls which may be
// interrupted by a signal and return EINTR. See man 7 signal.
#define HANDLE_EINTR(x) ({ \
typeof(x) __eintr_result__; \
do { \
__eintr_result__ = x; \
} while (__eintr_result__ == -1 && errno == EINTR); \
__eintr_result__;\
})
using namespace google_breakpad;
namespace {
// Length of a formatted GUID string =
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
const int kGUIDStringSize = 37;
void sigchld_handler(int signo) { }
int CreateTMPFile(const std::string& dir, std::string* path) {
std::string file = dir + "/exception-handler-unittest.XXXXXX";
const char* c_file = file.c_str();
// Copy that string, mkstemp needs a C string it can modify.
char* c_path = strdup(c_file);
const int fd = mkstemp(c_path);
if (fd >= 0)
*path = c_path;
free(c_path);
return fd;
}
static void sigchld_handler(int signo) { }
class ExceptionHandlerTest : public ::testing::Test {
protected:
@@ -89,547 +73,68 @@ class ExceptionHandlerTest : public ::testing::Test {
struct sigaction old_action;
};
void WaitForProcessToTerminate(pid_t process_id, int expected_status) {
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(expected_status, WTERMSIG(status));
TEST(ExceptionHandlerTest, Simple) {
ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
}
// Reads the minidump path sent over the pipe |fd| and sets it in |path|.
void ReadMinidumpPathFromPipe(int fd, std::string* path) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fd;
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint32_t len;
ASSERT_EQ(static_cast<ssize_t>(sizeof(len)), read(fd, &len, sizeof(len)));
ASSERT_LT(len, static_cast<uint32_t>(2048));
char* filename = static_cast<char*>(malloc(len + 1));
ASSERT_EQ(len, read(fd, filename, len));
filename[len] = 0;
close(fd);
*path = filename;
free(filename);
}
} // namespace
TEST(ExceptionHandlerTest, SimpleWithPath) {
AutoTempDir temp_dir;
ExceptionHandler handler(
MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
}
TEST(ExceptionHandlerTest, SimpleWithFD) {
AutoTempDir temp_dir;
std::string path;
const int fd = CreateTMPFile(temp_dir.path(), &path);
ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1);
close(fd);
}
static bool DoneCallback(const MinidumpDescriptor& descriptor,
static bool DoneCallback(const char* dump_path,
const char* minidump_id,
void* context,
bool succeeded) {
if (!succeeded)
return false;
return succeeded;
int fd = (int) context;
uint32_t len = my_strlen(minidump_id);
HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
HANDLE_EINTR(sys_write(fd, minidump_id, len));
sys_close(fd);
if (!descriptor.IsFD()) {
int fd = reinterpret_cast<intptr_t>(context);
uint32_t len = 0;
len = my_strlen(descriptor.path());
IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len))));
IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len)));
}
return true;
}
void ChildCrash(bool use_fd) {
AutoTempDir temp_dir;
int fds[2] = {0};
int minidump_fd = -1;
std::string minidump_path;
if (use_fd) {
minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path);
} else {
ASSERT_NE(pipe(fds), -1);
}
const pid_t child = fork();
if (child == 0) {
{
scoped_ptr<ExceptionHandler> handler;
if (use_fd) {
handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd),
NULL, NULL, NULL, true, -1));
} else {
close(fds[0]); // Close the reading end.
void* fd_param = reinterpret_cast<void*>(fds[1]);
handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()),
NULL, DoneCallback, fd_param,
true, -1));
}
// Crash with the exception handler in scope.
*reinterpret_cast<volatile int*>(NULL) = 0;
}
}
if (!use_fd)
close(fds[1]); // Close the writting end.
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
if (!use_fd)
ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
struct stat st;
ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
ASSERT_GT(st.st_size, 0u);
unlink(minidump_path.c_str());
}
TEST(ExceptionHandlerTest, ChildCrashWithPath) {
ASSERT_NO_FATAL_FAILURE(ChildCrash(false));
}
TEST(ExceptionHandlerTest, ChildCrashWithFD) {
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
}
// Test that memory around the instruction pointer is written
// to the dump as a MinidumpMemoryRegion.
TEST(ExceptionHandlerTest, InstructionPointerMemory) {
AutoTempDir temp_dir;
int fds[2];
ASSERT_NE(pipe(fds), -1);
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t kMemorySize = 256; // bytes
const int kOffset = kMemorySize / 2;
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
DoneCallback, reinterpret_cast<void*>(fds[1]),
true, -1);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them in the middle
// of the block of memory, because the minidump should contain 128
// bytes on either side of the instruction pointer.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
}
close(fds[1]);
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
string minidump_path;
ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
struct stat st;
ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
ASSERT_GT(st.st_size, 0u);
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_path);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_LT(0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
ASSERT_TRUE(region);
EXPECT_EQ(kMemorySize, region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t prefix_bytes[kOffset];
u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
memset(prefix_bytes, 0, sizeof(prefix_bytes));
memset(suffix_bytes, 0, sizeof(suffix_bytes));
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
suffix_bytes, sizeof(suffix_bytes)) == 0);
unlink(minidump_path.c_str());
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the low end.
TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
AutoTempDir temp_dir;
int fds[2];
ASSERT_NE(pipe(fds), -1);
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t kMemorySize = 256; // bytes
const int kOffset = 0;
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
DoneCallback, reinterpret_cast<void*>(fds[1]),
true, -1);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them in the middle
// of the block of memory, because the minidump should contain 128
// bytes on either side of the instruction pointer.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
}
close(fds[1]);
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
string minidump_path;
ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
struct stat st;
ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
ASSERT_GT(st.st_size, 0u);
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_path);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_LT(0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
ASSERT_TRUE(region);
EXPECT_EQ(kMemorySize / 2, region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
memset(suffix_bytes, 0, sizeof(suffix_bytes));
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
suffix_bytes, sizeof(suffix_bytes)) == 0);
unlink(minidump_path.c_str());
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the high end.
TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
AutoTempDir temp_dir;
int fds[2];
ASSERT_NE(pipe(fds), -1);
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
// Use 4k here because the OS will hand out a single page even
// if a smaller size is requested, and this test wants to
// test the upper bound of the memory range.
const u_int32_t kMemorySize = 4096; // bytes
// This crashes with SIGILL on x86/x86-64/arm.
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
const int kOffset = kMemorySize - sizeof(instructions);
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
DoneCallback, reinterpret_cast<void*>(fds[1]),
true, -1);
// Get some executable memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON,
-1,
0));
if (!memory)
exit(0);
// Write some instructions that will crash. Put them in the middle
// of the block of memory, because the minidump should contain 128
// bytes on either side of the instruction pointer.
memcpy(memory + kOffset, instructions, sizeof(instructions));
// Now execute the instructions, which should crash.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(memory + kOffset);
memory_function();
}
close(fds[1]);
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL));
string minidump_path;
ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
struct stat st;
ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
ASSERT_GT(st.st_size, 0u);
// Read the minidump. Locate the exception record and the memory list, and
// then ensure that there is a memory region in the memory list that covers
// the instruction pointer from the exception record.
Minidump minidump(minidump_path);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_LT(0, memory_list->region_count());
MinidumpContext* context = exception->GetContext();
ASSERT_TRUE(context);
u_int64_t instruction_pointer;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
instruction_pointer = context->GetContextX86()->eip;
break;
case MD_CONTEXT_AMD64:
instruction_pointer = context->GetContextAMD64()->rip;
break;
case MD_CONTEXT_ARM:
instruction_pointer = context->GetContextARM()->iregs[15];
break;
default:
FAIL() << "Unknown context CPU: " << context->GetContextCPU();
break;
}
MinidumpMemoryRegion* region =
memory_list->GetMemoryRegionForAddress(instruction_pointer);
ASSERT_TRUE(region);
const size_t kPrefixSize = 128; // bytes
EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
const u_int8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
u_int8_t prefix_bytes[kPrefixSize];
memset(prefix_bytes, 0, sizeof(prefix_bytes));
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
EXPECT_TRUE(memcmp(bytes + kPrefixSize,
instructions, sizeof(instructions)) == 0);
unlink(minidump_path.c_str());
}
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
// the number of memory regions differs, so there is no point in running
// this test if AddressSanitizer is used.
#ifndef ADDRESS_SANITIZER
// Ensure that an extra memory block doesn't get added when the instruction
// pointer is not in mapped memory.
TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
AutoTempDir temp_dir;
TEST(ExceptionHandlerTest, ChildCrash) {
int fds[2];
ASSERT_NE(pipe(fds), -1);
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL,
DoneCallback, reinterpret_cast<void*>(fds[1]),
true, -1);
// Try calling a NULL pointer.
typedef void (*void_function)(void);
void_function memory_function = reinterpret_cast<void_function>(NULL);
memory_function();
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
true);
*reinterpret_cast<int*>(NULL) = 0;
}
close(fds[1]);
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGSEGV);
string minidump_path;
ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path));
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
ASSERT_EQ(r, 1);
ASSERT_TRUE(pfd.revents & POLLIN);
uint32_t len;
ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
ASSERT_LT(len, 2048);
char* filename = reinterpret_cast<char*>(malloc(len + 1));
ASSERT_EQ(read(fds[0], filename, len), len);
filename[len] = 0;
close(fds[0]);
const std::string minidump_filename = std::string("/tmp/") + filename +
".dmp";
struct stat st;
ASSERT_EQ(0, stat(minidump_path.c_str(), &st));
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
Minidump minidump(minidump_path);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_EQ(static_cast<unsigned int>(1), memory_list->region_count());
unlink(minidump_path.c_str());
}
#endif // !ADDRESS_SANITIZER
// Test that anonymous memory maps can be annotated with names and IDs.
TEST(ExceptionHandlerTest, ModuleInfo) {
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
char module_identifier_buffer[kGUIDStringSize];
FileID::ConvertIdentifierToString(kModuleGUID,
module_identifier_buffer,
sizeof(module_identifier_buffer));
string module_identifier(module_identifier_buffer);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
// Get some memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
AutoTempDir temp_dir;
ExceptionHandler handler(
MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
// Add info about the anonymous memory mapping.
handler.AddMappingInfo(kMemoryName,
kModuleGUID,
kMemoryAddress,
kMemorySize,
0);
ASSERT_TRUE(handler.WriteMinidump());
const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor();
// Read the minidump. Load the module list, and ensure that the mmap'ed
// |memory| is listed with the given module name and debug ID.
Minidump minidump(minidump_desc.path());
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(kMemoryAddress, module->base_address());
EXPECT_EQ(kMemorySize, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
unlink(minidump_desc.path());
unlink(minidump_filename.c_str());
}
static const unsigned kControlMsgSize =
@@ -638,20 +143,15 @@ static const unsigned kControlMsgSize =
static bool
CrashHandler(const void* crash_context, size_t crash_context_size,
void* context) {
const int fd = (intptr_t) context;
const int fd = (int) context;
int fds[2];
if (pipe(fds) == -1) {
// There doesn't seem to be any way to reliably handle
// this failure without the parent process hanging
// At least make sure that this process doesn't access
// unexpected file descriptors
fds[0] = -1;
fds[1] = -1;
}
pipe(fds);
struct kernel_msghdr msg = {0};
struct kernel_iovec iov;
iov.iov_base = const_cast<void*>(crash_context);
iov.iov_len = crash_context_size;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsg[kControlMsgSize];
@@ -673,13 +173,11 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
cred->gid = getgid();
cred->pid = getpid();
ssize_t ret = HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
sys_close(fds[1]);
if (ret <= 0)
return false;
char b;
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
HANDLE_EINTR(sys_read(fds[0], &b, 1));
return true;
}
@@ -694,11 +192,11 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL,
reinterpret_cast<void*>(fds[1]), true, -1);
ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
handler.set_crash_handler(CrashHandler);
*reinterpret_cast<volatile int*>(NULL) = 0;
*reinterpret_cast<int*>(NULL) = 0;
}
close(fds[1]);
struct msghdr msg = {0};
struct iovec iov;
@@ -714,10 +212,9 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
msg.msg_controllen = kControlMsgSize;
const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
ASSERT_EQ(kCrashContextSize, n);
ASSERT_EQ(kControlMsgSize, msg.msg_controllen);
ASSERT_EQ(0, msg.msg_flags);
ASSERT_EQ(0, close(fds[0]));
ASSERT_EQ(n, kCrashContextSize);
ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
ASSERT_EQ(msg.msg_flags, 0);
pid_t crashing_pid = -1;
int signal_fd = -1;
@@ -728,8 +225,8 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
if (hdr->cmsg_type == SCM_RIGHTS) {
const unsigned len = hdr->cmsg_len -
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
ASSERT_EQ(sizeof(int), len);
signal_fd = *(reinterpret_cast<int*>(CMSG_DATA(hdr)));
ASSERT_EQ(len, sizeof(int));
signal_fd = *((int *) CMSG_DATA(hdr));
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
const struct ucred *cred =
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
@@ -740,66 +237,20 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
ASSERT_NE(crashing_pid, -1);
ASSERT_NE(signal_fd, -1);
AutoTempDir temp_dir;
string templ = temp_dir.path() + "/exception-handler-unittest";
ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context,
char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
mktemp(templ);
ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
kCrashContextSize));
static const char b = 0;
ASSERT_EQ(1U, (HANDLE_EINTR(write(signal_fd, &b, 1))));
ASSERT_EQ(0, close(signal_fd));
HANDLE_EINTR(write(signal_fd, &b, 1));
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGSEGV);
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0U);
unlink(templ.c_str());
}
TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) {
AutoTempDir temp_dir;
std::string path;
const int fd = CreateTMPFile(temp_dir.path(), &path);
ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1);
ASSERT_TRUE(handler.WriteMinidump());
// Check by the size of the data written to the FD that a minidump was
// generated.
off_t size = lseek(fd, 0, SEEK_CUR);
ASSERT_GT(size, 0);
// Generate another minidump.
ASSERT_TRUE(handler.WriteMinidump());
size = lseek(fd, 0, SEEK_CUR);
ASSERT_GT(size, 0);
}
TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) {
AutoTempDir temp_dir;
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
NULL, false, -1);
ASSERT_TRUE(handler.WriteMinidump());
const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor();
struct stat st;
ASSERT_EQ(0, stat(minidump_1.path(), &st));
ASSERT_GT(st.st_size, 0U);
std::string minidump_1_path(minidump_1.path());
// Check it is a valid minidump.
Minidump minidump1(minidump_1_path);
ASSERT_TRUE(minidump1.Read());
unlink(minidump_1.path());
// Generate another minidump, it should go to a different file.
ASSERT_TRUE(handler.WriteMinidump());
const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor();
ASSERT_EQ(0, stat(minidump_2.path(), &st));
ASSERT_GT(st.st_size, 0U);
std::string minidump_2_path(minidump_2.path());
// Check it is a valid minidump.
Minidump minidump2(minidump_2_path);
ASSERT_TRUE(minidump2.Read());
unlink(minidump_2.path());
// We expect 2 distinct files.
ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str());
ASSERT_EQ(stat(templ, &st), 0);
ASSERT_GT(st.st_size, 0u);
unlink(templ);
}

View File

View File

@@ -1,61 +0,0 @@
// 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());
}
void MinidumpDescriptor::UpdatePath() {
assert(fd_ == -1 && !directory_.empty());
GUID guid;
char guid_str[kGUIDStringLength + 1];
bool r = CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str));
assert(r);
path_.clear();
path_ = directory_ + "/" + guid_str + ".dmp";
c_path_ = path_.c_str();
}
} // namespace google_breakpad

View File

@@ -1,86 +0,0 @@
// 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 <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 std::string& directory)
: fd_(-1),
directory_(directory),
c_path_(NULL) {
assert(!directory.empty());
}
explicit MinidumpDescriptor(int fd) : fd_(fd), c_path_(NULL) {
assert(fd != -1);
}
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
bool IsFD() const { return fd_ != -1; }
int fd() const { return fd_; }
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();
private:
// The file descriptor where the minidump is generated.
const int fd_;
// The directory where the minidump should be generated.
const 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_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_

View File

@@ -1,48 +0,0 @@
// 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

View File

@@ -37,8 +37,7 @@
#include <errno.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
namespace google_breakpad {
@@ -91,7 +90,7 @@ class DirectoryReader {
reinterpret_cast<kernel_dirent*>(buf_);
buf_used_ -= dent->d_reclen;
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
memmove(buf_, buf_ + dent->d_reclen, buf_used_);
}
private:

View File

@@ -35,7 +35,6 @@
#include <sys/types.h>
#include "client/linux/minidump_writer/directory_reader.h"
#include "common/using_std_string.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
@@ -45,7 +44,7 @@ typedef testing::Test DirectoryReaderTest;
}
TEST(DirectoryReaderTest, CompareResults) {
std::set<string> dent_set;
std::set<std::string> dent_set;
DIR *const dir = opendir("/proc/self");
ASSERT_TRUE(dir != NULL);

View File

@@ -34,8 +34,7 @@
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
namespace google_breakpad {
@@ -115,7 +114,7 @@ class LineReader {
assert(buf_used_ >= len + 1);
buf_used_ -= len + 1;
my_memmove(buf_, buf_ + len + 1, buf_used_);
memmove(buf_, buf_ + len + 1, buf_used_);
}
private:

View File

@@ -33,18 +33,11 @@
#include "client/linux/minidump_writer/line_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
static int TemporaryFile() {
static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX";
static const char templ[] = "/tmp/line-reader-unittest-XXXXXX";
char templ_copy[sizeof(templ)];
memcpy(templ_copy, templ, sizeof(templ));
const int fd = mkstemp(templ_copy);
@@ -71,17 +64,16 @@ TEST(LineReaderTest, EmptyFile) {
TEST(LineReaderTest, OneLineTerminated) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\n", 2));
ASSERT_EQ(2, r);
write(fd, "a\n", 2);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned int len;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned int)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
@@ -91,17 +83,16 @@ TEST(LineReaderTest, OneLineTerminated) {
TEST(LineReaderTest, OneLine) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a", 1));
ASSERT_EQ(1, r);
write(fd, "a", 1);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
@@ -111,23 +102,22 @@ TEST(LineReaderTest, OneLine) {
TEST(LineReaderTest, TwoLinesTerminated) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb\n", 4));
ASSERT_EQ(4, r);
write(fd, "a\nb\n", 4);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
@@ -137,23 +127,22 @@ TEST(LineReaderTest, TwoLinesTerminated) {
TEST(LineReaderTest, TwoLines) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb", 3));
ASSERT_EQ(3, r);
write(fd, "a\nb", 3);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
@@ -165,17 +154,16 @@ TEST(LineReaderTest, MaxLength) {
const int fd = TemporaryFile();
char l[LineReader::kMaxLineLen - 1];
memset(l, 'a', sizeof(l));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(sizeof(l), r);
write(fd, l, sizeof(l));
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(sizeof(l), len);
ASSERT_EQ(len, sizeof(l));
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
ASSERT_EQ('\0', line[len]);
ASSERT_EQ(line[len], 0);
close(fd);
}
@@ -184,8 +172,7 @@ TEST(LineReaderTest, TooLong) {
const int fd = TemporaryFile();
char l[LineReader::kMaxLineLen];
memset(l, 'a', sizeof(l));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(sizeof(l), r);
write(fd, l, sizeof(l));
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);

View File

@@ -1,233 +0,0 @@
// 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_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
// See linux_core_dumper.h for details.
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
#include <sys/procfs.h>
#include "common/linux/linux_libc_support.h"
namespace google_breakpad {
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
const char* core_path,
const char* procfs_path)
: LinuxDumper(pid),
core_path_(core_path),
procfs_path_(procfs_path),
thread_infos_(&allocator_, 8) {
assert(core_path_);
}
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
size_t procfs_path_len = my_strlen(procfs_path_);
size_t total_length = procfs_path_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
memcpy(path, procfs_path_, procfs_path_len);
path[procfs_path_len] = '/';
memcpy(path + procfs_path_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
// TODO(benchan): Investigate whether the data to be copied could span
// across multiple segments in the core dump file. ElfCoreDump::CopyData
// and this method do not handle that case yet.
if (!core_.CopyData(dest, virtual_address, length)) {
// If the data segment is not found in the core dump, fill the result
// with marker characters.
memset(dest, 0xab, length);
}
}
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= thread_infos_.size())
return false;
*info = thread_infos_[index];
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
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
return true;
}
bool LinuxCoreDumper::IsPostMortem() const {
return true;
}
bool LinuxCoreDumper::ThreadsSuspend() {
return true;
}
bool LinuxCoreDumper::ThreadsResume() {
return true;
}
bool LinuxCoreDumper::EnumerateThreads() {
if (!mapped_core_file_.Map(core_path_)) {
fprintf(stderr, "Could not map core dump file into memory\n");
return false;
}
core_.SetContent(mapped_core_file_.content());
if (!core_.IsValid()) {
fprintf(stderr, "Invalid core dump file\n");
return false;
}
ElfCoreDump::Note note = core_.GetFirstNote();
if (!note.IsValid()) {
fprintf(stderr, "PT_NOTE section not found\n");
return false;
}
bool first_thread = true;
do {
ElfCoreDump::Word type = note.GetType();
MemoryRange name = note.GetName();
MemoryRange description = note.GetDescription();
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
return false;
}
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
// Thread Name Type
// -------------------------------------------------------------------
// 1st thread CORE NT_PRSTATUS
// process-wide CORE NT_PRPSINFO
// process-wide CORE NT_AUXV
// 1st thread CORE NT_FPREGSET
// 1st thread LINUX NT_PRXFPREG
// 1st thread LINUX NT_386_TLS
//
// 2nd thread CORE NT_PRSTATUS
// 2nd thread CORE NT_FPREGSET
// 2nd thread LINUX NT_PRXFPREG
// 2nd thread LINUX NT_386_TLS
//
// 3rd thread CORE NT_PRSTATUS
// 3rd thread CORE NT_FPREGSET
// 3rd thread LINUX NT_PRXFPREG
// 3rd thread LINUX NT_386_TLS
//
// The following code only works if notes are ordered as expected.
switch (type) {
case NT_PRSTATUS: {
if (description.length() != sizeof(elf_prstatus)) {
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
return false;
}
const elf_prstatus* status =
reinterpret_cast<const elf_prstatus*>(description.data());
pid_t pid = status->pr_pid;
ThreadInfo info;
memset(&info, 0, sizeof(ThreadInfo));
info.tgid = status->pr_pgrp;
info.ppid = status->pr_ppid;
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
if (first_thread) {
crash_thread_ = pid;
crash_signal_ = status->pr_info.si_signo;
}
first_thread = false;
threads_.push_back(pid);
thread_infos_.push_back(info);
break;
}
#if defined(__i386) || defined(__x86_64)
case NT_FPREGSET: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpregs)) {
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
break;
}
#endif
#if defined(__i386)
case NT_PRXFPREG: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpxregs)) {
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
break;
}
#endif
}
note = note.GetNextNote();
} while (note.IsValid());
return true;
}
} // namespace google_breakpad

View File

@@ -1,122 +0,0 @@
// 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_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via its core dump and proc files.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/elf_core_dump.h"
#include "common/linux/memory_mapped_file.h"
namespace google_breakpad {
class LinuxCoreDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid| via its core dump file at |core_path| and
// its proc files at |procfs_path|. If |procfs_path| is a copy of
// /proc/<pid>, it should contain the following files:
// auxv, cmdline, environ, exe, maps, status
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
// 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. Return true on
// success.
//
// As this dumper performs a post-mortem dump and makes use of a copy
// of the proc files of the crashed process, this derived method does
// not actually make use of |pid| and always returns a subpath of
// |procfs_path_| regardless of whether |pid| corresponds to the main
// process or a thread of the process, i.e. assuming both the main process
// and its threads have the following proc files with the same content:
// auxv, cmdline, environ, exe, maps, status
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 extracts the content
// the core dump and fills |dest| with a sequence of marker bytes
// if the expected data is not found in the core dump.
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 true to indicate that this dumper performs a
// post-mortem dump of a crashed process via a core dump file.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to suspend. This method does nothing and
// always returns true.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to resume. This method does nothing and
// always returns true.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Path of the core dump file.
const char* core_path_;
// Path of the directory containing the proc files of the given process,
// which is usually a copy of /proc/<pid>.
const char* procfs_path_;
// Memory-mapped core dump file at |core_path_|.
MemoryMappedFile mapped_core_file_;
// Content of the core dump file.
ElfCoreDump core_;
// Thread info found in the core dump file.
wasteful_vector<ThreadInfo> thread_infos_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_

View File

@@ -1,113 +0,0 @@
// 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_core_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxCoreDumoer.
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "common/linux/tests/crash_generator.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
TEST(LinuxCoreDumperTest, BuildProcPath) {
const pid_t pid = getpid();
const char procfs_path[] = "/procfs_copy";
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"%s/maps", procfs_path);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
}
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to non-default core pattern\n");
return;
}
const unsigned kNumOfThreads = 3;
const unsigned kCrashThread = 1;
const int kCrashSignal = SIGABRT;
pid_t child_pid;
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
// CrashGenerator is identified and fixed.
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, &child_pid)) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to no core dump generated\n");
return;
}
const string core_file = crash_generator.GetCoreFilePath();
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
EXPECT_TRUE(dumper.Init());
EXPECT_TRUE(dumper.IsPostMortem());
// These are no-ops and should always return true.
EXPECT_TRUE(dumper.ThreadsSuspend());
EXPECT_TRUE(dumper.ThreadsResume());
// LinuxCoreDumper cannot determine the crash address and thus it always
// sets the crash address to 0.
EXPECT_EQ(0, dumper.crash_address());
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
dumper.crash_thread());
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
for (unsigned i = 0; i < kNumOfThreads; ++i) {
ThreadInfo info;
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
EXPECT_EQ(getpid(), info.ppid);
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010, Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,9 +27,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
// See linux_dumper.h for details.
// This code deals with the mechanics of getting information about a crashed
// process. Since this code may run in a compromised address space, the same
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
@@ -38,104 +35,134 @@
#include "client/linux/minidump_writer/linux_dumper.h"
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.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/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "common/linux/safe_readlink.h"
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
// Suspend a thread by attaching to it.
static bool SuspendThread(pid_t pid) {
// This may fail if the thread has just died or debugged.
errno = 0;
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
errno != 0) {
return false;
}
while (sys_waitpid(pid, NULL, __WALL) < 0) {
if (errno != EINTR) {
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
return false;
}
}
return true;
}
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
return my_strncmp(mapping.name,
kMappedFileUnsafePrefix,
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
// Resume a thread by detaching from it.
static bool ResumeThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
namespace google_breakpad {
LinuxDumper::LinuxDumper(pid_t pid)
LinuxDumper::LinuxDumper(int pid)
: pid_(pid),
crash_address_(0),
crash_signal_(0),
crash_thread_(0),
threads_suspened_(false),
threads_(&allocator_, 8),
mappings_(&allocator_) {
}
LinuxDumper::~LinuxDumper() {
}
bool LinuxDumper::Init() {
return EnumerateThreads() && EnumerateMappings();
return EnumerateThreads(&threads_) &&
EnumerateMappings(&mappings_);
}
bool
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)])
{
assert(!member || mapping_id < mappings_.size());
my_memset(identifier, 0, sizeof(MDGUID));
if (IsMappedFileOpenUnsafe(mapping))
return false;
bool LinuxDumper::ThreadsSuspend() {
if (threads_suspened_)
return true;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= SuspendThread(threads_[i]);
threads_suspened_ = true;
return good;
}
// 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);
bool LinuxDumper::ThreadsResume() {
if (!threads_suspened_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
threads_suspened_ = false;
return good;
}
void
LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
assert(path);
if (!path) {
return;
}
char filename[NAME_MAX];
size_t filename_len = my_strlen(mapping.name);
assert(filename_len < NAME_MAX);
if (filename_len >= NAME_MAX)
return false;
my_memcpy(filename, mapping.name, filename_len);
filename[filename_len] = '\0';
bool filename_modified = HandleDeletedFileInMapping(filename);
path[0] = '\0';
MemoryMappedFile mapped_file(filename);
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
return false;
const unsigned pid_len = my_int_len(pid);
bool success =
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
if (success && member && filename_modified) {
mappings_[mapping_id]->name[filename_len -
sizeof(kDeletedSuffix) + 1] = '\0';
assert(node);
if (!node) {
return;
}
return success;
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);
memcpy(path + total_length, "\0", 1);
}
void*
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid, "auxv"))
return NULL;
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
char auxv_path[80];
BuildProcPath(auxv_path, pid, "auxv");
// 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
@@ -151,44 +178,18 @@ LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const {
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
one_aux_entry.a_type != AT_NULL) {
if (one_aux_entry.a_type == AT_SYSINFO_EHDR) {
sys_close(fd);
close(fd);
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
}
}
sys_close(fd);
close(fd);
return NULL;
}
void*
LinuxDumper::FindEntryPoint(pid_t pid) const {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid, "auxv"))
return NULL;
int fd = sys_open(auxv_path, O_RDONLY, 0);
if (fd < 0) {
return NULL;
}
// Find the AT_ENTRY entry
elf_aux_entry one_aux_entry;
while (sys_read(fd,
&one_aux_entry,
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
one_aux_entry.a_type != AT_NULL) {
if (one_aux_entry.a_type == AT_ENTRY) {
sys_close(fd);
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
}
}
sys_close(fd);
return NULL;
}
bool LinuxDumper::EnumerateMappings() {
char maps_path[NAME_MAX];
if (!BuildProcPath(maps_path, pid_, "maps"))
return false;
bool
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
char maps_path[80];
BuildProcPath(maps_path, pid_, "maps");
// 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
@@ -197,10 +198,6 @@ bool LinuxDumper::EnumerateMappings() {
// of mappings.
const void* linux_gate_loc;
linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_);
// 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 = FindEntryPoint(pid_);
const int fd = sys_open(maps_path, O_RDONLY, 0);
if (fd < 0)
@@ -218,56 +215,27 @@ bool LinuxDumper::EnumerateMappings() {
if (*i2 == ' ') {
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
if (*i3 == ' ') {
const char* name = NULL;
// Only copy name if the name is a valid path name, or if
// it's the VDSO image.
if (((name = my_strchr(line, '/')) == NULL) &&
linux_gate_loc &&
reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
name = kLinuxGateLibraryName;
offset = 0;
}
// Merge adjacent mappings with the same name into one module,
// assuming they're a single library mapped by the dynamic linker
if (name && !mappings_.empty()) {
MappingInfo* module = mappings_.back();
if ((start_addr == module->start_addr + module->size) &&
(my_strlen(name) == my_strlen(module->name)) &&
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
module->size = end_addr - module->start_addr;
line_reader->PopLine(line_len);
continue;
}
}
MappingInfo* const module = new(allocator_) MappingInfo;
my_memset(module, 0, sizeof(MappingInfo));
memset(module, 0, sizeof(MappingInfo));
module->start_addr = start_addr;
module->size = end_addr - start_addr;
module->offset = offset;
if (name != NULL) {
const char* name = NULL;
// Only copy name if the name is a valid path name, or if
// we've found the VDSO image
if ((name = my_strchr(line, '/')) != NULL) {
const unsigned l = my_strlen(name);
if (l < sizeof(module->name))
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);
memcpy(module->name, name, l);
} else if (linux_gate_loc &&
reinterpret_cast<void*>(module->start_addr) ==
linux_gate_loc) {
memcpy(module->name,
kLinuxGateLibraryName,
my_strlen(kLinuxGateLibraryName));
module->offset = 0;
}
result->push_back(module);
}
}
}
@@ -276,7 +244,108 @@ bool LinuxDumper::EnumerateMappings() {
sys_close(fd);
return !mappings_.empty();
return result->size() > 0;
}
// Parse /proc/$pid/task to list all the threads of the process identified by
// pid.
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
char task_path[80];
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 unavailible,
// these members are set to -1. Returns true iff all three members are
// availible.
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
assert(info != NULL);
char status_path[80];
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 ||
sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
return false;
}
#if defined(__i386) || defined(__x86_64)
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
return false;
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));
#else
#error "This code hasn't been ported to your platform yet."
#endif
if (!GetStackInfo(&info->stack, &info->stack_len,
(uintptr_t) stack_pointer))
return false;
return true;
}
// Get information about the stack, given the stack pointer. We don't try to
@@ -284,27 +353,56 @@ bool LinuxDumper::EnumerateMappings() {
// unwind. So we just grab, up to, 32k of stack.
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) {
#if defined(__i386) || defined(__x86_64)
static const bool stack_grows_down = true;
static const uintptr_t page_size = 4096;
#else
#error "This code has not been ported to your platform yet."
#endif
// Move the stack pointer to the bottom of the page that it's in.
const uintptr_t page_size = getpagesize();
uint8_t* const stack_pointer =
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
// The number of bytes of stack which we try to capture.
static const ptrdiff_t kStackToCapture = 32 * 1024;
static unsigned kStackToCapture = 32 * 1024;
const MappingInfo* mapping = FindMapping(stack_pointer);
if (!mapping)
return false;
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
if (stack_grows_down) {
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
} else {
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
*stack_len = offset > kStackToCapture ? kStackToCapture : offset;
*stack = stack_pointer - *stack_len;
}
return true;
}
// static
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) {
unsigned long tmp;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
uint8_t* const remote = (uint8_t*) src;
while (done < length) {
const size_t l = length - done > word_size ? word_size : length - done;
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
tmp = 0;
memcpy(local + done, &tmp, l);
done += l;
}
}
// Find the mapping which the given memory address falls in.
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
const uintptr_t addr = (uintptr_t) address;
@@ -318,41 +416,4 @@ const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
return NULL;
}
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
// Check for ' (deleted)' in |path|.
// |path| has to be at least as long as "/x (deleted)".
const size_t path_len = my_strlen(path);
if (path_len < kDeletedSuffixLen + 2)
return false;
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
kDeletedSuffixLen) != 0) {
return false;
}
// Check |path| against the /proc/pid/exe 'symlink'.
char exe_link[NAME_MAX];
char new_path[NAME_MAX];
if (!BuildProcPath(exe_link, pid_, "exe"))
return false;
if (!SafeReadLink(exe_link, new_path))
return false;
if (my_strcmp(path, new_path) != 0)
return false;
// Check to see if someone actually named their executable 'foo (deleted)'.
struct kernel_stat exe_stat;
struct kernel_stat new_path_stat;
if (sys_stat(exe_link, &exe_stat) == 0 &&
sys_stat(new_path, &new_path_stat) == 0 &&
exe_stat.st_dev == new_path_stat.st_dev &&
exe_stat.st_ino == new_path_stat.st_ino) {
return false;
}
my_memcpy(path, exe_link, NAME_MAX);
return true;
}
} // namespace google_breakpad

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2010, Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,36 +27,24 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
// is a base class for extracting information of a crashed process. It
// was originally a complete implementation using the ptrace API, but
// has been refactored to allow derived implementations supporting both
// ptrace and core dump. A portion of the original implementation is now
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
// details).
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#include <elf.h>
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#include <linux/limits.h>
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
#include "common/linux/memory.h"
namespace google_breakpad {
#if defined(__i386) || defined(__x86_64)
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
#endif
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386) || defined(__ARM_EABI__)
#if defined(__i386)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64)
#elif defined(__x86_64__)
typedef Elf64_auxv_t elf_aux_entry;
#endif
// When we find the VDSO mapping in the process's address space, this
@@ -69,22 +57,18 @@ struct ThreadInfo {
pid_t tgid; // thread group id
pid_t ppid; // parent process
uintptr_t stack_pointer; // thread stack pointer
// Even on platforms where the stack grows down, the following will point to
// the smallest address in the stack.
const void* stack; // pointer to the stack area
size_t stack_len; // length of the stack to copy
#if defined(__i386) || defined(__x86_64)
user_regs_struct regs;
user_fpregs_struct fpregs;
#if defined(__i386) || defined(__x86_64)
user_fpxregs_struct fpxregs;
static const unsigned kNumDebugRegisters = 8;
debugreg_t dregs[8];
#if defined(__i386)
user_fpxregs_struct fpxregs;
#endif // defined(__i386)
#elif defined(__ARM_EABI__)
// Mimicking how strace does this(see syscall.c, search for GETREGS)
struct user_regs regs;
struct user_fpregs fpregs;
#endif
};
@@ -101,21 +85,16 @@ class LinuxDumper {
public:
explicit LinuxDumper(pid_t pid);
virtual ~LinuxDumper();
// Parse the data for |threads| and |mappings|.
virtual bool Init();
// Return true if the dumper performs a post-mortem dump.
virtual bool IsPostMortem() const = 0;
bool Init();
// Suspend/resume all threads in the given process.
virtual bool ThreadsSuspend() = 0;
virtual bool ThreadsResume() = 0;
bool ThreadsSuspend();
bool ThreadsResume();
// Read information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
// Read information about the given thread. Returns true on success. One must
// have called |ThreadsSuspend| first.
bool ThreadInfoGet(pid_t tid, ThreadInfo* info);
// These are only valid after a call to |Init|.
const wasteful_vector<pid_t> &threads() { return threads_; }
@@ -130,79 +109,32 @@ class LinuxDumper {
PageAllocator* allocator() { return &allocator_; }
// Copy content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) = 0;
// memcpy from a remote process.
static void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
// Generate a File ID from the .text section of a mapped entry.
// If not a member, mapping_id is ignored.
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)]);
// Builds a proc path for a certain pid for a node. path is a
// character array that is overwritten, and node is the final node
// without any slashes.
void BuildProcPath(char* path, pid_t pid, const char* node) const;
// Utility method to find the location of where the kernel has
// mapped linux-gate.so in memory(shows up in /proc/pid/maps as
// [vdso], but we can't guarantee that it's the only virtual dynamic
// shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR
// is the safest way to go.)
void* FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const;
// Utility method to find the entry point location.
void* FindEntryPoint(pid_t pid) const;
void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const;
private:
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const;
bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
}
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
protected:
virtual bool EnumerateMappings();
virtual bool EnumerateThreads() = 0;
// For the case where a running program has been deleted, it'll show up in
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
// also checks to see if '/path/to/program (deleted)' exists, so it does not
// get fooled by a poorly named binary.
// For programs that don't end with ' (deleted)', this is a no-op.
// This assumes |path| is a buffer with length NAME_MAX.
// Returns true if |path| is modified.
bool HandleDeletedFileInMapping(char* path) const;
// ID of the crashed process.
const pid_t pid_;
// Virtual address at which the process crashed.
uintptr_t crash_address_;
// Signal that terminated the crashed process.
int crash_signal_;
// ID of the crashed thread.
pid_t crash_thread_;
mutable PageAllocator allocator_;
// IDs of all the threads.
wasteful_vector<pid_t> threads_;
// Info from /proc/<pid>/maps.
wasteful_vector<MappingInfo*> mappings_;
bool threads_suspened_;
wasteful_vector<pid_t> threads_; // the ids of all the threads
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
};
} // namespace google_breakpad

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2009, 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 <unistd.h>
#include "client/linux/minidump_writer/linux_dumper.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxDumperTest;
}
TEST(LinuxDumperTest, Setup) {
LinuxDumper dumper(getpid());
}
TEST(LinuxDumperTest, FindMappings) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
ASSERT_FALSE(dumper.FindMapping(NULL));
}
TEST(LinuxDumperTest, ThreadList) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), 1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
found = true;
break;
}
}
}
TEST(LinuxDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxDumper dumper(pid);
char maps_path[256] = "dummymappath";
char maps_path_expected[256];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"/proc/%d/maps", pid);
dumper.BuildProcPath(maps_path, pid, "maps");
ASSERT_STREQ(maps_path, maps_path_expected);
// In release mode, we expect BuildProcPath to handle the invalid
// parameters correctly and fill map_path with an empty
// NULL-terminated string.
#ifdef NDEBUG
snprintf(maps_path, sizeof(maps_path), "dummymappath");
dumper.BuildProcPath(maps_path, 0, "maps");
EXPECT_STREQ(maps_path, "");
snprintf(maps_path, sizeof(maps_path), "dummymappath");
dumper.BuildProcPath(maps_path, getpid(), "");
EXPECT_STREQ(maps_path, "");
snprintf(maps_path, sizeof(maps_path), "dummymappath");
dumper.BuildProcPath(maps_path, getpid(), NULL);
EXPECT_STREQ(maps_path, "");
#endif
}
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
if (linux_gate_loc) {
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
}

View File

@@ -1,90 +0,0 @@
// Copyright (c) 2010, 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.
//
// Helper program for the linux_dumper class, which creates a bunch of
// threads. The first word of each thread's stack is set to the thread
// id.
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "processor/scoped_ptr.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ARM_EABI__)
#define TID_PTR_REGISTER "r3"
#elif defined(__i386)
#define TID_PTR_REGISTER "ecx"
#elif defined(__x86_64)
#define TID_PTR_REGISTER "rcx"
#else
#error This test has not been ported to this platform.
#endif
void *thread_function(void *data) {
int pipefd = *static_cast<int *>(data);
volatile pid_t thread_id = syscall(__NR_gettid);
// Signal parent that a thread has started.
uint8_t byte = 1;
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
perror("ERROR: parent notification failed");
return NULL;
}
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr,
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
return 1;
}
int pipefd = atoi(argv[1]);
int num_threads = atoi(argv[2]);
if (num_threads < 1) {
fprintf(stderr, "ERROR: number of threads is 0");
return 1;
}
google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
for (int i = 1; i < num_threads; i++) {
pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
}
thread_function(&pipefd);
return 0;
}

View File

@@ -1,291 +0,0 @@
// 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, &regs) == -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_int_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_itos(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

View File

@@ -1,92 +0,0 @@
// 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_

View File

@@ -1,437 +0,0 @@
// Copyright (c) 2009, 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_unittest.cc:
// Unit tests for google_breakpad::LinuxPtraceDumoer.
//
// This file was renamed from linux_dumper_unittest.cc and modified due
// to LinuxDumper being splitted into two classes.
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/memory.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxPtraceDumperTest;
} // namespace
TEST(LinuxPtraceDumperTest, Setup) {
LinuxPtraceDumper dumper(getpid());
}
TEST(LinuxPtraceDumperTest, FindMappings) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
ASSERT_FALSE(dumper.FindMapping(NULL));
}
TEST(LinuxPtraceDumperTest, ThreadList) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), (size_t)1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
found = true;
break;
}
}
}
// Helper stack class to close a file descriptor and unmap
// a mmap'ed mapping.
class StackHelper {
public:
StackHelper(int fd, char* mapping, size_t size)
: fd_(fd), mapping_(mapping), size_(size) {}
~StackHelper() {
munmap(mapping_, size_);
close(fd_);
}
private:
int fd_;
char* mapping_;
size_t size_;
};
TEST(LinuxPtraceDumperTest, MergedMappings) {
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// mmap two segments out of the helper binary, one
// enclosed in the other, but with different protections.
const size_t kPageSize = sysconf(_SC_PAGESIZE);
const size_t kMappingSize = 3 * kPageSize;
int fd = open(helper_path.c_str(), O_RDONLY);
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path
<< ", Error: " << strerror(errno);
char* mapping =
reinterpret_cast<char*>(mmap(NULL,
kMappingSize,
PROT_READ,
MAP_SHARED,
fd,
0));
ASSERT_TRUE(mapping);
const uintptr_t kMappingAddress = reinterpret_cast<uintptr_t>(mapping);
// Ensure that things get cleaned up.
StackHelper helper(fd, mapping, kMappingSize);
// Carve a page out of the first mapping with different permissions.
char* inside_mapping = reinterpret_cast<char*>(
mmap(mapping + 2 *kPageSize,
kPageSize,
PROT_NONE,
MAP_SHARED | MAP_FIXED,
fd,
// Map a different offset just to
// better test real-world conditions.
kPageSize));
ASSERT_TRUE(inside_mapping);
// Now check that LinuxPtraceDumper interpreted the mappings properly.
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
int mapping_count = 0;
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
const MappingInfo& mapping = *dumper.mappings()[i];
if (strcmp(mapping.name, helper_path.c_str()) == 0) {
// This mapping should encompass the entire original mapped
// range.
EXPECT_EQ(kMappingAddress, mapping.start_addr);
EXPECT_EQ(kMappingSize, mapping.size);
EXPECT_EQ(0, mapping.offset);
mapping_count++;
}
}
EXPECT_EQ(1, mapping_count);
}
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
static const int kNumberOfThreadsInHelperProgram = 5;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
exit(0);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), sizeof(junk));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
// Children are ready now.
LinuxPtraceDumper dumper(child_pid);
ASSERT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
EXPECT_TRUE(dumper.ThreadsSuspend());
ThreadInfo one_thread;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
one_thread.stack_pointer));
// In the helper program, we stored a pointer to the thread id in a
// specific register. Check that we can recover its value.
#if defined(__ARM_EABI__)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
#elif defined(__i386)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
#elif defined(__x86_64)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
#else
#error This test has not been ported to this platform.
#endif
pid_t one_thread_id;
dumper.CopyFromProcess(&one_thread_id,
dumper.threads()[i],
process_tid_location,
4);
EXPECT_EQ(dumper.threads()[i], one_thread_id);
}
EXPECT_TRUE(dumper.ThreadsResume());
kill(child_pid, SIGKILL);
// Reap child
int status;
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(SIGKILL, WTERMSIG(status));
}
TEST(LinuxPtraceDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxPtraceDumper dumper(pid);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"/proc/%d/maps", pid);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
}
#if !defined(__ARM_EABI__)
// Ensure that the linux-gate VDSO is included in the mapping list.
TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
ASSERT_TRUE(linux_gate_loc);
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
TEST(LinuxPtraceDumperTest, LinuxGateMappingID) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID
// from a child process.
TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
// Now wait forever for the parent.
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxPtraceDumper dumper(child);
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
// Need to suspend the child so ptrace actually works.
ASSERT_TRUE(dumper.ThreadsSuspend());
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
EXPECT_TRUE(dumper.ThreadsResume());
close(fds[1]);
}
#endif
TEST(LinuxPtraceDumperTest, FileIDsMatch) {
// Calculate the File ID of our binary using both
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
// and ensure that we get the same result from both.
char exe_name[PATH_MAX];
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
// Now wait forever for the parent.
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxPtraceDumper dumper(child);
ASSERT_TRUE(dumper.Init());
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
bool found_exe = false;
unsigned i;
for (i = 0; i < mappings.size(); ++i) {
const MappingInfo* mapping = mappings[i];
if (!strcmp(mapping->name, exe_name)) {
found_exe = true;
break;
}
}
ASSERT_TRUE(found_exe);
uint8_t identifier1[sizeof(MDGUID)];
uint8_t identifier2[sizeof(MDGUID)];
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
identifier1));
FileID fileid(exe_name);
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
char identifier_string1[37];
char identifier_string2[37];
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
37);
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
37);
EXPECT_STREQ(identifier_string1, identifier_string2);
close(fds[1]);
}

File diff suppressed because it is too large Load Diff

View File

@@ -33,51 +33,20 @@
#include <stdint.h>
#include <unistd.h>
#include <list>
#include <utility>
#include "client/linux/minidump_writer/linux_dumper.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class ExceptionHandler;
struct MappingEntry {
MappingInfo first;
u_int8_t second[sizeof(MDGUID)];
};
// A list of <MappingInfo, GUID>
typedef std::list<MappingEntry> MappingList;
// Writes a minidump to the filesystem. These functions do not malloc nor use
// Write a minidump to the filesystem. This function does not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
// minidump_path: the path to the file to write to. This is opened O_EXCL and
// fails open fails.
// filename: the filename to write to. This is opened O_EXCL and fails if
// open fails.
// crashing_process: the pid of the crashing process. This must be trusted.
// blob: a blob of data from the crashing process. See exception_handler.h
// blob_size: the length of |blob|, in bytes
//
// Returns true iff successful.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
bool WriteMinidump(const char* filename, 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);
// These overloads also allow passing a list of known mappings.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings);
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings);
bool WriteMinidump(const char* filename,
const MappingList& mappings,
LinuxDumper* dumper);
} // namespace google_breakpad

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2011 Google Inc.
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,70 +27,30 @@
// (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 <fcntl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/tests/auto_tempdir.h"
#include "common/using_std_string.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/scoped_ptr.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
// Length of a formatted GUID string =
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
const int kGUIDStringSize = 37;
// This provides a wrapper around system calls which may be
// interrupted by a signal and return EINTR. See man 7 signal.
#define HANDLE_EINTR(x) ({ \
typeof(x) __eintr_result__; \
do { \
__eintr_result__ = x; \
} while (__eintr_result__ == -1 && errno == EINTR); \
__eintr_result__;\
})
namespace {
typedef testing::Test MinidumpWriterTest;
}
TEST(MinidumpWriterTest, SetupWithPath) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
AutoTempDir temp_dir;
string templ = temp_dir.path() + "/minidump-writer-unittest";
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(stat(templ.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
close(fds[1]);
}
TEST(MinidumpWriterTest, SetupWithFD) {
TEST(MinidumpWriterTest, Setup) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
@@ -107,388 +67,13 @@ TEST(MinidumpWriterTest, SetupWithFD) {
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
AutoTempDir temp_dir;
string templ = temp_dir.path() + "/minidump-writer-unittest";
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
char templ[] = "/tmp/minidump-writer-unittest-XXXXXX";
mktemp(templ);
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(stat(templ.c_str(), &st), 0);
ASSERT_EQ(stat(templ, &st), 0);
ASSERT_GT(st.st_size, 0u);
close(fds[1]);
}
// Test that mapping info can be specified when writing a minidump,
// and that it ends up in the module list of the minidump.
TEST(MinidumpWriterTest, MappingInfo) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
char module_identifier_buffer[kGUIDStringSize];
FileID::ConvertIdentifierToString(kModuleGUID,
module_identifier_buffer,
sizeof(module_identifier_buffer));
string module_identifier(module_identifier_buffer);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
// Get some memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
context.tid = 1;
AutoTempDir temp_dir;
string templ = temp_dir.path() + "/minidump-writer-unittest";
// Add information about the mapped memory.
MappingInfo info;
info.start_addr = kMemoryAddress;
info.size = memory_size;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(templ.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(kMemoryAddress, module->base_address());
EXPECT_EQ(memory_size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
close(fds[1]);
}
// Test that mapping info can be specified, and that it overrides
// existing mappings that are wholly contained within the specified
// range.
TEST(MinidumpWriterTest, MappingInfoContained) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const u_int32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
char module_identifier_buffer[kGUIDStringSize];
FileID::ConvertIdentifierToString(kModuleGUID,
module_identifier_buffer,
sizeof(module_identifier_buffer));
string module_identifier(module_identifier_buffer);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
// mmap a file
AutoTempDir temp_dir;
string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
ASSERT_NE(-1, fd);
unlink(tempfile.c_str());
// fill with zeros
google_breakpad::scoped_array<char> buffer(new char[memory_size]);
memset(buffer.get(), 0, memory_size);
ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
lseek(fd, 0, SEEK_SET);
char* memory =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
close(fd);
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
context.tid = 1;
string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
// Add information about the mapped memory. Report it as being larger than
// it actually is.
MappingInfo info;
info.start_addr = kMemoryAddress - memory_size;
info.size = memory_size * 3;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
mappings));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(dumpfile.c_str());
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(info.start_addr, module->base_address());
EXPECT_EQ(info.size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
close(fds[1]);
}
TEST(MinidumpWriterTest, DeletedBinary) {
static const int kNumberOfThreadsInHelperProgram = 1;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Copy binary to a temp file.
AutoTempDir temp_dir;
string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
char cmdline[2 * PATH_MAX];
sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
binpath.c_str());
ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(binpath.c_str(),
binpath.c_str(),
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
}
close(fds[1]);
// Wait for the child process to signal that it's ready.
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
ASSERT_EQ(sizeof(junk), nr);
close(fds[0]);
// Child is ready now.
// Unlink the test binary.
unlink(binpath.c_str());
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
string templ = temp_dir.path() + "/minidump-writer-unittest";
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
sizeof(context)));
kill(child_pid, SIGKILL);
struct stat st;
ASSERT_EQ(stat(templ.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
Minidump minidump(templ.c_str());
ASSERT_TRUE(minidump.Read());
// Check that the main module filename is correct.
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module = module_list->GetMainModule();
EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
// Check that the file ID is correct.
FileID fileid(helper_path.c_str());
uint8_t identifier[sizeof(MDGUID)];
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
char identifier_string[kGUIDStringSize];
FileID::ConvertIdentifierToString(identifier,
identifier_string,
kGUIDStringSize);
string module_identifier(identifier_string);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
EXPECT_EQ(module_identifier, module->debug_identifier());
}
// Test that an invalid thread stack pointer still results in a minidump.
TEST(MinidumpWriterTest, InvalidStackPointer) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
// This needs a valid context for minidump writing to work, but getting
// a useful one from the child is too much work, so just use one from
// the parent since the child is just a forked copy anyway.
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
// Fake the child's stack pointer for its crashing thread. NOTE: This must
// be an invalid memory address for the child process (stack or otherwise).
#if defined(__i386)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
#elif defined(__x86_64)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
#elif defined(__ARM_EABI__)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
#else
# error "This code has not been ported to your platform yet."
#endif
AutoTempDir temp_dir;
string templ = temp_dir.path() + "/minidump-writer-unittest";
// NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
// presented with an invalid stack pointer.
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
// Read the minidump. Ensure that the memory region is present
Minidump minidump(templ.c_str());
ASSERT_TRUE(minidump.Read());
// TODO(ted.mielczarek,mkrebs): Enable this part of the test once
// https://breakpad.appspot.com/413002/ is committed.
#if 0
// Make sure there's a thread without a stack. NOTE: It's okay if
// GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
// region problem".
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
bool found_empty_stack = false;
for (int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
if (thread->GetMemory() == NULL) {
found_empty_stack = true;
break;
}
}
// NOTE: If you fail this, first make sure that "invalid_stack_pointer"
// above is indeed set to an invalid address.
ASSERT_TRUE(found_empty_stack);
#endif
unlink(templ);
close(fds[1]);
}

View File

@@ -1,66 +0,0 @@
// 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.
// minidump_writer_unittest_utils.cc:
// Shared routines used by unittests under client/linux/minidump_writer.
#include <limits.h>
#include <stdlib.h>
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/safe_readlink.h"
#include "common/using_std_string.h"
namespace google_breakpad {
string GetHelperBinary() {
string helper_path;
char *bindir = getenv("bindir");
if (bindir) {
helper_path = string(bindir) + "/";
} else {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (!SafeReadLink("/proc/self/exe", self_path)) {
return "";
}
helper_path = string(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
return "";
}
helper_path.erase(pos + 1);
}
helper_path += "linux_dumper_unittest_helper";
return helper_path;
}
} // namespace google_breakpad

View File

@@ -1,49 +0,0 @@
// 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.
// minidump_writer_unittest_utils.h:
// Shared routines used by unittests under client/linux/minidump_writer.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// Returns the full path to linux_dumper_unittest_helper. The full path is
// discovered either by using the environment variable "bindir" or by using
// the location of the main module of the currently running process.
string GetHelperBinary();
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_

View File

@@ -1,104 +0,0 @@
// Copyright (c) 2009, 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/google_crashdump_uploader.h"
#include "third_party/linux/include/gflags/gflags.h"
#include <string>
#include <iostream>
#include "common/using_std_string.h"
DEFINE_string(crash_server, "https://clients2.google.com/cr",
"The crash server to upload minidumps to.");
DEFINE_string(product_name, "",
"The product name that the minidump corresponds to.");
DEFINE_string(product_version, "",
"The version of the product that produced the minidump.");
DEFINE_string(client_id, "",
"The client GUID");
DEFINE_string(minidump_path, "",
"The path of the minidump file.");
DEFINE_string(ptime, "",
"The process uptime in milliseconds.");
DEFINE_string(ctime, "",
"The cumulative process uptime in milliseconds.");
DEFINE_string(email, "",
"The user's email address.");
DEFINE_string(comments, "",
"Extra user comments");
DEFINE_string(proxy_host, "",
"Proxy host");
DEFINE_string(proxy_userpasswd, "",
"Proxy username/password in user:pass format.");
bool CheckForRequiredFlagsOrDie() {
string error_text = "";
if (FLAGS_product_name.empty()) {
error_text.append("\nProduct name must be specified.");
}
if (FLAGS_product_version.empty()) {
error_text.append("\nProduct version must be specified.");
}
if (FLAGS_client_id.empty()) {
error_text.append("\nClient ID must be specified.");
}
if (FLAGS_minidump_path.empty()) {
error_text.append("\nMinidump pathname must be specified.");
}
if (!error_text.empty()) {
std::cout << error_text;
return false;
}
return true;
}
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
if (!CheckForRequiredFlagsOrDie()) {
return 1;
}
google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name,
FLAGS_product_version,
FLAGS_client_id,
FLAGS_ptime,
FLAGS_ctime,
FLAGS_email,
FLAGS_comments,
FLAGS_minidump_path,
FLAGS_crash_server,
FLAGS_proxy_host,
FLAGS_proxy_userpasswd);
g.Upload();
}

File diff suppressed because it is too large Load Diff

View File

@@ -41,10 +41,6 @@
// modify the default behavior to suit your needs and wants and
// desires.
// A service name associated with the original bootstrap parent port, saved in
// OnDemandServer and restored in Inspector.
#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent"
typedef void *BreakpadRef;
#ifdef __cplusplus
@@ -54,7 +50,46 @@ extern "C" {
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include "BreakpadDefines.h"
// Keys for configuration file
#define kReporterMinidumpDirectoryKey "MinidumpDir"
#define kReporterMinidumpIDKey "MinidumpID"
// 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
// this crash or forward it along.
@@ -75,19 +110,16 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// Key: Value:
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
// This one is used as the key to identify
// the product when uploading. Falls back to
// CFBundleName if not specified.
// the product when uploading
// REQUIRED
//
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
// name for the product when the crash_sender
// pops up UI for the user. Falls back first to
// CFBundleDisplayName and then to
// pops up UI for the user. Falls back to
// BREAKPAD_PRODUCT if not specified.
//
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
// as metadata for crash report. Falls back to
// CFBundleVersion if not specified.
// as metadata for crash report
// REQUIRED
//
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
@@ -186,15 +218,13 @@ typedef bool (*BreakpadFilterCallback)(int exception_type,
// completeness. They are calculated by Breakpad during initialization &
// crash-dump generation, or entered in by the user.
//
// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the
// process started
// BREAKPAD_PROCESS_START_TIME The time the process started.
//
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
// process crashed.
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
//
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process
// has been running. This parameter is not
// set until the crash-dump-generation phase.
// BREAKPAD_PROCESS_UP_TIME The total time the process has been
// running. This parameter is not set
// until the crash-dump-generation phase.
//
// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the
// parameter dictionary correspond to log

View File

@@ -39,25 +39,22 @@
#define DEBUGLOG if (gDebugLog) fprintf
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
#import "client/mac/Framework/Breakpad.h"
#import <Foundation/Foundation.h>
#import <sys/stat.h>
#import <sys/sysctl.h>
#import "common/mac/MachIPC.h"
#import "common/mac/SimpleStringDictionary.h"
#import "client/mac/crash_generation/Inspector.h"
#import "client/mac/handler/exception_handler.h"
#import "client/mac/Framework/Breakpad.h"
#import "client/mac/Framework/OnDemandServer.h"
#import "client/mac/handler/protected_memory_allocator.h"
#import "common/mac/MachIPC.h"
#import "common/mac/SimpleStringDictionary.h"
#import <sys/stat.h>
#import <sys/sysctl.h>
#import <Foundation/Foundation.h>
using google_breakpad::KeyValueEntry;
using google_breakpad::MachPortSender;
using google_breakpad::MachReceiveMessage;
using google_breakpad::MachSendMessage;
using google_breakpad::ReceivePort;
using google_breakpad::SimpleStringDictionary;
using google_breakpad::SimpleStringDictionaryIterator;
@@ -167,7 +164,7 @@ class Breakpad {
: handler_(NULL),
config_params_(NULL),
send_and_exit_(true),
filter_callback_(NULL),
filter_callback_(NULL),
filter_callback_context_(NULL) {
inspector_path_[0] = 0;
}
@@ -268,7 +265,7 @@ bool Breakpad::ExceptionHandlerDirectCallback(void *context,
//=============================================================================
#pragma mark -
#include <dlfcn.h>
#include <mach-o/dyld.h>
//=============================================================================
// Returns the pathname to the Resources directory for this version of
@@ -289,21 +286,28 @@ NSString * GetResourcePath() {
//
// Get the pathname to the code which contains this function
Dl_info info;
if (dladdr((const void*)GetResourcePath, &info) != 0) {
NSFileManager *filemgr = [NSFileManager defaultManager];
NSString *filePath =
[filemgr stringWithFileSystemRepresentation:info.dli_fname
length:strlen(info.dli_fname)];
NSString *bundlePath = [filePath stringByDeletingLastPathComponent];
// The "Resources" directory should be in the same directory as the
// executable code, since that's how the Breakpad framework is built.
resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"];
void *address = nil;
NSModule module = nil;
_dyld_lookup_and_bind_fully("_GetResourcePath",
&address,
&module);
if (module && address) {
const char* moduleName = NSNameOfModule(module);
if (moduleName) {
// The "Resources" directory should be in the same directory as the
// executable code, since that's how the Breakpad framework is built.
resourcePath = [NSString stringWithUTF8String:moduleName];
resourcePath = [resourcePath stringByDeletingLastPathComponent];
resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"];
} else {
DEBUGLOG(stderr, "Missing moduleName\n");
}
} else {
DEBUGLOG(stderr, "Could not find GetResourcePath\n");
// fallback plan
NSBundle *bundle =
[NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
[NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"];
resourcePath = [bundle resourcePath];
}
@@ -367,10 +371,9 @@ bool Breakpad::Initialize(NSDictionary *parameters) {
// Create the handler (allocating it in our special protected pool)
handler_ =
new (gBreakpadAllocator->Allocate(
sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
Breakpad::ExceptionHandlerDirectCallback, this, true);
new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler)))
google_breakpad::ExceptionHandler(
Breakpad::ExceptionHandlerDirectCallback, this, true);
return true;
}
@@ -400,23 +403,22 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL];
NSString *inspectorPathString =
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION];
NSString *reporterPathString =
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION];
NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT];
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES];
NSString *logFileTailSize =
[parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE];
NSString *requestUserText =
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS];
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL];
NSString *vendor =
[parameters objectForKey:@BREAKPAD_VENDOR];
[parameters objectForKey:@BREAKPAD_VENDOR];
NSString *dumpSubdirectory =
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
NSDictionary *serverParameters =
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
NSDictionary *serverParameters =
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
// These may have been set above as user prefs, which take priority.
if (!skipConfirm) {
@@ -429,12 +431,8 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
if (!product)
product = [parameters objectForKey:@"CFBundleName"];
if (!display) {
display = [parameters objectForKey:@"CFBundleDisplayName"];
if (!display) {
display = product;
}
}
if (!display)
display = product;
if (!version)
version = [parameters objectForKey:@"CFBundleVersion"];
@@ -514,10 +512,8 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
// Find Reporter.
if (!reporterPathString) {
reporterPathString =
[resourcePath
stringByAppendingPathComponent:@"crash_report_sender.app"];
reporterPathString =
[[NSBundle bundleWithPath:reporterPathString] executablePath];
[resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"];
reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath];
}
// Verify that there is a Reporter application.
@@ -562,9 +558,9 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]);
dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]);
dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION,
[inspectorPathString fileSystemRepresentation]);
[inspectorPathString fileSystemRepresentation]);
dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION,
[reporterPathString fileSystemRepresentation]);
[reporterPathString fileSystemRepresentation]);
dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE,
[logFileTailSize UTF8String]);
dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS,
@@ -573,11 +569,11 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
[dumpSubdirectory UTF8String]);
struct timeval tv;
gettimeofday(&tv, NULL);
char timeStartedString[32];
sprintf(timeStartedString, "%zd", tv.tv_sec);
sprintf(timeStartedString, "%d", tv.tv_sec);
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME,
timeStartedString);
@@ -595,7 +591,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
// For each key-value pair, call BreakpadAddUploadParameter()
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
NSString *aParameter;
while ((aParameter = [keyEnumerator nextObject])) {
while (aParameter = [keyEnumerator nextObject]) {
BreakpadAddUploadParameter(this, aParameter,
[serverParameters objectForKey:aParameter]);
}
@@ -604,7 +600,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
}
//=============================================================================
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
// We allow nil values. This is the same as removing the keyvalue.
if (!config_params_ || !key)
return;
@@ -613,7 +609,7 @@ void Breakpad::SetKeyValue(NSString *key, NSString *value) {
}
//=============================================================================
NSString *Breakpad::KeyValue(NSString *key) {
NSString * Breakpad::KeyValue(NSString *key) {
if (!config_params_ || !key)
return nil;
@@ -622,24 +618,25 @@ NSString *Breakpad::KeyValue(NSString *key) {
}
//=============================================================================
void Breakpad::RemoveKeyValue(NSString *key) {
if (!config_params_ || !key) return;
void Breakpad::RemoveKeyValue(NSString *key) {
if (!config_params_ || !key)
return;
config_params_->RemoveKey([key UTF8String]);
}
//=============================================================================
void Breakpad::GenerateAndSendReport() {
void Breakpad::GenerateAndSendReport() {
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES");
HandleException(0, 0, 0, mach_thread_self());
HandleException(0, 0, 0, mach_thread_self());
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO");
}
//=============================================================================
bool Breakpad::HandleException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread) {
bool Breakpad::HandleException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread) {
DEBUGLOG(stderr, "Breakpad: an exception occurred\n");
if (filter_callback_) {
@@ -715,7 +712,8 @@ bool Breakpad::HandleException(int exception_type,
// If we don't want any forwarding, return true here to indicate that we've
// processed things as much as we want.
if (send_and_exit_) return true;
if (send_and_exit_)
return true;
return false;
}
@@ -741,67 +739,73 @@ BreakpadRef BreakpadCreate(NSDictionary *parameters) {
// since once it does its allocations and locks the memory, smashes to itself
// don't affect anything we care about.
gMasterAllocator =
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
gKeyValueAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
// Create a mutex for use in accessing the SimpleStringDictionary
int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
if (mutexResult == 0) {
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
// Let's round up to the nearest page size.
//
int breakpad_pool_size = 4096;
/*
sizeof(Breakpad)
+ sizeof(google_breakpad::ExceptionHandler)
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
*/
gBreakpadAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(breakpad_pool_size);
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Breakpad *breakpad = Breakpad::Create(parameters);
if (breakpad) {
// Make read-only to protect against memory smashers
gMasterAllocator->Protect();
gKeyValueAllocator->Protect();
gBreakpadAllocator->Protect();
// Can uncomment this line to figure out how much space was actually
// allocated using this allocator
// printf("gBreakpadAllocator allocated size = %d\n",
// gBreakpadAllocator->GetAllocatedSize() );
[pool release];
return (BreakpadRef)breakpad;
}
[pool release];
if (mutexResult != 0) {
throw mutexResult; // caught down below
}
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
// Let's round up to the nearest page size.
//
int breakpad_pool_size = 4096;
/*
sizeof(Breakpad)
+ sizeof(google_breakpad::ExceptionHandler)
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
*/
gBreakpadAllocator =
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
ProtectedMemoryAllocator(breakpad_pool_size);
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Breakpad *breakpad = Breakpad::Create(parameters);
if (breakpad) {
// Make read-only to protect against memory smashers
gMasterAllocator->Protect();
gKeyValueAllocator->Protect();
gBreakpadAllocator->Protect();
} else {
[pool release];
#ifdef __EXCEPTIONS
throw(-1);
#else
return NULL;
#endif
}
// Can uncomment this line to figure out how much space was actually
// allocated using this allocator
// printf("gBreakpadAllocator allocated size = %d\n",
// gBreakpadAllocator->GetAllocatedSize() );
[pool release];
return (BreakpadRef)breakpad;
} catch(...) { // don't let exceptions leave this C API
fprintf(stderr, "BreakpadCreate() : error\n");
}
if (gKeyValueAllocator) {
gKeyValueAllocator->~ProtectedMemoryAllocator();
gKeyValueAllocator = NULL;
}
if (gKeyValueAllocator) {
gKeyValueAllocator->~ProtectedMemoryAllocator();
gKeyValueAllocator = NULL;
}
if (gBreakpadAllocator) {
gBreakpadAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator = NULL;
}
if (gBreakpadAllocator) {
gBreakpadAllocator->~ProtectedMemoryAllocator();
gBreakpadAllocator = NULL;
delete gMasterAllocator;
gMasterAllocator = NULL;
}
delete gMasterAllocator;
gMasterAllocator = NULL;
return NULL;
}

View File

@@ -27,12 +27,13 @@
// (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 <mach/mach.h>
#include <servers/bootstrap.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#import <iostream>
#import <mach/mach.h>
#import <servers/bootstrap.h>
#import <stdio.h>
#import <stdlib.h>
#import <sys/stat.h>
#import <unistd.h>
//==============================================================================
// class OnDemandServer :

View File

@@ -29,23 +29,11 @@
#import "OnDemandServer.h"
#import "Breakpad.h"
#include "common/mac/bootstrap_compat.h"
#if DEBUG
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
#if defined(MAC_OS_X_VERSION_10_5) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ );
#else
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
PRINT_MACH_RESULT(result_, message_)
#endif
#else
#define PRINT_MACH_RESULT(result_, message_)
#define PRINT_BOOTSTRAP_RESULT(result_, message_)
#endif
//==============================================================================
@@ -79,66 +67,34 @@ kern_return_t OnDemandServer::Initialize(const char *server_command,
bool unregister_on_cleanup) {
unregister_on_cleanup_ = unregister_on_cleanup;
mach_port_t self_task = mach_task_self();
kern_return_t kr =
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) {
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(): ");
PRINT_MACH_RESULT(kr, "bootstrap_create_server() : ");
return kr;
}
strlcpy(service_name_, service_name, sizeof(service_name_));
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// Create a service called service_name, and return send rights to
// that port in service_port_.
kr = bootstrap_create_service(server_port_,
const_cast<char*>(service_name),
&service_port_);
#pragma clang diagnostic pop
if (kr != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): ");
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "bootstrap_create_service() : ");
// perhaps the service has already been created - try to look it up
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_);
if (kr != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): ");
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "bootstrap_look_up() : ");
Unregister(); // clean up server port
return kr;
}
@@ -160,7 +116,7 @@ void OnDemandServer::LaunchOnDemand() {
// and holding on to this port delays launching until the current process
// exits!
mach_port_deallocate(mach_task_self(), server_port_);
server_port_ = MACH_PORT_DEAD;
server_port_ = NULL;
// Now, the service is still registered and all we need to do is send
// a mach message to the service port in order to launch the server.
@@ -175,9 +131,9 @@ void OnDemandServer::Unregister() {
if (server_port_ != MACH_PORT_NULL) {
// unregister the service
kern_return_t kr = breakpad::BootstrapRegister(server_port_,
service_name_,
MACH_PORT_NULL);
kern_return_t kr = bootstrap_register(server_port_,
service_name_,
MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : ");

View File

@@ -1,83 +0,0 @@
// 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

View File

@@ -1,190 +0,0 @@
// 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 "client/mac/crash_generation/ConfigFile.h"
#import <Foundation/Foundation.h>
#include <stdio.h>
#include <sys/time.h>
#import "client/apple/Framework/BreakpadDefines.h"
#import "common/mac/SimpleStringDictionary.h"
#import "GTMDefines.h"
#define VERBOSE 0
#if VERBOSE
bool gDebugLog = true;
#else
bool gDebugLog = false;
#endif
#define DEBUGLOG if (gDebugLog) fprintf
namespace google_breakpad {
//=============================================================================
BOOL EnsureDirectoryPathExists(NSString *dirPath) {
NSFileManager *mgr = [NSFileManager defaultManager];
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
return [mgr createDirectoryAtPath:dirPath
withIntermediateDirectories:YES
attributes:attrs
error:nil];
}
//=============================================================================
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));
}
//=============================================================================
BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) {
// Set process uptime parameter
struct timeval tv;
gettimeofday(&tv, NULL);
char processUptimeString[32], processCrashtimeString[32];
// 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;
// Store the uptime in milliseconds.
sprintf(processUptimeString, "%llu",
static_cast<unsigned long long int>(processUptime) * 1000);
if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString))
return false;
}
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME,
processCrashtimeString);
}
//=============================================================================
void ConfigFile::WriteFile(const char* directory,
const SimpleStringDictionary *configurationParameters,
const char *dump_dir,
const char *minidump_id) {
assert(config_file_ == -1);
// Open and write out configuration file preamble
if (directory) {
snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX",
directory);
} else {
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;
}
else {
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
}
has_created_file_ = true;
// Add the minidump dir
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
AppendConfigString(kReporterMinidumpIDKey, minidump_id);
// Write out the configuration parameters
BOOL result = YES;
const SimpleStringDictionary &dictionary = *configurationParameters;
const KeyValueEntry *entry = NULL;
SimpleStringDictionaryIterator iter(dictionary);
while ((entry = iter.Next())) {
DEBUGLOG(stderr,
"config: (%s) -> (%s)\n",
entry->GetKey(),
entry->GetValue());
result = AppendConfigString(entry->GetKey(), entry->GetValue());
if (!result)
break;
}
AppendCrashTimeParameters(
configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME));
close(config_file_);
config_file_ = -1;
}
} // namespace google_breakpad

View File

@@ -33,11 +33,10 @@
#import "common/mac/SimpleStringDictionary.h"
#import <Foundation/Foundation.h>
#include <mach/mach.h>
#import "client/mac/crash_generation/ConfigFile.h"
#import "client/mac/handler/minidump_generator.h"
#define VERBOSE 0
extern bool gDebugLog;
#define DEBUGLOG if (gDebugLog) fprintf
@@ -79,10 +78,52 @@ using google_breakpad::MinidumpGenerator;
namespace google_breakpad {
static BOOL EnsureDirectoryPathExists(NSString *dirPath);
//=============================================================================
class ConfigFile {
public:
ConfigFile() {
config_file_ = -1;
config_file_path_[0] = 0;
has_created_file_ = false;
};
~ConfigFile() {
};
void WriteFile(const SimpleStringDictionary *configurationParameters,
const char *dump_dir,
const char *minidump_id);
const char *GetFilePath() { return config_file_path_; }
void Unlink() {
if (config_file_ != -1)
unlink(config_file_path_);
config_file_ = -1;
}
private:
BOOL WriteData(const void *data, size_t length);
BOOL AppendConfigData(const char *key,
const void *data,
size_t length);
BOOL AppendConfigString(const char *key,
const char *value);
int config_file_; // descriptor for config file
char config_file_path_[PATH_MAX]; // Path to configuration file
bool has_created_file_;
};
//=============================================================================
class MinidumpLocation {
public:
MinidumpLocation(NSString *minidumpDir) {
MinidumpLocation(const NSString *minidumpDir) {
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
assert(minidumpDir);
if (!EnsureDirectoryPathExists(minidumpDir)) {
@@ -122,18 +163,6 @@ class Inspector {
void Inspect(const char *receive_port_name);
private:
// The Inspector is invoked with its bootstrap port set to the bootstrap
// subset established in OnDemandServer.mm OnDemandServer::Initialize.
// For proper communication with the system, the sender (which will inherit
// the Inspector's bootstrap port) needs the per-session bootstrap namespace
// available directly in its bootstrap port. OnDemandServer stashed this
// port into the subset namespace under a special name. ResetBootstrapPort
// recovers this port and switches this task to use it as its own bootstrap
// (ensuring that children like the sender will inherit it), and saves the
// subset in bootstrap_subset_port_ for use by ServiceCheckIn and
// ServiceCheckOut.
kern_return_t ResetBootstrapPort();
kern_return_t ServiceCheckIn(const char *receive_port_name);
kern_return_t ServiceCheckOut(const char *receive_port_name);
@@ -143,9 +172,7 @@ class Inspector {
kern_return_t SendAcknowledgement();
void LaunchReporter(const char *inConfigFilePath);
// The bootstrap port in which the inspector is registered and into which it
// must check in.
mach_port_t bootstrap_subset_port_;
void SetCrashTimeParameters();
mach_port_t service_rcv_port_;

View File

@@ -31,10 +31,10 @@
#include <cstdio>
#include <iostream>
#include <servers/bootstrap.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/time.h>
#import "client/mac/crash_generation/Inspector.h"
@@ -43,22 +43,165 @@
#import "common/mac/SimpleStringDictionary.h"
#import "common/mac/MachIPC.h"
#include "common/mac/bootstrap_compat.h"
#import "GTMDefines.h"
#import <Foundation/Foundation.h>
#if VERBOSE
bool gDebugLog = true;
#else
bool gDebugLog = false;
#endif
namespace google_breakpad {
//=============================================================================
void Inspector::Inspect(const char *receive_port_name) {
kern_return_t result = ResetBootstrapPort();
if (result != KERN_SUCCESS) {
return;
static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
NSFileManager *mgr = [NSFileManager defaultManager];
// If we got a relative path, prepend the current directory
if (![dirPath isAbsolutePath])
dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath];
NSString *path = dirPath;
// Ensure that no file exists within the path which would block creation
while (1) {
BOOL isDir;
if ([mgr fileExistsAtPath:path isDirectory:&isDir]) {
if (isDir)
break;
return NO;
}
path = [path stringByDeletingLastPathComponent];
}
result = ServiceCheckIn(receive_port_name);
// 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];
unsigned count = [components count];
// Rebuild the path one component at a time
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
path = common;
for (unsigned 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;
}
else {
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_);
}
has_created_file_ = true;
// Add the minidump dir
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
AppendConfigString(kReporterMinidumpIDKey, minidump_id);
// Write out the configuration parameters
BOOL result = YES;
const SimpleStringDictionary &dictionary = *configurationParameters;
const KeyValueEntry *entry = NULL;
SimpleStringDictionaryIterator iter(dictionary);
while ((entry = iter.Next())) {
DEBUGLOG(stderr,
"config: (%s) -> (%s)\n",
entry->GetKey(),
entry->GetValue());
result = AppendConfigString(entry->GetKey(), entry->GetValue());
if (!result)
break;
}
close(config_file_);
config_file_ = -1;
}
//=============================================================================
void Inspector::Inspect(const char *receive_port_name) {
kern_return_t result = ServiceCheckIn(receive_port_name);
if (result == KERN_SUCCESS) {
result = ReadMessages();
@@ -95,56 +238,11 @@ void Inspector::Inspect(const char *receive_port_name) {
}
}
//=============================================================================
kern_return_t Inspector::ResetBootstrapPort() {
// A reasonable default, in case anything fails.
bootstrap_subset_port_ = bootstrap_port;
mach_port_t self_task = mach_task_self();
kern_return_t kr = task_get_bootstrap_port(self_task,
&bootstrap_subset_port_);
if (kr != KERN_SUCCESS) {
NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)",
mach_error_string(kr), kr);
return kr;
}
mach_port_t bootstrap_parent_port;
kr = bootstrap_look_up(bootstrap_subset_port_,
const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
&bootstrap_parent_port);
if (kr != BOOTSTRAP_SUCCESS) {
NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)",
#if defined(MAC_OS_X_VERSION_10_5) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
bootstrap_strerror(kr),
#else
mach_error_string(kr),
#endif
kr);
return kr;
}
kr = task_set_bootstrap_port(self_task, bootstrap_parent_port);
if (kr != KERN_SUCCESS) {
NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)",
mach_error_string(kr), kr);
return kr;
}
// Some things access the bootstrap port through this global variable
// instead of calling task_get_bootstrap_port.
bootstrap_port = bootstrap_parent_port;
return KERN_SUCCESS;
}
//=============================================================================
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
// We need to get the mach port representing this service, so we can
// get information from the crashed process.
kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_,
kern_return_t kr = bootstrap_check_in(bootstrap_port,
(char*)receive_port_name,
&service_rcv_port_);
@@ -175,9 +273,9 @@ kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
}
// Unregister the service associated with the receive port.
kr = breakpad::BootstrapRegister(bootstrap_subset_port_,
(char*)receive_port_name,
MACH_PORT_NULL);
kr = bootstrap_register(bootstrap_port,
(char*)receive_port_name,
MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
@@ -231,12 +329,12 @@ kern_return_t Inspector::ReadMessages() {
// we are expected to read.
// Read each key/value pair, one mach message per key/value pair.
for (unsigned int i = 0; i < info.parameter_count; ++i) {
MachReceiveMessage parameter_message;
result = receive_port.WaitForMessage(&parameter_message, 1000);
MachReceiveMessage message;
result = receive_port.WaitForMessage(&message, 1000);
if(result == KERN_SUCCESS) {
KeyValueMessageData &key_value_data =
(KeyValueMessageData&)*parameter_message.GetData();
(KeyValueMessageData&)*message.GetData();
// If we get a blank key, make sure we don't increment the
// parameter count; in some cases (notably on-demand generation
// many times in a short period of time) caused the Mach IPC
@@ -263,6 +361,30 @@ 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, "%d", processUptime);
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
}
sprintf(processCrashtimeString, "%d", tv.tv_sec);
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
processCrashtimeString);
}
bool Inspector::InspectTask() {
// keep the task quiet while we're looking at it
task_suspend(remote_task_);
@@ -273,6 +395,7 @@ bool Inspector::InspectTask() {
const char *minidumpDirectory =
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
SetCrashTimeParameters();
// If the client app has not specified a minidump directory,
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
@@ -323,8 +446,7 @@ bool Inspector::InspectTask() {
[minidumpPath UTF8String]);
config_file_.WriteFile( 0,
&config_params_,
config_file_.WriteFile( &config_params_,
minidumpLocation.GetPath(),
minidumpLocation.GetID());

View File

@@ -1,47 +0,0 @@
// Copyright (c) 2010 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_CRASH_GENERATION_CLIENT_INFO_H_
#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
namespace google_breakpad {
class ClientInfo {
public:
explicit ClientInfo(pid_t pid) : pid_(pid) {}
pid_t pid() const { return pid_; }
private:
pid_t pid_;
};
} // namespace google_breakpad
#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_

View File

@@ -1,72 +0,0 @@
// Copyright (c) 2010 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/mac/crash_generation/crash_generation_client.h"
#include "client/mac/crash_generation/crash_generation_server.h"
#include "common/mac/MachIPC.h"
namespace google_breakpad {
bool CrashGenerationClient::RequestDumpForException(
int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread) {
// The server will send a message to this port indicating that it
// has finished its work.
ReceivePort acknowledge_port;
MachSendMessage message(kDumpRequestMessage);
message.AddDescriptor(mach_task_self()); // this task
message.AddDescriptor(crashing_thread); // crashing thread
message.AddDescriptor(mach_thread_self()); // handler thread
message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
ExceptionInfo info;
info.exception_type = exception_type;
info.exception_code = exception_code;
info.exception_subcode = exception_subcode;
message.SetData(&info, sizeof(info));
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs);
if (result != KERN_SUCCESS)
return false;
// Give the server slightly longer to reply since it has to
// inspect this task and write the minidump.
const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000;
MachReceiveMessage acknowledge_message;
result = acknowledge_port.WaitForMessage(&acknowledge_message,
kReceiveTimeoutMs);
return result == KERN_SUCCESS;
}
} // namespace google_breakpad

View File

@@ -1,65 +0,0 @@
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include "common/mac/MachIPC.h"
namespace google_breakpad {
class CrashGenerationClient {
public:
explicit CrashGenerationClient(const char* mach_port_name)
: sender_(mach_port_name) {
}
// Request the crash server to generate a dump.
//
// Return true if the dump was successful; false otherwise.
bool RequestDumpForException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t crashing_thread);
bool RequestDump() {
return RequestDumpForException(0, 0, 0, MACH_PORT_NULL);
}
private:
MachPortSender sender_;
// Prevent copy construction and assignment.
CrashGenerationClient(const CrashGenerationClient&);
CrashGenerationClient& operator=(const CrashGenerationClient&);
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@@ -1,164 +0,0 @@
// Copyright (c) 2010 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/mac/crash_generation/crash_generation_server.h"
#include "client/mac/crash_generation/client_info.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/scoped_task_suspend-inl.h"
namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const char *mach_port_name,
FilterCallback filter,
void *filter_context,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
void *exit_context,
bool generate_dumps,
const std::string &dump_path)
: dump_callback_(dump_callback),
filter_(filter),
filter_context_(filter_context),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
generate_dumps_(generate_dumps),
dump_dir_(dump_path.empty() ? "/tmp" : dump_path),
started_(false),
receive_port_(mach_port_name),
mach_port_name_(mach_port_name) {
}
CrashGenerationServer::~CrashGenerationServer() {
if (started_)
Stop();
}
bool CrashGenerationServer::Start() {
int thread_create_result = pthread_create(&server_thread_, NULL,
&WaitForMessages, this);
started_ = thread_create_result == 0;
return started_;
}
bool CrashGenerationServer::Stop() {
if (!started_)
return false;
// Send a quit message to the background thread, and then join it.
MachPortSender sender(mach_port_name_.c_str());
MachSendMessage quit_message(kQuitMessage);
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs);
if (result == KERN_SUCCESS) {
int thread_join_result = pthread_join(server_thread_, NULL);
started_ = thread_join_result != 0;
}
return !started_;
}
// static
void *CrashGenerationServer::WaitForMessages(void *server) {
CrashGenerationServer *self =
reinterpret_cast<CrashGenerationServer*>(server);
while (self->WaitForOneMessage()) {}
return NULL;
}
bool CrashGenerationServer::WaitForOneMessage() {
MachReceiveMessage message;
kern_return_t result = receive_port_.WaitForMessage(&message,
MACH_MSG_TIMEOUT_NONE);
if (result == KERN_SUCCESS) {
switch (message.GetMessageID()) {
case kDumpRequestMessage: {
ExceptionInfo &info = (ExceptionInfo &)*message.GetData();
mach_port_t remote_task = message.GetTranslatedPort(0);
mach_port_t crashing_thread = message.GetTranslatedPort(1);
mach_port_t handler_thread = message.GetTranslatedPort(2);
mach_port_t ack_port = message.GetTranslatedPort(3);
pid_t remote_pid = -1;
pid_for_task(remote_task, &remote_pid);
ClientInfo client(remote_pid);
bool result;
std::string dump_path;
if (generate_dumps_ && (!filter_ || filter_(filter_context_))) {
ScopedTaskSuspend suspend(remote_task);
MinidumpGenerator generator(remote_task, handler_thread);
dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL);
if (info.exception_type && info.exception_code) {
generator.SetExceptionInformation(info.exception_type,
info.exception_code,
info.exception_subcode,
crashing_thread);
}
result = generator.Write(dump_path.c_str());
} else {
result = true;
}
if (result && dump_callback_) {
dump_callback_(dump_context_, client, dump_path);
}
// TODO(ted): support a way for the client to send additional data,
// perhaps with a callback so users of the server can read the data
// themselves?
if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) {
MachPortSender sender(ack_port);
MachSendMessage ack_message(kAcknowledgementMessage);
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000;
sender.SendMessage(ack_message, kSendTimeoutMs);
}
if (exit_callback_) {
exit_callback_(exit_context_, client);
}
break;
}
case kQuitMessage:
return false;
}
} else { // result != KERN_SUCCESS
return false;
}
return true;
}
} // namespace google_breakpad

View File

@@ -1,150 +0,0 @@
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#include <stdint.h>
#include <string>
#include "common/mac/MachIPC.h"
namespace google_breakpad {
class ClientInfo;
// Messages the server can read via its mach port
enum {
kDumpRequestMessage = 1,
kAcknowledgementMessage = 2,
kQuitMessage = 3
};
// Exception details sent by the client when requesting a dump.
struct ExceptionInfo {
int32_t exception_type;
int32_t exception_code;
int32_t exception_subcode;
};
class CrashGenerationServer {
public:
// WARNING: callbacks may be invoked on a different thread
// than that which creates the CrashGenerationServer. They must
// be thread safe.
typedef void (*OnClientDumpRequestCallback)(void *context,
const ClientInfo &client_info,
const std::string &file_path);
typedef void (*OnClientExitingCallback)(void *context,
const ClientInfo &client_info);
// If a FilterCallback returns false, the dump will not be written.
typedef bool (*FilterCallback)(void *context);
// Create an instance with the given parameters.
//
// mach_port_name: Named server port to listen on.
// filter: Callback for a client to cancel writing a dump.
// filter_context: Context for the filter callback.
// dump_callback: Callback for a client crash dump request.
// dump_context: Context for client crash dump request callback.
// exit_callback: Callback for client process exit.
// exit_context: Context for client exit callback.
// generate_dumps: Whether to automatically generate dumps.
// Client code of this class might want to generate dumps explicitly
// in the crash dump request callback. In that case, false can be
// passed for this parameter.
// dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const char *mach_port_name,
FilterCallback filter,
void *filter_context,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
void *exit_context,
bool generate_dumps,
const std::string &dump_path);
~CrashGenerationServer();
// Perform initialization steps needed to start listening to clients.
//
// Return true if initialization is successful; false otherwise.
bool Start();
// Stop the server.
bool Stop();
private:
// Return a unique filename at which a minidump can be written.
bool MakeMinidumpFilename(std::string &outFilename);
// Loop reading client messages and responding to them until
// a quit message is received.
static void *WaitForMessages(void *server);
// Wait for a single client message and respond to it. Returns false
// if a quit message was received or if an error occurred.
bool WaitForOneMessage();
FilterCallback filter_;
void *filter_context_;
OnClientDumpRequestCallback dump_callback_;
void *dump_context_;
OnClientExitingCallback exit_callback_;
void *exit_context_;
bool generate_dumps_;
std::string dump_dir_;
bool started_;
// The mach port that receives requests to dump from child processes.
ReceivePort receive_port_;
// The name of the mach port. Stored so the Stop method can message
// the background thread to shut it down.
std::string mach_port_name_;
// The thread that waits on the receive port.
pthread_t server_thread_;
// Disable copy constructor and operator=.
CrashGenerationServer(const CrashGenerationServer&);
CrashGenerationServer& operator=(const CrashGenerationServer&);
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,258 @@
#ifndef _exc_user_
#define _exc_user_
/* Module exc */
#include <string.h>
#include <mach/ndr.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/port.h>
#ifdef AUTOTEST
#ifndef FUNCTION_PTR_T
#define FUNCTION_PTR_T
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
typedef struct {
char *name;
function_ptr_t function;
} function_table_entry;
typedef function_table_entry *function_table_t;
#endif /* FUNCTION_PTR_T */
#endif /* AUTOTEST */
#ifndef exc_MSG_COUNT
#define exc_MSG_COUNT 3
#endif /* exc_MSG_COUNT */
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#ifdef __BeforeMigUserHeader
__BeforeMigUserHeader
#endif /* __BeforeMigUserHeader */
#include <sys/cdefs.h>
__BEGIN_DECLS
/* Routine exception_raise */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise
(
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
);
/* Routine exception_raise_state */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
/* Routine exception_raise_state_identity */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t 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
);
__END_DECLS
/********************** Caution **************************/
/* The following data types should be used to calculate */
/* maximum message sizes only. The actual message may be */
/* smaller, and the position of the arguments within the */
/* message layout may vary from what is presented here. */
/* For example, if any of the arguments are variable- */
/* sized, and less than the maximum is sent, the data */
/* will be packed tight in the actual message to reduce */
/* the presence of holes. */
/********************** Caution **************************/
/* typedefs for all requests */
#ifndef __Request__exc_subsystem__defined
#define __Request__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
} __Request__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[144];
} __Request__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Request__exc_subsystem__defined */
/* union of all requests */
#ifndef __RequestUnion__exc_subsystem__defined
#define __RequestUnion__exc_subsystem__defined
union __RequestUnion__exc_subsystem {
__Request__exception_raise_t Request_exception_raise;
__Request__exception_raise_state_t Request_exception_raise_state;
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
/* typedefs for all replies */
#ifndef __Reply__exc_subsystem__defined
#define __Reply__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[144];
} __Reply__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Reply__exc_subsystem__defined */
/* union of all replies */
#ifndef __ReplyUnion__exc_subsystem__defined
#define __ReplyUnion__exc_subsystem__defined
union __ReplyUnion__exc_subsystem {
__Reply__exception_raise_t Reply_exception_raise;
__Reply__exception_raise_state_t Reply_exception_raise_state;
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
#ifndef subsystem_to_name_map_exc
#define subsystem_to_name_map_exc \
{ "exception_raise", 2401 },\
{ "exception_raise_state", 2402 },\
{ "exception_raise_state_identity", 2403 }
#endif
#ifdef __AfterMigUserHeader
__AfterMigUserHeader
#endif /* __AfterMigUserHeader */
#endif /* _exc_user_ */

View File

@@ -54,7 +54,7 @@
*/
/*
/* nealsid:
* This file was copied from libc/gen/nlist.c from Darwin's source code
* The version of nlist used as a base is from 10.5.2, libc-498
* http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
@@ -62,23 +62,24 @@
* The full tarball is at:
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
*
* I've modified it to be compatible with 64-bit images.
* I've modified it to be compatible with 64-bit images. However,
* 32-bit compatibility has not been retained.
*/
#include "breakpad_nlist_64.h"
#ifdef __LP64__
#include <CoreFoundation/CoreFoundation.h>
#include <fcntl.h>
#include <mach-o/nlist.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <mach/mach.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <TargetConditionals.h>
#include <unistd.h>
#include "breakpad_nlist_64.h"
#include <TargetConditionals.h>
#include <stdio.h>
#include <mach/mach.h>
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */
/*
@@ -107,77 +108,44 @@ struct exec {
#define N_SYMOFF(x) \
(N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize)
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
template<typename T>
struct MachBits {};
typedef struct nlist nlist32;
typedef struct nlist_64 nlist64;
template<>
struct MachBits<nlist32> {
typedef mach_header mach_header_type;
typedef uint32_t word_type;
static const uint32_t magic = MH_MAGIC;
};
template<>
struct MachBits<nlist64> {
typedef mach_header_64 mach_header_type;
typedef uint64_t word_type;
static const uint32_t magic = MH_MAGIC_64;
};
template<typename nlist_type>
int
__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type);
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames);
/*
* nlist - retreive attributes from name list (string table version)
*/
template <typename nlist_type>
int breakpad_nlist_common(const char *name,
nlist_type *list,
const char **symbolNames,
cpu_type_t cpu_type) {
int fd = open(name, O_RDONLY, 0);
int
breakpad_nlist_64(const char *name,
breakpad_nlist *list,
const char **symbolNames) {
int fd, n;
fd = open(name, O_RDONLY, 0);
if (fd < 0)
return -1;
int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type);
close(fd);
return n;
}
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
}
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type) {
return breakpad_nlist_common(name, list, symbolNames, cpu_type);
return (-1);
n = __breakpad_fdnlist_64(fd, list, symbolNames);
(void)close(fd);
return (n);
}
/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */
template<typename nlist_type>
int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
cpu_type_t cpu_type) {
typedef typename MachBits<nlist_type>::mach_header_type mach_header_type;
typedef typename MachBits<nlist_type>::word_type word_type;
int
__breakpad_fdnlist_64(int fd, breakpad_nlist *list, const char **symbolNames) {
register breakpad_nlist *p, *q;
breakpad_nlist space[BUFSIZ/sizeof (breakpad_nlist)];
const uint32_t magic = MachBits<nlist_type>::magic;
const register char *s1, *s2;
register int n, m;
int maxlen, nreq;
off_t sa; /* symbol address */
off_t ss; /* start of strings */
struct exec buf;
unsigned arch_offset = 0;
int maxlen = 500;
int nreq = 0;
for (nlist_type* q = list;
maxlen = 500;
for (q = list, nreq = 0;
symbolNames[q-list] && symbolNames[q-list][0];
q++, nreq++) {
@@ -188,226 +156,226 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames,
q->n_un.n_strx = 0;
}
struct exec buf;
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) ||
(N_BADMAG(buf) && *((uint32_t *)&buf) != magic &&
CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
/* The following is the big-endian ppc64 check */
(*((uint32_t*)&buf)) != FAT_MAGIC)) {
return -1;
(N_BADMAG(buf) && *((long *)&buf) != MH_MAGIC &&
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
/* nealsid: The following is the big-endian ppc64 check */
(*((uint32_t*)&buf)) != FAT_MAGIC) {
return (-1);
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC ||
/* The following is the big-endian ppc64 check */
*((unsigned int *)&buf) == FAT_MAGIC) {
/* Get host info */
host_t host = mach_host_self();
unsigned i = HOST_BASIC_INFO_COUNT;
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC ||
/* nealsid: The following is the big-endian ppc64 check */
*((int*)&buf) == FAT_MAGIC) {
struct host_basic_info hbi;
struct fat_header fh;
struct fat_arch *fat_archs, *fap;
unsigned i;
host_t host;
/* Get our host info */
host = mach_host_self();
i = HOST_BASIC_INFO_COUNT;
kern_return_t kr;
if ((kr = host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return -1;
if ((kr=host_info(host, HOST_BASIC_INFO,
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) {
return (-1);
}
mach_port_deallocate(mach_task_self(), host);
/* Read in the fat header */
struct fat_header fh;
if (lseek(fd, 0, SEEK_SET) == -1) {
return -1;
}
lseek(fd, 0, SEEK_SET);
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) {
return -1;
return (-1);
}
/* Convert fat_narchs to host byte order */
fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch);
fh.nfat_arch = NXSwapBigLongToHost(fh.nfat_arch);
/* Read in the fat archs */
struct fat_arch *fat_archs =
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch));
fat_archs = (struct fat_arch *)malloc(fh.nfat_arch *
sizeof(struct fat_arch));
if (fat_archs == NULL) {
return -1;
return (-1);
}
if (read(fd, (char *)fat_archs,
sizeof(struct fat_arch) * fh.nfat_arch) !=
(ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) {
sizeof(struct fat_arch) * fh.nfat_arch) {
free(fat_archs);
return -1;
return (-1);
}
/*
* Convert archs to host byte ordering (a constraint of
* cpusubtype_getbestarch()
*/
for (unsigned i = 0; i < fh.nfat_arch; i++) {
for (i = 0; i < fh.nfat_arch; i++) {
fat_archs[i].cputype =
CFSwapInt32BigToHost(fat_archs[i].cputype);
NXSwapBigLongToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
NXSwapBigLongToHost(fat_archs[i].cpusubtype);
fat_archs[i].offset =
CFSwapInt32BigToHost(fat_archs[i].offset);
NXSwapBigLongToHost(fat_archs[i].offset);
fat_archs[i].size =
CFSwapInt32BigToHost(fat_archs[i].size);
NXSwapBigLongToHost(fat_archs[i].size);
fat_archs[i].align =
CFSwapInt32BigToHost(fat_archs[i].align);
NXSwapBigLongToHost(fat_archs[i].align);
}
struct fat_arch *fap = NULL;
for (unsigned i = 0; i < fh.nfat_arch; i++) {
if (fat_archs[i].cputype == cpu_type) {
fap = &fat_archs[i];
break;
fap = NULL;
for (i = 0; i < fh.nfat_arch; i++) {
/* nealsid: Although the original Apple code uses host_info */
/* to retrieve the CPU type, the host_info will still return */
/* CPU_TYPE_X86 even if running as an x86_64 binary. Given that */
/* this code isn't necessary on i386, I've decided to hardcode */
/* looking for a 64-bit binary */
#if TARGET_CPU_X86_64
if (fat_archs[i].cputype == CPU_TYPE_X86_64) {
#elif TARGET_CPU_PPC64
if (fat_archs[i].cputype == CPU_TYPE_POWERPC64) {
#else
#error undefined cpu!
{
#endif
fap = &fat_archs[i];
break;
}
}
}
if (!fap) {
if (!fap) {
free(fat_archs);
return (-1);
}
arch_offset = fap->offset;
free(fat_archs);
return -1;
}
arch_offset = fap->offset;
free(fat_archs);
/* Read in the beginning of the architecture-specific file */
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return -1;
}
}
off_t sa; /* symbol address */
off_t ss; /* start of strings */
register register_t n;
if (*((unsigned int *)&buf) == magic) {
if (lseek(fd, arch_offset, SEEK_SET) == -1) {
return -1;
}
mach_header_type mh;
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return -1;
/* Read in the beginning of the architecture-specific file */
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) {
return (-1);
}
}
struct load_command *load_commands =
(struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return -1;
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
(ssize_t)mh.sizeofcmds) {
free(load_commands);
return -1;
}
struct symtab_command *stp = NULL;
struct load_command *lcp = load_commands;
// iterate through all load commands, looking for
// LC_SYMTAB load command
for (uint32_t i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(word_type) != 0 ||
lcp->cmdsize <= 0 ||
(char *)lcp + lcp->cmdsize >
(char *)load_commands + mh.sizeofcmds) {
if (*((int *)&buf) == MH_MAGIC_64) {
struct mach_header_64 mh;
struct load_command *load_commands, *lcp;
struct symtab_command *stp;
long i;
lseek(fd, arch_offset, SEEK_SET);
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) {
return (-1);
}
load_commands = (struct load_command *)malloc(mh.sizeofcmds);
if (load_commands == NULL) {
return (-1);
}
if (read(fd, (char *)load_commands, mh.sizeofcmds) !=
mh.sizeofcmds) {
free(load_commands);
return -1;
return (-1);
}
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize !=
sizeof(struct symtab_command)) {
stp = NULL;
lcp = load_commands;
// nealsid:iterate through all load commands, looking for
// LC_SYMTAB load command
for (i = 0; i < mh.ncmds; i++) {
if (lcp->cmdsize % sizeof(long) != 0 ||
lcp->cmdsize <= 0 ||
(char *)lcp + lcp->cmdsize >
(char *)load_commands + mh.sizeofcmds) {
free(load_commands);
return -1;
return (-1);
}
stp = (struct symtab_command *)lcp;
break;
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize !=
sizeof(struct symtab_command)) {
free(load_commands);
return (-1);
}
stp = (struct symtab_command *)lcp;
break;
}
lcp = (struct load_command *)
((char *)lcp + lcp->cmdsize);
}
lcp = (struct load_command *)
((char *)lcp + lcp->cmdsize);
}
if (stp == NULL) {
if (stp == NULL) {
free(load_commands);
return (-1);
}
// sa points to the beginning of the symbol table
sa = stp->symoff + arch_offset;
// ss points to the beginning of the string table
ss = stp->stroff + arch_offset;
// n is the number of bytes in the symbol table
// each symbol table entry is an nlist structure
n = stp->nsyms * sizeof(breakpad_nlist);
free(load_commands);
return -1;
}
// sa points to the beginning of the symbol table
sa = stp->symoff + arch_offset;
// ss points to the beginning of the string table
ss = stp->stroff + arch_offset;
// n is the number of bytes in the symbol table
// each symbol table entry is an nlist structure
n = stp->nsyms * sizeof(nlist_type);
free(load_commands);
} else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
if (lseek(fd, sa, SEEK_SET) == -1) {
return -1;
}
// the algorithm here is to read the nlist entries in m-sized
// chunks into q. q is then iterated over. for each entry in q,
// use the string table index(q->n_un.n_strx) to read the symbol
// name, then scan the nlist entries passed in by the user(via p),
// and look for a match
while (n) {
nlist_type space[BUFSIZ/sizeof (nlist_type)];
register register_t m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
n -= m;
long savpos = lseek(fd, 0, SEEK_CUR);
if (savpos == -1) {
return -1;
else {
sa = N_SYMOFF(buf) + arch_offset;
ss = sa + buf.a_syms + arch_offset;
n = buf.a_syms;
}
for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) {
char nambuf[BUFSIZ];
if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
continue;
lseek(fd, sa, SEEK_SET);
// seek to the location in the binary where the symbol
// name is stored & read it into memory
if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) {
return -1;
}
if (read(fd, nambuf, maxlen+1) == -1) {
return -1;
}
const char *s2 = nambuf;
for (nlist_type *p = list;
symbolNames[p-list] && symbolNames[p-list][0];
p++) {
// get the symbol name the user has passed in that
// corresponds to the nlist entry that we're looking at
const char *s1 = symbolNames[p - list];
while (*s1) {
if (*s1++ != *s2++)
goto cont;
}
if (*s2)
goto cont;
p->n_value = q->n_value;
p->n_type = q->n_type;
p->n_desc = q->n_desc;
p->n_sect = q->n_sect;
p->n_un.n_strx = q->n_un.n_strx;
if (--nreq == 0)
return nreq;
// the algorithm here is to read the nlist entries in m-sized
// chunks into q. q is then iterated over. for each entry in q,
// use the string table index(q->n_un.n_strx) to read the symbol
// name, then scan the nlist entries passed in by the user(via p),
// and look for a match
while (n) {
long savpos;
m = sizeof (space);
if (n < m)
m = n;
if (read(fd, (char *)space, m) != m)
break;
cont: ;
n -= m;
savpos = lseek(fd, 0, SEEK_CUR);
for (q = space; (m -= sizeof(breakpad_nlist)) >= 0; q++) {
char nambuf[BUFSIZ];
if (q->n_un.n_strx == 0 || q->n_type & N_STAB)
continue;
// seek to the location in the binary where the symbol
// name is stored & read it into memory
lseek(fd, ss+q->n_un.n_strx, SEEK_SET);
read(fd, nambuf, maxlen+1);
s2 = nambuf;
for (p = list;
symbolNames[p-list] &&
symbolNames[p-list][0];
p++) {
// get the symbol name the user has passed in that
// corresponds to the nlist entry that we're looking at
s1 = symbolNames[p - list];
while (*s1) {
if (*s1++ != *s2++)
goto cont;
}
if (*s2)
goto cont;
p->n_value = q->n_value;
p->n_type = q->n_type;
p->n_desc = q->n_desc;
p->n_sect = q->n_sect;
p->n_un.n_strx = q->n_un.n_strx;
if (--nreq == 0)
return (nreq);
break;
cont: ;
}
}
lseek(fd, savpos, SEEK_SET);
}
if (lseek(fd, savpos, SEEK_SET) == -1) {
return -1;
}
return (nreq);
}
return nreq;
}
#endif /* __LP64__ */

View File

@@ -33,15 +33,11 @@
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
#include <mach/machine.h>
typedef struct nlist_64 breakpad_nlist;
int breakpad_nlist(const char *name,
struct nlist *list,
const char **symbolNames,
cpu_type_t cpu_type);
int breakpad_nlist(const char *name,
struct nlist_64 *list,
const char **symbolNames,
cpu_type_t cpu_type);
int
breakpad_nlist_64(const char *name,
breakpad_nlist *list,
const char **symbolNames);
#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */

View File

@@ -27,56 +27,20 @@
// (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/mac/handler/dynamic_images.h"
extern "C" { // needed to compile on Leopard
#include <mach-o/nlist.h>
#include <stdlib.h>
#include <stdio.h>
}
#include <assert.h>
#include <AvailabilityMacros.h>
#include <dlfcn.h>
#include <mach/task_info.h>
#include <sys/sysctl.h>
#include <TargetConditionals.h>
#include <algorithm>
#include <string>
#include <vector>
#include "breakpad_nlist_64.h"
#if !TARGET_OS_IPHONE
#include <CoreServices/CoreServices.h>
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
// <mach/task_info.h> in the Mac OS X 10.6 SDK.
#define TASK_DYLD_INFO 17
struct task_dyld_info {
mach_vm_address_t all_image_info_addr;
mach_vm_size_t all_image_info_size;
};
typedef struct task_dyld_info task_dyld_info_data_t;
typedef struct task_dyld_info *task_dyld_info_t;
#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
#endif
#endif // !TARGET_OS_IPHONE
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <algorithm>
#include "client/mac/handler/dynamic_images.h"
namespace google_breakpad {
using std::string;
using std::vector;
//==============================================================================
// Returns the size of the memory region containing |address| and the
// number of bytes from |address| to the end of the region.
@@ -86,7 +50,7 @@ using std::vector;
// straddle two vm regions.
//
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
const uint64_t address,
const void* address,
mach_vm_size_t *size_to_end) {
mach_vm_address_t region_base = (mach_vm_address_t)address;
mach_vm_size_t region_size;
@@ -151,8 +115,8 @@ static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
//
// Warning! This will not read any strings longer than kMaxStringLength-1
//
static string ReadTaskString(task_port_t target_task,
const uint64_t address) {
static void* ReadTaskString(task_port_t target_task,
const void* address) {
// The problem is we don't know how much to read until we know how long
// the string is. And we don't know how long the string is, until we've read
// the memory! So, we'll try to read kMaxStringLength bytes
@@ -164,140 +128,68 @@ static string ReadTaskString(task_port_t target_task,
mach_vm_size_t size_to_read =
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;
vector<uint8_t> bytes;
if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) !=
KERN_SUCCESS)
return string();
return string(reinterpret_cast<const char*>(&bytes[0]));
kern_return_t kr;
return ReadTaskMemory(target_task, address, size_to_read, &kr);
}
return string();
return NULL;
}
//==============================================================================
// Reads an address range from another task. The bytes read will be returned
// in bytes, which will be resized as necessary.
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes) {
// Reads an address range from another task. A block of memory is malloced
// and should be freed by the caller.
void* ReadTaskMemory(task_port_t target_task,
const void* address,
size_t length,
kern_return_t *kr) {
void* result = NULL;
int systemPageSize = getpagesize();
// use the negative of the page size for the mask to find the page address
mach_vm_address_t page_address = address & (-systemPageSize);
mach_vm_address_t page_address =
reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize);
mach_vm_address_t last_page_address =
(address + length + (systemPageSize - 1)) & (-systemPageSize);
(reinterpret_cast<mach_vm_address_t>(address) + length +
(systemPageSize - 1)) & (-systemPageSize);
mach_vm_size_t page_size = last_page_address - page_address;
uint8_t* local_start;
uint32_t local_length;
kern_return_t r = mach_vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
kern_return_t r;
if (r != KERN_SUCCESS)
return r;
r = mach_vm_read(target_task,
page_address,
page_size,
reinterpret_cast<vm_offset_t*>(&local_start),
&local_length);
bytes.resize(length);
memcpy(&bytes[0],
&local_start[(mach_vm_address_t)address - page_address],
length);
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
return KERN_SUCCESS;
if (kr != NULL) {
*kr = r;
}
if (r == KERN_SUCCESS) {
result = malloc(length);
if (result != NULL) {
memcpy(result,
&local_start[(mach_vm_address_t)address - page_address],
length);
}
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
}
return result;
}
#pragma mark -
//==============================================================================
// Traits structs for specializing function templates to handle
// 32-bit/64-bit Mach-O files.
struct MachO32 {
typedef mach_header mach_header_type;
typedef segment_command mach_segment_command_type;
typedef dyld_image_info32 dyld_image_info;
typedef dyld_all_image_infos32 dyld_all_image_infos;
typedef struct nlist nlist_type;
static const uint32_t magic = MH_MAGIC;
static const uint32_t segment_load_command = LC_SEGMENT;
};
struct MachO64 {
typedef mach_header_64 mach_header_type;
typedef segment_command_64 mach_segment_command_type;
typedef dyld_image_info64 dyld_image_info;
typedef dyld_all_image_infos64 dyld_all_image_infos;
typedef struct nlist_64 nlist_type;
static const uint32_t magic = MH_MAGIC_64;
static const uint32_t segment_load_command = LC_SEGMENT_64;
};
template<typename MachBits>
bool FindTextSection(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
typedef typename MachBits::mach_segment_command_type
mach_segment_command_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
if(header->magic != MachBits::magic) {
return false;
}
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
bool found_text_section = false;
bool found_dylib_id_command = false;
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (!found_text_section) {
if (cmd->cmd == MachBits::segment_load_command) {
const mach_segment_command_type *seg =
reinterpret_cast<const mach_segment_command_type *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
image.vmaddr_ = seg->vmaddr;
image.vmsize_ = seg->vmsize;
image.slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
image.slide_ =
(uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
found_text_section = true;
}
}
}
if (!found_dylib_id_command) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
image.version_ = dc->dylib.current_version;
found_dylib_id_command = true;
}
}
if (found_dylib_id_command && found_text_section) {
return true;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
return false;
}
//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryAndVersionInfo() {
breakpad_mach_header *header = GetMachHeader();
// unless we can process the header, ensure that calls to
// IsValid() will return false
vmaddr_ = 0;
@@ -305,29 +197,78 @@ void DynamicImage::CalculateMemoryAndVersionInfo() {
slide_ = 0;
version_ = 0;
// The function template above does all the real work.
if (Is64Bit())
FindTextSection<MachO64>(*this);
else
FindTextSection<MachO32>(*this);
bool foundTextSection = false;
bool foundDylibIDCommand = false;
#if __LP64__
if(header->magic != MH_MAGIC_64) {
return;
}
#else
if(header->magic != MH_MAGIC) {
return;
}
#endif
#ifdef __LP64__
const uint32_t segmentLoadCommand = LC_SEGMENT_64;
#else
const uint32_t segmentLoadCommand = LC_SEGMENT;
#endif
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
if (!foundTextSection) {
if (cmd->cmd == segmentLoadCommand) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
vmaddr_ = seg->vmaddr;
vmsize_ = seg->vmsize;
slide_ = 0;
if (seg->fileoff == 0 && seg->filesize != 0) {
slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
}
foundTextSection = true;
}
}
}
if (!foundDylibIDCommand) {
if (cmd->cmd == LC_ID_DYLIB) {
const struct dylib_command *dc =
reinterpret_cast<const struct dylib_command *>(cmd);
version_ = dc->dylib.current_version;
foundDylibIDCommand = true;
}
}
if (foundDylibIDCommand && foundTextSection) {
return;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
}
//==============================================================================
// The helper function template abstracts the 32/64-bit differences.
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image) {
typedef typename MachBits::mach_header_type mach_header_type;
const mach_header_type* header =
reinterpret_cast<const mach_header_type*>(&image.header_[0]);
return header->filetype;
}
uint32_t DynamicImage::GetFileType() {
if (Is64Bit())
return GetFileTypeFromHeader<MachO64>(*this);
return GetFileTypeFromHeader<MachO32>(*this);
void DynamicImage::Print() {
const char *path = GetFilePath();
if (!path) {
path = "(unknown)";
}
printf("%p: %s\n", GetLoadAddress(), path);
breakpad_mach_header *header = GetMachHeader();
MachHeader(*header).Print();
printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
printf("vmsize\t\t: %llu\n", GetVMSize());
printf("slide\t\t: %td\n", GetVMAddrSlide());
}
#pragma mark -
@@ -335,194 +276,146 @@ uint32_t DynamicImage::GetFileType() {
//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
: task_(task),
cpu_type_(DetermineTaskCPUType(task)),
image_list_() {
: task_(task) {
ReadImageInfoForTask();
}
template<typename MachBits>
static uint64_t LookupSymbol(const char* symbol_name,
const char* filename,
cpu_type_t cpu_type) {
typedef typename MachBits::nlist_type nlist_type;
void* DynamicImages::GetDyldAllImageInfosPointer()
{
nlist_type symbol_info[8] = {};
const char *symbolNames[2] = { symbol_name, "\0" };
nlist_type &list = symbol_info[0];
int invalidEntriesCount = breakpad_nlist(filename,
&list,
symbolNames,
cpu_type);
const char *imageSymbolName = "_dyld_all_image_infos";
const char *dyldPath = "/usr/lib/dyld";
#ifndef __LP64__
struct nlist l[8];
memset(l, 0, sizeof(l) );
// First we lookup the address of the "_dyld_all_image_infos" struct
// which lives in "dyld". This structure contains information about all
// of the loaded dynamic images.
struct nlist &list = l[0];
list.n_un.n_name = const_cast<char *>(imageSymbolName);
nlist(dyldPath,&list);
if(list.n_value) {
return reinterpret_cast<void*>(list.n_value);
}
return NULL;
#else
struct nlist_64 l[8];
struct nlist_64 &list = l[0];
memset(l, 0, sizeof(l) );
const char *symbolNames[2] = { imageSymbolName, "\0" };
int invalidEntriesCount = breakpad_nlist_64(dyldPath,&list,symbolNames);
if(invalidEntriesCount != 0) {
return 0;
return NULL;
}
assert(list.n_value);
return list.n_value;
}
#if TARGET_OS_IPHONE
static bool HasTaskDyldInfo() {
return true;
}
#else
static SInt32 GetOSVersionInternal() {
SInt32 os_version = 0;
Gestalt(gestaltSystemVersion, &os_version);
return os_version;
}
static SInt32 GetOSVersion() {
static SInt32 os_version = GetOSVersionInternal();
return os_version;
}
static bool HasTaskDyldInfo() {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
return true;
#else
return GetOSVersion() >= 0x1060;
return reinterpret_cast<void*>(list.n_value);
#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_);
}
}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
template<typename MachBits>
void ReadImageInfo(DynamicImages& images,
uint64_t image_list_address) {
typedef typename MachBits::dyld_image_info dyld_image_info;
typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos;
typedef typename MachBits::mach_header_type mach_header_type;
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
// Here we make the assumption that dyld loaded at the same address in
// the crashed process vs. this one. This is an assumption made in
// "dyld_debug.c" and is said to be nearly always valid.
vector<uint8_t> dyld_all_info_bytes;
if (ReadTaskMemory(images.task_,
image_list_address,
sizeof(dyld_all_image_infos),
dyld_all_info_bytes) != KERN_SUCCESS)
return;
dyld_all_image_infos *dyldInfo =
reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]);
// number of loaded images
int count = dyldInfo->infoArrayCount;
// Read an array of dyld_image_info structures each containing
// information about a loaded image.
vector<uint8_t> dyld_info_array_bytes;
if (ReadTaskMemory(images.task_,
dyldInfo->infoArray,
count * sizeof(dyld_image_info),
dyld_info_array_bytes) != KERN_SUCCESS)
return;
dyld_image_info *infoArray =
reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]);
images.image_list_.reserve(count);
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// First read just the mach_header from the image in the task.
vector<uint8_t> mach_header_bytes;
if (ReadTaskMemory(images.task_,
info.load_address_,
sizeof(mach_header_type),
mach_header_bytes) != KERN_SUCCESS)
continue; // bail on this dynamic image
mach_header_type *header =
reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
// Now determine the total amount necessary to read the header
// plus all of the load commands.
size_t header_size =
sizeof(mach_header_type) + header->sizeofcmds;
if (ReadTaskMemory(images.task_,
info.load_address_,
header_size,
mach_header_bytes) != KERN_SUCCESS)
continue;
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]);
// Read the file name from the task's memory space.
string file_path;
if (info.file_path_) {
// Although we're reading kMaxStringLength bytes, it's copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = ReadTaskString(images.task_, info.file_path_);
}
// Create an object representing this image and add it to our list.
DynamicImage *new_image;
new_image = new DynamicImage(&mach_header_bytes[0],
header_size,
info.load_address_,
file_path,
info.file_mod_date_,
images.task_,
images.cpu_type_);
if (new_image->IsValid()) {
images.image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
}
// sorts based on loading address
sort(images.image_list_.begin(), images.image_list_.end());
// remove duplicates - this happens in certain strange cases
// You can see it in DashboardClient when Google Gadgets plugin
// is installed. Apple's crash reporter log and gdb "info shared"
// both show the same library multiple times at the same address
vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(),
images.image_list_.end());
images.image_list_.erase(it, images.image_list_.end());
}
void DynamicImages::ReadImageInfoForTask() {
uint64_t imageList = GetDyldAllImageInfosPointer();
void *imageList = GetDyldAllImageInfosPointer();
if (imageList) {
if (Is64Bit())
ReadImageInfo<MachO64>(*this, imageList);
else
ReadImageInfo<MachO32>(*this, imageList);
kern_return_t kr;
// Read the structure inside of dyld that contains information about
// loaded images. We're reading from the desired task's address space.
// Here we make the assumption that dyld loaded at the same address in
// the crashed process vs. this one. This is an assumption made in
// "dyld_debug.c" and is said to be nearly always valid.
dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*>
(ReadTaskMemory(task_,
reinterpret_cast<void*>(imageList),
sizeof(dyld_all_image_infos), &kr));
if (dyldInfo) {
// number of loaded images
int count = dyldInfo->infoArrayCount;
// Read an array of dyld_image_info structures each containing
// information about a loaded image.
dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
(ReadTaskMemory(task_,
dyldInfo->infoArray,
count*sizeof(dyld_image_info), &kr));
image_list_.reserve(count);
for (int i = 0; i < count; ++i) {
dyld_image_info &info = infoArray[i];
// First read just the mach_header from the image in the task.
breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
(ReadTaskMemory(task_,
info.load_address_,
sizeof(breakpad_mach_header), &kr));
if (!header)
break; // bail on this dynamic image
// Now determine the total amount we really want to read based on the
// size of the load commands. We need the header plus all of the
// load commands.
unsigned int header_size =
sizeof(breakpad_mach_header) + header->sizeofcmds;
free(header);
header = reinterpret_cast<breakpad_mach_header*>
(ReadTaskMemory(task_, info.load_address_, header_size, &kr));
// Read the file name from the task's memory space.
char *file_path = NULL;
if (info.file_path_) {
// Although we're reading kMaxStringLength bytes, it's copied in the
// the DynamicImage constructor below with the correct string length,
// so it's not really wasting memory.
file_path = reinterpret_cast<char*>
(ReadTaskString(task_, info.file_path_));
}
// Create an object representing this image and add it to our list.
DynamicImage *new_image;
new_image = new DynamicImage(header,
header_size,
(breakpad_mach_header*)info.load_address_,
file_path,
info.file_mod_date_,
task_);
if (new_image->IsValid()) {
image_list_.push_back(DynamicImageRef(new_image));
} else {
delete new_image;
}
if (file_path) {
free(file_path);
}
}
free(dyldInfo);
free(infoArray);
// sorts based on loading address
sort(image_list_.begin(), image_list_.end() );
// remove duplicates - this happens in certain strange cases
// You can see it in DashboardClient when Google Gadgets plugin
// is installed. Apple's crash reporter log and gdb "info shared"
// both show the same library multiple times at the same address
vector<DynamicImageRef>::iterator it = unique(image_list_.begin(),
image_list_.end() );
image_list_.erase(it, image_list_.end());
}
}
}
@@ -544,7 +437,7 @@ int DynamicImages::GetExecutableImageIndex() {
for (int i = 0; i < image_count; ++i) {
DynamicImage *image = GetImage(i);
if (image->GetFileType() == MH_EXECUTE) {
if (image->GetMachHeader()->filetype == MH_EXECUTE) {
return i;
}
}
@@ -552,27 +445,4 @@ int DynamicImages::GetExecutableImageIndex() {
return -1;
}
//==============================================================================
// static
cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) {
if (task == mach_task_self())
return GetNativeCPUType();
int mib[CTL_MAXNAME];
size_t mibLen = CTL_MAXNAME;
int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen);
if (err == 0) {
assert(mibLen < CTL_MAXNAME);
pid_for_task(task, &mib[mibLen]);
mibLen += 1;
cpu_type_t cpu_type;
size_t cpuTypeSize = sizeof(cpu_type);
sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0);
return cpu_type;
}
return GetNativeCPUType();
}
} // namespace google_breakpad

View File

@@ -41,51 +41,32 @@
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include "mach_vm_compat.h"
namespace google_breakpad {
using std::string;
using std::vector;
//==============================================================================
// The memory layout of this struct matches the dyld_image_info struct
// defined in "dyld_gdb.h" in the darwin source.
typedef struct dyld_image_info32 {
uint32_t load_address_; // struct mach_header*
uint32_t file_path_; // char*
uint32_t file_mod_date_;
} dyld_image_info32;
typedef struct dyld_image_info64 {
uint64_t load_address_; // struct mach_header*
uint64_t file_path_; // char*
uint64_t file_mod_date_;
} dyld_image_info64;
typedef struct dyld_image_info {
struct mach_header *load_address_;
char *file_path_;
uintptr_t file_mod_date_;
} dyld_image_info;
//==============================================================================
// This is as defined in "dyld_gdb.h" in the darwin source.
// _dyld_all_image_infos (in dyld) is a structure of this type
// which will be used to determine which dynamic code has been loaded.
typedef struct dyld_all_image_infos32 {
typedef struct dyld_all_image_infos {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
uint32_t infoArray; // const struct dyld_image_info*
uint32_t notification;
const struct dyld_image_info *infoArray;
void* notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos32;
typedef struct dyld_all_image_infos64 {
uint32_t version; // == 1 in Mac OS X 10.4
uint32_t infoArrayCount;
uint64_t infoArray; // const struct dyld_image_info*
uint64_t notification;
bool processDetachedFromSharedRegion;
} dyld_all_image_infos64;
} dyld_all_image_infos;
// some typedefs to isolate 64/32 bit differences
#ifdef __LP64__
@@ -96,49 +77,66 @@ typedef mach_header breakpad_mach_header;
typedef segment_command breakpad_mach_segment_command;
#endif
// Helper functions to deal with 32-bit/64-bit Mach-O differences.
class DynamicImage;
template<typename MachBits>
bool FindTextSection(DynamicImage& image);
//==============================================================================
// A simple wrapper for a mach_header
//
// This could be fleshed out with some more interesting methods.
class MachHeader {
public:
explicit MachHeader(const breakpad_mach_header &header) : header_(header) {}
template<typename MachBits>
uint32_t GetFileTypeFromHeader(DynamicImage& image);
void Print() {
printf("magic\t\t: %4x\n", header_.magic);
printf("cputype\t\t: %d\n", header_.cputype);
printf("cpusubtype\t: %d\n", header_.cpusubtype);
printf("filetype\t: %d\n", header_.filetype);
printf("ncmds\t\t: %d\n", header_.ncmds);
printf("sizeofcmds\t: %d\n", header_.sizeofcmds);
printf("flags\t\t: %d\n", header_.flags);
}
breakpad_mach_header header_;
};
//==============================================================================
// Represents a single dynamically loaded mach-o image
class DynamicImage {
public:
DynamicImage(uint8_t *header, // data is copied
size_t header_size, // includes load commands
uint64_t load_address,
string file_path,
DynamicImage(breakpad_mach_header *header, // we take ownership
int header_size, // includes load commands
breakpad_mach_header *load_address,
char *inFilePath,
uintptr_t image_mod_date,
mach_port_t task,
cpu_type_t cpu_type)
: header_(header, header + header_size),
mach_port_t task)
: header_(header),
header_size_(header_size),
load_address_(load_address),
vmaddr_(0),
vmsize_(0),
slide_(0),
version_(0),
file_path_(file_path),
file_mod_date_(image_mod_date),
task_(task),
cpu_type_(cpu_type) {
task_(task) {
InitializeFilePath(inFilePath);
CalculateMemoryAndVersionInfo();
}
~DynamicImage() {
if (file_path_) {
free(file_path_);
}
free(header_);
}
// Returns pointer to a local copy of the mach_header plus load commands
breakpad_mach_header *GetMachHeader() {return header_;}
// Size of mach_header plus load commands
size_t GetHeaderSize() const {return header_.size();}
int GetHeaderSize() const {return header_size_;}
// Full path to mach-o binary
string GetFilePath() {return file_path_;}
char *GetFilePath() {return file_path_;}
uint64_t GetModDate() const {return file_mod_date_;}
uintptr_t GetModDate() const {return file_mod_date_;}
// Actual address where the image was loaded
uint64_t GetLoadAddress() const {return load_address_;}
breakpad_mach_header *GetLoadAddress() const {return load_address_;}
// Address where the image should be loaded
mach_vm_address_t GetVMAddr() const {return vmaddr_;}
@@ -152,49 +150,46 @@ class DynamicImage {
// Task owning this loaded image
mach_port_t GetTask() {return task_;}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
// filetype from the Mach-O header.
uint32_t GetFileType();
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
uint32_t GetVersion() {return version_;}
// For sorting
bool operator<(const DynamicImage &inInfo) {
return GetLoadAddress() < inInfo.GetLoadAddress();
}
// Debugging
void Print();
private:
friend class DynamicImages;
// Sanity checking
bool IsValid() {return GetVMSize() != 0;}
private:
DynamicImage(const DynamicImage &);
DynamicImage &operator=(const DynamicImage &);
friend class DynamicImages;
template<typename MachBits>
friend bool FindTextSection(DynamicImage& image);
template<typename MachBits>
friend uint32_t GetFileTypeFromHeader(DynamicImage& image);
// Makes local copy of file path to mach-o binary
void InitializeFilePath(char *inFilePath) {
if (inFilePath) {
size_t path_size = 1 + strlen(inFilePath);
file_path_ = reinterpret_cast<char*>(malloc(path_size));
strlcpy(file_path_, inFilePath, path_size);
} else {
file_path_ = NULL;
}
}
// Initializes vmaddr_, vmsize_, and slide_
void CalculateMemoryAndVersionInfo();
const vector<uint8_t> header_; // our local copy of the header
size_t header_size_; // mach_header plus load commands
uint64_t load_address_; // base address image is mapped into
breakpad_mach_header *header_; // our local copy of the header
int header_size_; // mach_header plus load commands
breakpad_mach_header *load_address_; // base address image is mapped into
mach_vm_address_t vmaddr_;
mach_vm_size_t vmsize_;
ptrdiff_t slide_;
uint32_t version_; // Dylib version
string file_path_; // path dyld used to load the image
char *file_path_; // path dyld used to load the image
uintptr_t file_mod_date_; // time_t of image file
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
};
//==============================================================================
@@ -227,11 +222,6 @@ class DynamicImageRef {
DynamicImage *p;
};
// Helper function to deal with 32-bit/64-bit Mach-O differences.
class DynamicImages;
template<typename MachBits>
void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
//==============================================================================
// An object of type DynamicImages may be created to allow introspection of
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
@@ -241,13 +231,13 @@ class DynamicImages {
explicit DynamicImages(mach_port_t task);
~DynamicImages() {
for (int i = 0; i < GetImageCount(); ++i) {
for (int i = 0; i < (int)image_list_.size(); ++i) {
delete image_list_[i];
}
}
// Returns the number of dynamically loaded mach-o images.
int GetImageCount() const {return static_cast<int>(image_list_.size());}
int GetImageCount() const {return image_list_.size();}
// Returns an individual image.
DynamicImage *GetImage(int i) {
@@ -264,53 +254,43 @@ class DynamicImages {
// Returns the task which we're looking at.
mach_port_t GetTask() const {return task_;}
// CPU type of the task
cpu_type_t GetCPUType() {return cpu_type_;}
// Debugging
void Print() {
for (int i = 0; i < (int)image_list_.size(); ++i) {
image_list_[i]->Print();
}
}
// Return true if the task is a 64-bit architecture.
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; }
void TestPrint() {
const breakpad_mach_header *header;
for (int i = 0; i < (int)image_list_.size(); ++i) {
printf("dyld: %p: name = %s\n", _dyld_get_image_header(i),
_dyld_get_image_name(i) );
// Determine the CPU type of the task being dumped.
static cpu_type_t DetermineTaskCPUType(task_t task);
const void *imageHeader = _dyld_get_image_header(i);
header = reinterpret_cast<const breakpad_mach_header*>(imageHeader);
// Get the native CPU type of this task.
static cpu_type_t GetNativeCPUType() {
#if defined(__i386__)
return CPU_TYPE_I386;
#elif defined(__x86_64__)
return CPU_TYPE_X86_64;
#elif defined(__ppc__)
return CPU_TYPE_POWERPC;
#elif defined(__ppc64__)
return CPU_TYPE_POWERPC64;
#elif defined(__arm__)
return CPU_TYPE_ARM;
#else
#error "GetNativeCPUType not implemented for this architecture"
#endif
MachHeader(*header).Print();
}
}
private:
template<typename MachBits>
friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address);
bool IsOurTask() {return task_ == mach_task_self();}
// Initialization
void ReadImageInfoForTask();
uint64_t GetDyldAllImageInfosPointer();
void* GetDyldAllImageInfosPointer();
mach_port_t task_;
cpu_type_t cpu_type_; // CPU type of task_
vector<DynamicImageRef> image_list_;
};
// Fill bytes with the contents of memory at a particular
// Returns a malloced block containing the contents of memory at a particular
// location in another task.
kern_return_t ReadTaskMemory(task_port_t target_task,
const uint64_t address,
size_t length,
vector<uint8_t> &bytes);
void* ReadTaskMemory(task_port_t target_task,
const void* address,
size_t len,
kern_return_t *kr);
} // namespace google_breakpad

View File

@@ -28,25 +28,15 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <map>
#include <mach/exc.h>
#include <mach/mig.h>
#include <pthread.h>
#include <signal.h>
#include <TargetConditionals.h>
#include "client/mac/handler/exception_handler.h"
#include "client/mac/handler/minidump_generator.h"
#include "common/mac/macho_utilities.h"
#include "common/mac/scoped_task_suspend-inl.h"
#include "google_breakpad/common/minidump_exception_mac.h"
#ifndef USE_PROTECTED_ALLOCATIONS
#if TARGET_OS_IPHONE
#define USE_PROTECTED_ALLOCATIONS 1
#else
#define USE_PROTECTED_ALLOCATIONS 0
#endif
#endif
// If USE_PROTECTED_ALLOCATIONS is activated then the
// gBreakpadAllocator needs to be setup in other code
@@ -57,14 +47,8 @@
extern ProtectedMemoryAllocator *gBreakpadAllocator;
#endif
namespace google_breakpad {
static union {
#if USE_PROTECTED_ALLOCATIONS
char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
#endif
google_breakpad::ExceptionHandler *handler;
} gProtectedData;
namespace google_breakpad {
using std::map;
@@ -102,114 +86,139 @@ struct ExceptionReplyMessage {
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
#if !TARGET_OS_IPHONE
extern "C"
{
// Forward declarations for functions that need "C" style compilation
boolean_t exc_server(mach_msg_header_t *request,
mach_msg_header_t *reply);
// This symbol must be visible to dlsym() - see
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
kern_return_t catch_exception_raise(mach_port_t target_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)
__attribute__((visibility("default")));
}
#endif
mach_msg_type_number_t code_count);
kern_return_t ForwardException(mach_port_t task,
mach_port_t failed_thread,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
kern_return_t ForwardException(mach_port_t task,
mach_port_t failed_thread,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
#if TARGET_OS_IPHONE
// Implementation is based on the implementation generated by mig.
boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
mach_msg_header_t *OutHeadP) {
OutHeadP->msgh_bits =
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
/* Minimal size: routine() will update it if different */
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
OutHeadP->msgh_local_port = MACH_PORT_NULL;
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
kern_return_t exception_raise(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t exception_code_count);
if (InHeadP->msgh_id != 2401) {
((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
return FALSE;
}
kern_return_t
exception_raise_state(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *target_flavor,
thread_state_t thread_state,
mach_msg_type_number_t thread_state_count,
thread_state_t thread_state,
mach_msg_type_number_t *thread_state_count);
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
mach_msg_trailer_t trailer;
} Request;
kern_return_t
exception_raise_state_identity(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t exception_code,
mach_msg_type_number_t exception_code_count,
thread_state_flavor_t *target_flavor,
thread_state_t thread_state,
mach_msg_type_number_t thread_state_count,
thread_state_t thread_state,
mach_msg_type_number_t *thread_state_count);
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
Request *In0P = (Request *)InHeadP;
Reply *OutP = (Reply *)OutHeadP;
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
);
if (In0P->task.name != mach_task_self()) {
return FALSE;
}
OutP->RetCode = ForwardException(In0P->task.name,
In0P->thread.name,
In0P->exception,
In0P->code,
In0P->codeCnt);
OutP->NDR = NDR_record;
return TRUE;
}
#else
boolean_t breakpad_exc_server(mach_msg_header_t *request,
mach_msg_header_t *reply) {
return exc_server(request, reply);
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count);
}
// Callback from exc_server()
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
return KERN_SUCCESS;
}
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
return KERN_SUCCESS;
}
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
if (task != mach_task_self()) {
return KERN_FAILURE;
}
return ForwardException(task, failed_thread, exception, code, code_count);
}
#endif
ExceptionHandler::ExceptionHandler(const string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
void *callback_context,
bool install_handler,
const char *port_name)
bool install_handler)
: dump_path_(),
filter_(filter),
callback_(callback),
@@ -225,10 +234,6 @@ ExceptionHandler::ExceptionHandler(const string &dump_path,
// This will update to the ID and C-string pointers
set_dump_path(dump_path);
MinidumpGenerator::GatherSystemInformation();
#if !TARGET_OS_IPHONE
if (port_name)
crash_generation_client_.reset(new CrashGenerationClient(port_name));
#endif
Setup(install_handler);
}
@@ -257,7 +262,7 @@ ExceptionHandler::~ExceptionHandler() {
Teardown();
}
bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
bool ExceptionHandler::WriteMinidump() {
// If we're currently writing, just return
if (use_minidump_write_mutex_)
return false;
@@ -269,9 +274,7 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
// Send an empty message to the handle port so that a minidump will
// be written
SendMessageToHandlerThread(write_exception_stream ?
kWriteDumpWithExceptionMessage :
kWriteDumpMessage);
SendEmptyMachMessage();
// Wait for the minidump writer to complete its writing. It will unlock
// the mutex when completed
@@ -285,54 +288,16 @@ bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
// static
bool ExceptionHandler::WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context) {
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
NULL);
return handler.WriteMinidump(write_exception_stream);
}
// static
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
mach_port_t child_blamed_thread,
const string &dump_path,
MinidumpCallback callback,
void *callback_context) {
ScopedTaskSuspend suspend(child);
MinidumpGenerator generator(child, MACH_PORT_NULL);
string dump_id;
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
generator.SetExceptionInformation(EXC_BREAKPOINT,
#if defined (__i386__) || defined(__x86_64__)
EXC_I386_BPT,
#elif defined (__ppc__) || defined (__ppc64__)
EXC_PPC_BREAKPOINT,
#elif defined (__arm__)
EXC_ARM_BREAKPOINT,
#else
#error architecture not supported
#endif
0,
child_blamed_thread);
bool result = generator.Write(dump_filename.c_str());
if (callback) {
return callback(dump_path.c_str(), dump_id.c_str(),
callback_context, result);
}
return result;
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false);
return handler.WriteMinidump();
}
bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
ucontext_t *task_context,
mach_port_t thread_name,
bool exit_after_write,
bool report_current_thread) {
mach_port_t thread_name) {
bool result = false;
if (directCallback_) {
@@ -341,36 +306,19 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exit_after_write)
if (exception_type && exception_code)
_exit(exception_type);
}
#if !TARGET_OS_IPHONE
} else if (IsOutOfProcess()) {
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decide if this should be sent.
if (filter_ && !filter_(callback_context_))
return false;
return crash_generation_client_->RequestDumpForException(
exception_type,
exception_code,
exception_subcode,
thread_name);
}
#endif
} else {
string minidump_id;
// Putting the MinidumpGenerator in its own context will ensure that the
// destructor is executed, closing the newly created minidump file.
if (!dump_path_.empty()) {
MinidumpGenerator md(mach_task_self(),
report_current_thread ? MACH_PORT_NULL :
mach_thread_self());
md.SetTaskContext(task_context);
MinidumpGenerator md;
if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decide if this should be sent.
// decided if this should be sent
if (filter_ && !filter_(callback_context_))
return false;
@@ -388,7 +336,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
result)) {
if (exit_after_write)
if (exception_type && exception_code)
_exit(exception_type);
}
}
@@ -408,13 +356,13 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
current.count = EXC_TYPES_COUNT;
mach_port_t current_task = mach_task_self();
task_get_exception_ports(current_task,
s_exception_mask,
current.masks,
&current.count,
current.ports,
current.behaviors,
current.flavors);
kern_return_t result = task_get_exception_ports(current_task,
s_exception_mask,
current.masks,
&current.count,
current.ports,
current.behaviors,
current.flavors);
// Find the first exception handler that matches the exception
unsigned int found;
@@ -432,16 +380,48 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
mach_port_t target_port = current.ports[found];
exception_behavior_t target_behavior = current.behaviors[found];
thread_state_flavor_t target_flavor = current.flavors[found];
kern_return_t result;
mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX;
breakpad_thread_state_data_t thread_state;
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = exception_raise(target_port, failed_thread, task, exception,
code, code_count);
break;
case EXCEPTION_STATE:
result = thread_get_state(failed_thread, target_flavor, thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = exception_raise_state(target_port, failed_thread, task,
exception, code,
code_count, &target_flavor,
thread_state, thread_state_count,
thread_state, &thread_state_count);
if (result == KERN_SUCCESS)
result = thread_set_state(failed_thread, target_flavor, thread_state,
thread_state_count);
break;
case EXCEPTION_STATE_IDENTITY:
result = thread_get_state(failed_thread, target_flavor, thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = exception_raise_state_identity(target_port, failed_thread,
task, exception, code,
code_count, &target_flavor,
thread_state,
thread_state_count,
thread_state,
&thread_state_count);
if (result == KERN_SUCCESS)
result = thread_set_state(failed_thread, target_flavor, thread_state,
thread_state_count);
break;
default:
fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
fprintf(stderr, "** Unknown exception behavior\n");
result = KERN_FAILURE;
break;
}
@@ -449,6 +429,15 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
return result;
}
// Callback from exc_server()
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
return ForwardException(task, failed_thread, exception, code, code_count);
}
// static
void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
ExceptionHandler *self =
@@ -458,11 +447,10 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Wait for the exception info
while (1) {
receive.header.msgh_local_port = self->handler_port_;
receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
receive.header.msgh_size = sizeof(receive);
kern_return_t result = mach_msg(&(receive.header),
MACH_RCV_MSG | MACH_RCV_LARGE, 0,
receive.header.msgh_size,
self->handler_port_,
sizeof(receive), self->handler_port_,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
@@ -479,43 +467,24 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// to avoid misleading stacks. If appropriate they will be resumed
// afterwards.
if (!receive.exception) {
// Don't touch self, since this message could have been sent
// from its destructor.
if (receive.header.msgh_id == kShutdownMessage)
if (self->is_in_teardown_)
return NULL;
self->SuspendThreads();
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
if(gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
mach_port_t thread = MACH_PORT_NULL;
int exception_type = 0;
int exception_code = 0;
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
thread = receive.thread.name;
exception_type = EXC_BREAKPOINT;
#if defined (__i386__) || defined(__x86_64__)
exception_code = EXC_I386_BPT;
#elif defined (__ppc__) || defined (__ppc64__)
exception_code = EXC_PPC_BREAKPOINT;
#elif defined (__arm__)
exception_code = EXC_ARM_BREAKPOINT;
#else
#error architecture not supported
#endif
}
// Write out the dump and save the result for later retrieval
self->last_minidump_write_result_ =
self->WriteMinidumpWithException(exception_type, exception_code,
0, NULL, thread,
false, false);
self->WriteMinidumpWithException(0, 0, 0, 0);
self->UninstallHandler(false);
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
if(gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
@@ -530,13 +499,13 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// exceptions that occur in the parent process are caught and
// processed. If the exception was not caused by this task, we
// still need to call into the exception server and have it return
// KERN_FAILURE (see catch_exception_raise) in order for the kernel
// KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
// to move onto the host exception handler for the child task
if (receive.task.name == mach_task_self()) {
self->SuspendThreads();
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
if(gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
@@ -546,35 +515,26 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
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
subcode, receive.thread.name);
self->UninstallHandler(true);
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
if(gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
}
// Pass along the exception to the server, which will setup the
// message and call catch_exception_raise() and put the return
// message and call breakpad_exception_raise() and put the return
// code into the reply.
ExceptionReplyMessage reply;
if (!breakpad_exc_server(&receive.header, &reply.header))
if (!exc_server(&receive.header, &reply.header))
exit(1);
// Send a reply and exit
mach_msg(&(reply.header), MACH_SEND_MSG,
reply.header.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
result = mach_msg(&(reply.header), MACH_SEND_MSG,
reply.header.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
}
}
@@ -582,53 +542,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
return NULL;
}
//static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
gProtectedData.handler->WriteMinidumpWithException(
EXC_SOFTWARE,
MD_EXCEPTION_CODE_MAC_ABORT,
0,
static_cast<ucontext_t*>(uc),
mach_thread_self(),
true,
true);
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
}
bool ExceptionHandler::InstallHandler() {
// If a handler is already installed, something is really wrong.
if (gProtectedData.handler != NULL) {
return false;
}
#if TARGET_OS_IPHONE
if (!IsOutOfProcess()) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGABRT);
sa.sa_sigaction = ExceptionHandler::SignalHandler;
sa.sa_flags = SA_SIGINFO;
scoped_ptr<struct sigaction> old(new struct sigaction);
if (sigaction(SIGABRT, &sa, old.get()) == -1) {
return false;
}
old_handler_.swap(old);
gProtectedData.handler = this;
#if USE_PROTECTED_ALLOCATIONS
assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
#endif
}
#endif
try {
#if USE_PROTECTED_ALLOCATIONS
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
@@ -667,16 +581,6 @@ bool ExceptionHandler::InstallHandler() {
bool ExceptionHandler::UninstallHandler(bool in_exception) {
kern_return_t result = KERN_SUCCESS;
if (old_handler_.get()) {
sigaction(SIGABRT, old_handler_.get(), NULL);
#if USE_PROTECTED_ALLOCATIONS
mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
PROT_READ | PROT_WRITE);
#endif
old_handler_.reset();
gProtectedData.handler = NULL;
}
if (installed_exception_handler_) {
mach_port_t current_task = mach_task_self();
@@ -746,7 +650,7 @@ bool ExceptionHandler::Teardown() {
return false;
// Send an empty message so that the handler_thread exits
if (SendMessageToHandlerThread(kShutdownMessage)) {
if (SendEmptyMachMessage()) {
mach_port_t current_task = mach_task_self();
result = mach_port_deallocate(current_task, handler_port_);
if (result != KERN_SUCCESS)
@@ -756,31 +660,22 @@ bool ExceptionHandler::Teardown() {
}
handler_thread_ = NULL;
handler_port_ = MACH_PORT_NULL;
handler_port_ = NULL;
pthread_mutex_destroy(&minidump_write_mutex_);
return result == KERN_SUCCESS;
}
bool ExceptionHandler::SendMessageToHandlerThread(
HandlerThreadMessage message_id) {
ExceptionMessage msg;
memset(&msg, 0, sizeof(msg));
msg.header.msgh_id = message_id;
if (message_id == kWriteDumpMessage ||
message_id == kWriteDumpWithExceptionMessage) {
// Include this thread's port.
msg.thread.name = mach_thread_self();
msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
}
msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
msg.header.msgh_remote_port = handler_port_;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
bool ExceptionHandler::SendEmptyMachMessage() {
ExceptionMessage empty;
memset(&empty, 0, sizeof(empty));
empty.header.msgh_size = sizeof(empty) - sizeof(empty.padding);
empty.header.msgh_remote_port = handler_port_;
empty.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
MACH_MSG_TYPE_MAKE_SEND_ONCE);
kern_return_t result = mach_msg(&(msg.header),
kern_return_t result = mach_msg(&(empty.header),
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
msg.header.msgh_size, 0, 0,
empty.header.msgh_size, 0, 0,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
return result == KERN_SUCCESS;

View File

@@ -37,32 +37,15 @@
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
#include <mach/mach.h>
#include <TargetConditionals.h>
#include <string>
#include "processor/scoped_ptr.h"
#if !TARGET_OS_IPHONE
#include "client/mac/crash_generation/crash_generation_client.h"
#endif
namespace google_breakpad {
using std::string;
struct ExceptionParameters;
enum HandlerThreadMessage {
// Message ID telling the handler thread to write a dump.
kWriteDumpMessage = 0,
// Message ID telling the handler thread to write a dump and include
// an exception stream.
kWriteDumpWithExceptionMessage = 1,
// Message ID telling the handler thread to quit.
kShutdownMessage = 2
};
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
@@ -103,12 +86,9 @@ class ExceptionHandler {
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
// If port_name is non-NULL, attempt to perform out-of-process dump generation
// If port_name is NULL, in-process dump generation will be used.
ExceptionHandler(const string &dump_path,
FilterCallback filter, MinidumpCallback callback,
void *callback_context, bool install_handler,
const char *port_name);
void *callback_context, bool install_handler);
// A special constructor if we want to bypass minidump writing and
// simply get a callback with the exception information.
@@ -128,41 +108,13 @@ class ExceptionHandler {
// Writes a minidump immediately. This can be used to capture the
// execution state independently of a crash. Returns true on success.
bool WriteMinidump() {
return WriteMinidump(false);
}
bool WriteMinidump(bool write_exception_stream);
bool WriteMinidump();
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback,
void *callback_context) {
return WriteMinidump(dump_path, false, callback, callback_context);
}
static bool WriteMinidump(const string &dump_path,
bool write_exception_stream,
MinidumpCallback callback,
void *callback_context);
// Write a minidump of child immediately. This can be used to capture
// the execution state of a child process independently of a crash.
static bool WriteMinidumpForChild(mach_port_t child,
mach_port_t child_blamed_thread,
const std::string &dump_path,
MinidumpCallback callback,
void *callback_context);
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
#if TARGET_OS_IPHONE
return false;
#else
return crash_generation_client_.get() != NULL;
#endif
}
private:
// Install the mach exception handler
bool InstallHandler();
@@ -178,28 +130,18 @@ class ExceptionHandler {
// thread
bool Teardown();
// Send a mach message to the exception handler. Return true on
// success, false otherwise.
bool SendMessageToHandlerThread(HandlerThreadMessage message_id);
// Send an "empty" mach message to the exception handler. Return true on
// success, false otherwise
bool SendEmptyMachMessage();
// All minidump writing goes through this one routine.
// |task_context| can be NULL. If not, it will be used to retrieve the
// context of the current thread, instead of using |thread_get_state|.
bool WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
ucontext_t *task_context,
mach_port_t thread_name,
bool exit_after_write,
bool report_current_thread);
// All minidump writing goes through this one routine
bool WriteMinidumpWithException(int exception_type, int exception_code,
int exception_subcode, mach_port_t thread_name);
// When installed, this static function will be call from a newly created
// pthread with |this| as the argument
static void *WaitForMessage(void *exception_handler_class);
// Signal handler for SIGABRT.
static void SignalHandler(int sig, siginfo_t* info, void* uc);
// disallow copy ctor and operator=
explicit ExceptionHandler(const ExceptionHandler &);
void operator=(const ExceptionHandler &);
@@ -264,15 +206,6 @@ class ExceptionHandler {
// True, if we're using the mutext to indicate when mindump writing occurs
bool use_minidump_write_mutex_;
// Old signal handler for SIGABRT. Used to be able to restore it when
// uninstalling.
scoped_ptr<struct sigaction> old_handler_;
#if !TARGET_OS_IPHONE
// Client for out-of-process dump generation.
scoped_ptr<CrashGenerationClient> crash_generation_client_;
#endif
};
} // namespace google_breakpad

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2006, 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.
/*
g++ -framework CoreFoundation -I../../.. ../../minidump_file_writer.cc ../../../common/convert_UTF.c ../../../common/string_conversion.cc ../../../common/mac/string_utilities.cc exception_handler.cc minidump_generator.cc exception_handler_test.cc -o exception_handler_test -mmacosx-version-min=10.4 ../../../common/mac/file_id.cc dynamic_images.cc ../../../common/mac/macho_id.cc ../../../common/mac/macho_walker.cc -lcrypto ../../../common/mac/macho_utilities.cc
*/
#include <pthread.h>
#include <pwd.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include "exception_handler.h"
#include "minidump_generator.h"
using std::string;
using google_breakpad::ExceptionHandler;
static void *SleepyFunction(void *) {
while (1) {
sleep(10000);
}
return NULL;
}
static void Crasher() {
int *a = (int*)0x42;
fprintf(stdout, "Going to crash...\n");
fprintf(stdout, "A = %d", *a);
}
static void SoonToCrash() {
Crasher();
}
bool MDCallback(const char *dump_dir, const char *file_name,
void *context, bool success) {
string path(dump_dir);
string dest(dump_dir);
path.append(file_name);
path.append(".dmp");
fprintf(stdout, "Minidump: %s\n", path.c_str());
// Indicate that we've handled the callback
exit(0);
}
int main(int argc, char * const argv[]) {
char buffer[PATH_MAX];
// Home dir
snprintf(buffer, sizeof(buffer), "/tmp/");
string path(buffer);
ExceptionHandler eh(path, NULL, MDCallback, NULL, true);
pthread_t t;
if (pthread_create(&t, NULL, SleepyFunction, NULL) == 0) {
pthread_detach(t);
} else {
perror("pthread_create");
}
// // Dump a test
// eh.WriteMinidump();
// Test the handler
SoonToCrash();
return 0;
}

View File

@@ -1,49 +0,0 @@
// 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_

File diff suppressed because it is too large Load Diff

View File

@@ -33,34 +33,42 @@
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
#include <mach/mach.h>
#include <TargetConditionals.h>
#include <string>
#include "client/minidump_file_writer.h"
#include "common/memory.h"
#include "common/mac/macho_utilities.h"
#include "google_breakpad/common/minidump_format.h"
#include "common/mac/macho_utilities.h"
#include "dynamic_images.h"
#include "mach_vm_compat.h"
#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
#define HAS_PPC_SUPPORT
#endif
#if defined(__arm__)
#define HAS_ARM_SUPPORT
#elif defined(__i386__) || defined(__x86_64__)
#define HAS_X86_SUPPORT
#endif
namespace google_breakpad {
using std::string;
#if TARGET_CPU_X86_64 || TARGET_CPU_PPC64
#define TOP_OF_THREAD0_STACK 0x00007fff5fbff000
#else
#define TOP_OF_THREAD0_STACK 0xbffff000
#endif
#if TARGET_CPU_X86_64
typedef x86_thread_state64_t breakpad_thread_state_t;
typedef MDRawContextAMD64 MinidumpContext;
#elif TARGET_CPU_X86
typedef i386_thread_state_t breakpad_thread_state_t;
typedef MDRawContextX86 MinidumpContext;
#elif TARGET_CPU_PPC64
typedef ppc_thread_state64_t breakpad_thread_state_t;
typedef MDRawContextPPC64 MinidumpContext;
#elif TARGET_CPU_PPC
typedef ppc_thread_state_t breakpad_thread_state_t;
typedef MDRawContextPPC MinidumpContext;
#endif
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
// breakpad_thread_state_t structure.
#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM
#if __DARWIN_UNIX03 || !TARGET_CPU_X86 || TARGET_CPU_X86_64
// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
// i386_thread_state_t structure. There's no good way to tell what version of
// the SDK we're compiling against so we just toggle on the same preprocessor
@@ -82,7 +90,7 @@ class MinidumpGenerator {
MinidumpGenerator();
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread);
virtual ~MinidumpGenerator();
~MinidumpGenerator();
// Return <dir>/<unique_name>.dmp
// Sets |unique_name| (if requested) to the unique name for the minidump
@@ -102,28 +110,16 @@ class MinidumpGenerator {
exception_thread_ = thread_name;
}
// Specify the task context. If |task_context| is not NULL, it will be used
// to retrieve the context of the current thread, instead of using
// |thread_get_state|.
void SetTaskContext(ucontext_t *task_context);
// Gather system information. This should be call at least once before using
// the MinidumpGenerator class.
static void GatherSystemInformation();
protected:
// Overridable Stream writers
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
// Overridable Helper
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
private:
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
// Stream writers
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream);
bool WriteExceptionStream(MDRawDirectory *exception_stream);
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream);
bool WriteModuleListStream(MDRawDirectory *module_list_stream);
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream);
@@ -131,62 +127,28 @@ class MinidumpGenerator {
// Helpers
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state);
bool GetThreadState(thread_act_t target_thread, thread_state_t state,
mach_msg_type_number_t *count);
bool WriteStackFromStartAddress(mach_vm_address_t start_addr,
MDMemoryDescriptor *stack_location);
bool WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path, bool in_memory);
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
bool WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// Per-CPU implementations of these methods
#ifdef HAS_ARM_SUPPORT
bool WriteStackARM(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextARM(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_PPC_SUPPORT
bool WriteStackPPC(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state);
bool WriteStackPPC64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextPPC64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_X86_SUPPORT
bool WriteStackX86(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
bool WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextX86_64(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
#endif
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
void operator=(const MinidumpGenerator &);
protected:
// Use this writer to put the data to disk
MinidumpFileWriter writer_;
private:
// Exception information
int exception_type_;
int exception_code_;
@@ -194,31 +156,15 @@ class MinidumpGenerator {
mach_port_t exception_thread_;
mach_port_t crashing_task_;
mach_port_t handler_thread_;
// CPU type of the task being dumped.
cpu_type_t cpu_type_;
// System information
static char build_string_[16];
static int os_major_version_;
static int os_minor_version_;
static int os_build_number_;
// Context of the task to dump.
ucontext_t *task_context_;
// Information about dynamically loaded code
DynamicImages *dynamic_images_;
// PageAllocator makes it possible to allocate memory
// directly from the system, even while handling an exception.
mutable PageAllocator allocator_;
protected:
// Blocks of memory written to the dump. These are all currently
// written while writing the thread list stream, but saved here
// so a memory list stream can be written afterwards.
wasteful_vector<MDMemoryDescriptor> memory_blocks_;
};
} // namespace google_breakpad

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2012, Google Inc.
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,31 +27,55 @@
// (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 <unistd.h>
#include "client/linux/handler/exception_handler.h"
#include "client/linux/handler/minidump_descriptor.h"
#include <pthread.h>
#include <pwd.h>
namespace {
#include <CoreFoundation/CoreFoundation.h>
bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
void* context,
bool succeeded) {
printf("Dump path: %s\n", descriptor.path());
return succeeded;
#include "minidump_generator.h"
#include "minidump_file_writer.h"
using std::string;
using google_breakpad::MinidumpGenerator;
static bool doneWritingReport = false;
static void *Reporter(void *) {
char buffer[PATH_MAX];
MinidumpGenerator md;
// Write it to the desktop
snprintf(buffer,
sizeof(buffer),
"/tmp/test.dmp");
fprintf(stdout, "Writing %s\n", buffer);
unlink(buffer);
md.Write(buffer);
doneWritingReport = true;
return NULL;
}
void Crash() {
volatile int* a = reinterpret_cast<volatile int*>(NULL);
*a = 1;
static void SleepyFunction() {
while (!doneWritingReport) {
usleep(100);
}
}
} // namespace
int main(int argc, char * const argv[]) {
pthread_t reporter_thread;
if (pthread_create(&reporter_thread, NULL, Reporter, NULL) == 0) {
pthread_detach(reporter_thread);
} else {
perror("pthread_create");
}
SleepyFunction();
int main(int argc, char* argv[]) {
google_breakpad::MinidumpDescriptor descriptor(".");
google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback,
NULL, true, -1);
Crash();
return 0;
}

View File

@@ -3,24 +3,10 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 42;
objects = {
/* Begin PBXBuildFile section */
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
@@ -72,7 +58,7 @@
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; };
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.c */; };
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; };
@@ -108,18 +94,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; };
8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; };
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -152,12 +132,12 @@
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.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; };
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; };
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/mac/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/mac/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/mac/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = "<absolute>"; };
F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; };
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
@@ -176,7 +156,6 @@
buildActionMask = 2147483647;
files = (
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */,
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -184,7 +163,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -193,7 +171,6 @@
buildActionMask = 2147483647;
files = (
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */,
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -201,9 +178,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */,
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -211,8 +185,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -222,8 +194,6 @@
files = (
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */,
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */,
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -233,10 +203,7 @@
08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
isa = PBXGroup;
children = (
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
F9721FA80E8B0E4800D7E813 /* md5.cc */,
F9721FA80E8B0E4800D7E813 /* md5.c */,
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
@@ -294,12 +261,9 @@
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = {
isa = PBXGroup;
children = (
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */,
8BFC815411FF9B7F002CB4DC /* Carbon.framework */,
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */,
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */,
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */,
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -450,15 +414,8 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
compatibilityVersion = "Xcode 2.4";
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
);
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
projectDirPath = "";
projectRoot = "";
@@ -556,7 +513,6 @@
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */,
D2F651110BEF951700920385 /* string_conversion.cc in Sources */,
D2F651150BEF953000920385 /* convert_UTF.c in Sources */,
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -568,7 +524,6 @@
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */,
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */,
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */,
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -588,7 +543,6 @@
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */,
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */,
D2F651210BEF975400920385 /* macho_walker.cc in Sources */,
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -601,7 +555,7 @@
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */,
F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */,
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */,
@@ -626,7 +580,6 @@
files = (
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -636,78 +589,154 @@
1DEB923208733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
COPY_PHASE_STRIP = NO;
GCC_CW_ASM_SYNTAX = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_ENABLE_PASCAL_STRINGS = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_THREADSAFE_STATICS = NO;
INSTALL_PATH = "$(HOME)/bin";
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
ZERO_LINK = NO;
};
name = Debug;
};
1DEB923308733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
ARCHS = (
ppc,
i386,
);
GCC_CW_ASM_SYNTAX = NO;
GCC_ENABLE_PASCAL_STRINGS = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
GCC_THREADSAFE_STATICS = NO;
INSTALL_PATH = "$(HOME)/bin";
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
ZERO_LINK = NO;
};
name = Release;
};
1DEB923608733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */;
buildSettings = {
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_LDFLAGS = "-lcrypto";
PREBINDING = NO;
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
};
name = Debug;
};
1DEB923708733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */;
buildSettings = {
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
OTHER_LDFLAGS = "-lcrypto";
PREBINDING = NO;
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
};
name = Release;
};
9B7CA8510B12984300CD3A1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INSTALL_PATH = "$(HOME)/bin";
PREBINDING = NO;
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
ZERO_LINK = NO;
};
name = Debug;
};
9B7CA8520B12984300CD3A1D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = YES;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
INSTALL_PATH = "$(HOME)/bin";
PREBINDING = NO;
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
ZERO_LINK = NO;
};
name = Release;
};
9BD82AA70B0026BF0055103E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INSTALL_PATH = "$(HOME)/bin";
OTHER_CFLAGS = "-Wall";
PREBINDING = NO;
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
ZERO_LINK = NO;
};
name = Debug;
};
9BD82AA80B0026BF0055103E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = YES;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_MODEL_TUNING = G5;
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INSTALL_PATH = "$(HOME)/bin";
OTHER_CFLAGS = "-Wall";
PREBINDING = NO;
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
ZERO_LINK = NO;
};
name = Release;
};
F93A88770E8B4C700026AF89 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_CHAR_IS_UNSIGNED_CHAR = YES;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
OTHER_LDFLAGS = (
"-lcrypto",
"-framework",
Cocoa,
"-framework",
SenTestingKit,
);
PREBINDING = NO;
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
@@ -717,20 +746,58 @@
F93A88780E8B4C700026AF89 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_CHAR_IS_UNSIGNED_CHAR = YES;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_MODEL_TUNING = G5;
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
OTHER_LDFLAGS = (
"-lcrypto",
"-framework",
Cocoa,
"-framework",
SenTestingKit,
);
PREBINDING = NO;
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
ZERO_LINK = NO;
};
name = Release;
};
F9AE19C40DB04A9500C98454 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = (
"$(NATIVE_ARCH_64_BIT)",
ppc64,
);
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.5;
OTHER_LDFLAGS = (
"-framework",
Carbon,
"-framework",
CPlusTest,
);
PREBINDING = NO;
PRODUCT_NAME = minidump_tests64;
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
@@ -739,19 +806,57 @@
F9AE19C50DB04A9500C98454 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = (
"$(NATIVE_ARCH_64_BIT)",
ppc64,
);
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.5;
OTHER_LDFLAGS = (
"-framework",
Carbon,
"-framework",
CPlusTest,
);
PREBINDING = NO;
PRODUCT_NAME = minidump_tests64;
SDKROOT = /Developer/SDKs/MacOSX10.5.sdk;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
ZERO_LINK = NO;
};
name = Release;
};
F9AE5B350DBFDBA300505983 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
OTHER_LDFLAGS = (
"-framework",
Carbon,
"-framework",
CPlusTest,
"-lcrypto",
);
PREBINDING = NO;
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
@@ -761,11 +866,28 @@
F9AE5B370DBFDBA300505983 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(NATIVE_ARCH)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Carbon.framework/Headers/Carbon.h";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles";
OTHER_LDFLAGS = (
"-lcrypto",
"-framework",
Carbon,
"-framework",
CPlusTest,
);
PREBINDING = NO;
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
ZERO_LINK = NO;
};
name = Release;
};

View File

@@ -59,7 +59,7 @@ ProtectedMemoryAllocator::~ProtectedMemoryAllocator() {
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) {
char *ProtectedMemoryAllocator::Allocate(size_t bytes) {
if (valid_ && next_alloc_offset_ + bytes <= pool_size_) {
char *p = (char*)base_address_ + next_alloc_offset_;
next_alloc_offset_ += bytes;

View File

@@ -53,7 +53,7 @@ class ProtectedMemoryAllocator {
// Fails by returning NULL is no more space is available.
// Please note that the pointers returned from this method should not
// be freed in any way (for example by calling free() on them ).
char * Allocate(vm_size_t n);
char * Allocate(size_t n);
// Returns the base address of the allocation pool.
char * GetBaseAddress() { return (char*)base_address_; }
@@ -78,7 +78,7 @@ class ProtectedMemoryAllocator {
private:
vm_size_t pool_size_;
vm_address_t base_address_;
vm_size_t next_alloc_offset_;
int next_alloc_offset_;
bool valid_;
};

View File

@@ -55,17 +55,21 @@ void DynamicImagesTests::ReadTaskMemoryTest() {
// pick test2 as a symbol we know to be valid to read
// anything will work, really
void *addr = reinterpret_cast<void*>(&test2);
std::vector<uint8_t> buf(getpagesize());
void *buf;
fprintf(stderr, "reading 0x%p\n", addr);
kr = google_breakpad::ReadTaskMemory(mach_task_self(),
(uint64_t)addr,
getpagesize(),
buf);
buf = google_breakpad::ReadTaskMemory(mach_task_self(),
addr,
getpagesize(),
&kr);
CPTAssert(kr == KERN_SUCCESS);
CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize()));
CPTAssert(buf != NULL);
CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize()));
free(buf);
}
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
@@ -75,5 +79,7 @@ void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() {
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount());
d->TestPrint();
CPTAssert(d->GetImageCount() > 0);
}

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>CLASS</key>
<string>LengthLimitingTextField</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextField</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow_</key>
<string>NSWindow</string>
<key>cancelButton_</key>
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>commentsEntryField_</key>
<string>LengthLimitingTextField</string>
<key>countdownLabel_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>LengthLimitingTextField</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>
<string>NSTextField</string>
<key>emailSectionBox_</key>
<string>NSBox</string>
<key>headerBox_</key>
<string>NSBox</string>
<key>preEmailBox_</key>
<string>NSBox</string>
<key>privacyLinkArrow_</key>
<string>NSView</string>
<key>privacyLinkLabel_</key>
<string>NSTextField</string>
<key>sendButton_</key>
<string>NSButton</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>676</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../Breakpad.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>132</integer>
</array>
<key>IBSystem Version</key>
<string>9J61</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More