10 Commits

Author SHA1 Message Date
nealsid
01ca4892d8 Forgot to add modified files for the last commit!
http://breakpad.appspot.com/33002

A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@415 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-10-08 19:20:06 +00:00
nealsid
11fbf26119 Handle DW_LNE_end_sequence, plus a better infrastructure for
creating DIE handlers using dynamic dispatch & virtual methods.

http://breakpad.appspot.com/33002 (although the version of upload.py I'm using doesn't appear to upload new files to the code review for git)

A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@414 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-10-08 19:19:56 +00:00
nealsid
548c407f4b Issue 32002: update char declarations to be unsigned
http://breakpad.appspot.com/32002

A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@413 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-10-08 19:19:49 +00:00
nealsid
52af30d80c Issue 33001: Add missing includes and update comments to DWARF code
http://breakpad.appspot.com/33001

A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@412 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-10-08 19:19:42 +00:00
nealsid
3d9c2ce401 Makefile refactoring/cleanup
A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@411 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-10-08 19:19:33 +00:00
nealsid
06a8c78818 Updated STABS comments and moved DWARF code to a common location. This should have been done already but I think I'm messing something up with my git-svn fiddling :-)
A=jim blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@399 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-09-17 22:16:13 +00:00
nealsid
0f3b2d75d9 Updated STABS comments and moved DWARF code to a common location.
A=jim blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@398 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-09-17 22:15:57 +00:00
nealsid
e1e8be4cae Continue past stray end of compilation unit marker rather than returning true early
A=jim blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@397 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-09-17 22:11:45 +00:00
nealsid
8583d8ee76 Add a warning function to print a formatted string to stderr
A=jim.blandy
R=nealsid


git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@396 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-09-17 22:09:30 +00:00
nealsid
64892f177d Branch for Linux dwarf work
git-svn-id: http://google-breakpad.googlecode.com/svn/branches/linux-dwarf@395 4c0a9323-5329-0410-9bdc-e9ce6186880e
2009-09-17 19:19:22 +00:00
468 changed files with 39659 additions and 124481 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) 2010, Google Inc.
# Copyright (c) 2006, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -33,8 +33,6 @@
# This allows #includes to be relative to src/
AM_CPPFLAGS = -I$(top_srcdir)/src
# Specify include paths for ac macros
ACLOCAL_AMFLAGS = -I m4
## Documentation
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
@@ -49,34 +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/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/minidump_file_writer.cc \
src/common/convert_UTF.c \
src/common/md5.c \
src/common/string_conversion.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.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 \
@@ -84,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 \
@@ -102,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 \
@@ -138,621 +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/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
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_dumper_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
src/common/memory_unittest.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/crash_generation/crash_generation_client.o \
src/client/linux/minidump_writer/linux_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/file_id.o \
src/common/linux/guid_creator.o \
src/common/string_conversion.o
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_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/file_id.cc \
src/tools/linux/dump_syms/dump_syms.cc
src_tools_linux_md2core_minidump_2_core_SOURCES = \
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_LDFLAGS = -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_LDFLAGS = -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/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/linux/dump_symbols.cc \
src/common/linux/dump_symbols_unittest.cc \
src/common/linux/elf_symbols_to_module.cc \
src/common/linux/elf_symbols_to_module_unittest.cc \
src/common/linux/synth_elf.cc \
src/common/linux/synth_elf_unittest.cc \
src/common/linux/file_id.cc \
src/common/linux/file_id_unittest.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
endif
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 =
@@ -761,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
##
@@ -848,8 +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/file_id.cc \
src/common/linux/file_id.h \
src/common/linux/guid_creator.cc \
@@ -932,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.

6984
aclocal.m4 vendored

File diff suppressed because it is too large Load Diff

View File

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

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=

20434
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -30,93 +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
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)
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,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.
// The Android NDK doesn't have link.h. Fortunately, the only thing
// that Breakpad uses from it is the ElfW macro, so define it here.
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
#include <sys/exec_elf.h>
#ifndef ElfW
#define ElfW(type) _ElfW (Elf, ELFSIZE, type)
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t)
#define _ElfW_1(e,w,t) e##w##t
#endif
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_

View File

@@ -1,77 +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.
// Android runs a fairly new Linux kernel, so signal info is there,
// but the NDK doesn't have the structs defined, so define
// them here.
// Adapted from platform-linux.cc in V8
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
#include <signal.h>
#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
struct sigcontext {
uint32_t trap_no;
uint32_t error_code;
uint32_t oldmask;
uint32_t arm_r0;
uint32_t arm_r1;
uint32_t arm_r2;
uint32_t arm_r3;
uint32_t arm_r4;
uint32_t arm_r5;
uint32_t arm_r6;
uint32_t arm_r7;
uint32_t arm_r8;
uint32_t arm_r9;
uint32_t arm_r10;
uint32_t arm_fp;
uint32_t arm_ip;
uint32_t arm_sp;
uint32_t arm_lr;
uint32_t arm_pc;
uint32_t arm_cpsr;
uint32_t fault_address;
};
typedef uint32_t __sigset_t;
typedef struct sigcontext mcontext_t;
typedef struct ucontext {
uint32_t uc_flags;
struct ucontext* uc_link;
stack_t uc_stack;
mcontext_t uc_mcontext;
__sigset_t uc_sigmask;
} ucontext_t;
#endif
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_

View File

@@ -1,89 +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/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];
HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
sys_close(fds[1]);
// wait for an ACK from the server
char b;
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,468 +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"
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[256];
const ssize_t n = readlink(path, buf, sizeof(buf) - 1);
if (n == -1) {
return false;
}
buf[n] = 0;
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[256];
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 std::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;
}
std::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(std::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,133 +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>
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 std::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 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.
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(std::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_;
std::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,43 +72,22 @@
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#if !defined(__ANDROID__)
#include <sys/signal.h>
#endif
#include <sys/syscall.h>
#if !defined(__ANDROID__)
#include <sys/ucontext.h>
#include <sys/user.h>
#endif
#include <sys/wait.h>
#if !defined(__ANDROID__)
#include <ucontext.h>
#endif
#include <unistd.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "common/linux/linux_libc_support.h"
#include "common/memory.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#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"
#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
// 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 {
@@ -133,26 +112,23 @@ ExceptionHandler::ExceptionHandler(const std::string &dump_path,
MinidumpCallback callback,
void *callback_context,
bool install_handler)
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
handler_installed_(install_handler)
{
Init(dump_path, -1);
}
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
dump_path_(),
handler_installed_(install_handler),
crash_handler_(NULL) {
set_dump_path(dump_path);
ExceptionHandler::ExceptionHandler(const std::string &dump_path,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd)
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
handler_installed_(install_handler)
{
Init(dump_path, server_fd);
if (install_handler) {
InstallHandlers();
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.
@@ -160,27 +136,6 @@ ExceptionHandler::~ExceptionHandler() {
UninstallHandlers();
}
void ExceptionHandler::Init(const std::string &dump_path,
const int server_fd)
{
crash_handler_ = NULL;
if (0 <= server_fd)
crash_generation_client_
.reset(CrashGenerationClient::TryCreate(server_fd));
if (handler_installed_)
InstallHandlers();
if (!IsOutOfProcess())
set_dump_path(dump_path);
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.
bool ExceptionHandler::InstallHandlers() {
// We run the signal handlers on an alternative stack because we might have
@@ -196,7 +151,7 @@ bool ExceptionHandler::InstallHandlers() {
stack.ss_sp = signal_stack;
stack.ss_size = kSigStackSize;
if (sys_sigaltstack(&stack, NULL) == -1)
if (sigaltstack(&stack, NULL) == -1)
return false;
struct sigaction sa;
@@ -216,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.
@@ -227,11 +181,7 @@ 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();
}
@@ -253,15 +203,12 @@ void ExceptionHandler::UpdateNextID() {
}
}
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
// crash_handler_ = callback;
// }
// 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()) {
@@ -280,28 +227,13 @@ 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 {
@@ -316,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;
}
@@ -331,39 +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 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;
@@ -377,40 +284,19 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
ThreadArgument thread_arg;
thread_arg.handler = this;
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:";
sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1);
sys_write(2, strerror(errno), strlen(strerror(errno)));
sys_write(2, "\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
prctl(PR_SET_PTRACER, child, 0, 0, 0);
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:";
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);
@@ -425,90 +311,12 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
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:";
sys_write(2, msg, sizeof(msg) - 1);
sys_write(2, strerror(errno), strlen(strerror(errno)));
sys_write(2, "\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:";
sys_write(2, msg, sizeof(msg) - 1);
sys_write(2, strerror(errno), strlen(strerror(errno)));
sys_write(2, "\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) {
return google_breakpad::WriteMinidump(next_minidump_path_c_,
crashing_process,
context,
context_size,
mapping_list_);
}
// static
bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
MinidumpCallback callback,
void* callback_context) {
ExceptionHandler eh(dump_path, NULL, callback, callback_context, false);
return eh.WriteMinidump();
}
bool ExceptionHandler::WriteMinidump() {
#if !defined(__ARM_EABI__)
// 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();
bool success = GenerateDump(&context);
UpdateNextID();
return success;
#else
return false;
#endif // !defined(__ARM_EABI__)
}
void ExceptionHandler::AddMappingInfo(const std::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(), std::min(name.size(), sizeof(info)));
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,28 +30,13 @@
#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>
#if defined(__ANDROID__)
#include "client/linux/android_ucontext.h"
#endif
#include "client/linux/crash_generation/crash_generation_client.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "google_breakpad/common/minidump_format.h"
#include "processor/scoped_ptr.h"
struct sigaction;
namespace google_breakpad {
class ExceptionHandler;
// ExceptionHandler
//
// ExceptionHandler can write a minidump file when an exception occurs,
@@ -112,7 +97,7 @@ class ExceptionHandler {
// 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,
@@ -131,18 +116,6 @@ class ExceptionHandler {
FilterCallback filter, MinidumpCallback callback,
void *callback_context,
bool install_handler);
// Creates a new ExceptionHandler instance that can attempt to
// perform out-of-process dump generation if server_fd is valid. If
// server_fd is invalid, in-process dump generation will be
// used. See the above ctor for a description of the other
// parameters.
ExceptionHandler(const std::string& dump_path,
FilterCallback filter, MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd);
~ExceptionHandler();
// Get and set the minidump path.
@@ -173,35 +146,13 @@ 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 std::string& name,
const u_int8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset);
private:
void Init(const std::string &dump_path,
const int server_fd);
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);
@@ -214,8 +165,6 @@ 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_;
@@ -239,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,38 +27,33 @@
// (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/linux_libc_support.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;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
// Length of a formatted GUID string =
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
const int kGUIDStringSize = 37;
static void sigchld_handler(int signo) { }
class ExceptionHandlerTest : public ::testing::Test {
@@ -79,7 +74,7 @@ class ExceptionHandlerTest : public ::testing::Test {
};
TEST(ExceptionHandlerTest, Simple) {
ExceptionHandler handler(TEMPDIR, NULL, NULL, NULL, true);
ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
}
static bool DoneCallback(const char* dump_path,
@@ -89,7 +84,7 @@ static bool DoneCallback(const char* dump_path,
if (!succeeded)
return succeeded;
int fd = (intptr_t) context;
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));
@@ -105,9 +100,9 @@ TEST(ExceptionHandlerTest, ChildCrash) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
true);
*reinterpret_cast<volatile int*>(NULL) = 0;
*reinterpret_cast<int*>(NULL) = 0;
}
close(fds[1]);
@@ -126,14 +121,14 @@ TEST(ExceptionHandlerTest, ChildCrash) {
ASSERT_TRUE(pfd.revents & POLLIN);
uint32_t len;
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)2048);
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(TEMPDIR) + "/" + filename +
const std::string minidump_filename = std::string("/tmp/") + filename +
".dmp";
struct stat st;
@@ -142,536 +137,21 @@ TEST(ExceptionHandlerTest, ChildCrash) {
unlink(minidump_filename.c_str());
}
// Test that memory around the instruction pointer is written
// to the dump as a MinidumpMemoryRegion.
TEST(ExceptionHandlerTest, InstructionPointerMemory) {
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(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// 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]);
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGILL);
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)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)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(TEMPDIR) + "/" + filename +
".dmp";
struct stat 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_filename);
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_filename.c_str());
free(filename);
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the low end.
TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
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(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// 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]);
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGILL);
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)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)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(TEMPDIR) + "/" + filename +
".dmp";
struct stat 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_filename);
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_filename.c_str());
free(filename);
}
// Test that the memory region around the instruction pointer is
// bounded correctly on the high end.
TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
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(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// 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]);
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGILL);
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)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)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(TEMPDIR) + "/" + filename +
".dmp";
struct stat 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_filename);
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_filename.c_str());
free(filename);
}
// Ensure that an extra memory block doesn't get added when the
// instruction pointer is not in mapped memory.
TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
int fds[2];
ASSERT_NE(pipe(fds), -1);
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1],
true);
// Try calling a NULL pointer.
typedef void (*void_function)(void);
void_function memory_function =
reinterpret_cast<void_function>(NULL);
memory_function();
}
close(fds[1]);
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(WTERMSIG(status), SIGSEGV);
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)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)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(TEMPDIR) + "/" + filename +
".dmp";
struct stat 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_filename);
ASSERT_TRUE(minidump.Read());
MinidumpException* exception = minidump.GetException();
MinidumpMemoryList* memory_list = minidump.GetMemoryList();
ASSERT_TRUE(exception);
ASSERT_TRUE(memory_list);
ASSERT_EQ((unsigned int)1, memory_list->region_count());
unlink(minidump_filename.c_str());
free(filename);
}
static bool SimpleCallback(const char* dump_path,
const char* minidump_id,
void* context,
bool succeeded) {
if (!succeeded)
return succeeded;
string* minidump_file = reinterpret_cast<string*>(context);
minidump_file->append(dump_path);
minidump_file->append("/");
minidump_file->append(minidump_id);
minidump_file->append(".dmp");
return true;
}
// 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 u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
ASSERT_TRUE(memory);
string minidump_filename;
ExceptionHandler handler(TEMPDIR, NULL, SimpleCallback,
(void*)&minidump_filename, true);
// Add info about the anonymous memory mapping.
handler.AddMappingInfo(kMemoryName,
kModuleGUID,
kMemoryAddress,
kMemorySize,
0);
handler.WriteMinidump();
// 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_filename);
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_filename.c_str());
}
static const unsigned kControlMsgSize =
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
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];
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];
@@ -712,10 +192,11 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
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;
@@ -756,7 +237,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
ASSERT_NE(crashing_pid, -1);
ASSERT_NE(signal_fd, -1);
char templ[] = TEMPDIR "/exception-handler-unittest-XXXXXX";
char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
mktemp(templ);
ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context,
kCrashContextSize));

View File

@@ -37,7 +37,7 @@
#include <errno.h>
#include <string.h>
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
namespace google_breakpad {

View File

@@ -34,7 +34,7 @@
#include <assert.h>
#include <string.h>
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
namespace google_breakpad {

View File

@@ -36,14 +36,8 @@
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);
@@ -75,9 +69,9 @@ TEST(LineReaderTest, OneLineTerminated) {
LineReader reader(fd);
const char *line;
unsigned int len;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, (unsigned int)1);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -96,7 +90,7 @@ TEST(LineReaderTest, OneLine) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -115,13 +109,13 @@ TEST(LineReaderTest, TwoLinesTerminated) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, (unsigned)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(len, (unsigned)1);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@@ -140,13 +134,13 @@ TEST(LineReaderTest, TwoLines) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, (unsigned)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(len, (unsigned)1);
ASSERT_EQ(len, 1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);

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
@@ -34,32 +34,25 @@
#include "client/linux/minidump_writer/linux_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include "client/linux/minidump_writer/directory_reader.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
#include "common/linux/linux_syscall_support.h"
// Suspend a thread by attaching to it.
static bool SuspendThread(pid_t pid) {
@@ -75,26 +68,6 @@ static bool SuspendThread(pid_t pid) {
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;
}
@@ -103,22 +76,11 @@ static bool ResumeThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// 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;
}
namespace google_breakpad {
LinuxDumper::LinuxDumper(int pid)
: pid_(pid),
threads_suspended_(false),
threads_suspened_(false),
threads_(&allocator_, 8),
mappings_(&allocator_) {
}
@@ -129,30 +91,22 @@ bool LinuxDumper::Init() {
}
bool LinuxDumper::ThreadsSuspend() {
if (threads_suspended_)
if (threads_suspened_)
return true;
for (size_t i = 0; i < threads_.size(); ++i) {
if (!SuspendThread(threads_[i])) {
// If the thread either disappeared before we could attach to it, or if
// it was part of the seccomp sandbox's trusted code, it is OK to
// silently drop it from the minidump.
memmove(&threads_[i], &threads_[i+1],
(threads_.size() - i - 1) * sizeof(threads_[i]));
threads_.resize(threads_.size() - 1);
--i;
}
}
threads_suspended_ = true;
return threads_.size() > 0;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= SuspendThread(threads_[i]);
threads_suspened_ = true;
return good;
}
bool LinuxDumper::ThreadsResume() {
if (!threads_suspended_)
if (!threads_suspened_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
threads_suspended_ = false;
threads_suspened_ = false;
return good;
}
@@ -199,73 +153,12 @@ LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const {
my_itos(path + 6, pid, pid_len);
memcpy(path + 6 + pid_len, "/", 1);
memcpy(path + 6 + pid_len + 1, node, node_len);
path[total_length] = '\0';
}
bool
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;
// Special-case linux-gate because it's not a real file.
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
const uintptr_t kPageSize = getpagesize();
void* linux_gate = NULL;
if (pid_ == sys_getpid()) {
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
} else {
linux_gate = allocator_.Alloc(kPageSize);
CopyFromProcess(linux_gate, pid_,
reinterpret_cast<const void*>(mapping.start_addr),
kPageSize);
}
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
}
char filename[NAME_MAX];
size_t filename_len = my_strlen(mapping.name);
assert(filename_len < NAME_MAX);
if (filename_len >= NAME_MAX)
return false;
memcpy(filename, mapping.name, filename_len);
filename[filename_len] = '\0';
bool filename_modified = HandleDeletedFileInMapping(filename);
int fd = sys_open(filename, O_RDONLY, 0);
if (fd < 0)
return false;
struct kernel_stat st;
if (sys_fstat(fd, &st) != 0) {
sys_close(fd);
return false;
}
#if defined(__x86_64)
#define sys_mmap2 sys_mmap
#endif
void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
sys_close(fd);
if (base == MAP_FAILED)
return false;
bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier);
sys_munmap(base, st.st_size);
if (success && member && filename_modified) {
mappings_[mapping_id]->name[filename_len -
sizeof(kDeletedSuffix) + 1] = '\0';
}
return success;
memcpy(path + total_length, "\0", 1);
}
void*
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
char auxv_path[NAME_MAX];
char auxv_path[80];
BuildProcPath(auxv_path, pid, "auxv");
// If BuildProcPath errors out due to invalid input, we'll handle it when
@@ -295,7 +188,7 @@ LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const {
bool
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
char maps_path[NAME_MAX];
char maps_path[80];
BuildProcPath(maps_path, pid_, "maps");
// linux_gate_loc is the beginning of the kernel's mapping of
@@ -322,36 +215,25 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
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 && result->size()) {
MappingInfo* module = (*result)[result->size() - 1];
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;
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))
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);
}
@@ -368,7 +250,7 @@ LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const {
// Parse /proc/$pid/task to list all the threads of the process identified by
// pid.
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
char task_path[NAME_MAX];
char task_path[80];
BuildProcPath(task_path, pid_, "task");
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
@@ -398,12 +280,12 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
}
// Read thread info from /proc/$pid/status.
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// 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
// available.
// availible.
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
assert(info != NULL);
char status_path[NAME_MAX];
char status_path[80];
BuildProcPath(status_path, tid, "status");
const int fd = open(status_path, O_RDONLY);
@@ -429,22 +311,15 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
if (info->ppid == -1 || info->tgid == -1)
return false;
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 ||
sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
return false;
}
#if !defined(__ANDROID__)
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
return false;
}
#endif
#if defined(__i386)
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
return false;
#endif
#if defined(__i386) || defined(__x86_64)
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,
@@ -462,14 +337,15 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#else
#error "This code hasn't been ported to your platform yet."
#endif
return GetStackInfo(&info->stack, &info->stack_len,
(uintptr_t) stack_pointer);
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
@@ -477,31 +353,42 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
// 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 = 55;
unsigned long tmp;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
@@ -509,9 +396,8 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* 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) {
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
tmp = 0;
}
memcpy(local + done, &tmp, l);
done += l;
}
@@ -530,42 +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];
BuildProcPath(exe_link, pid_, "exe");
ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX);
if (new_path_len <= 0 || new_path_len == NAME_MAX)
return false;
new_path[new_path_len] = '\0';
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;
}
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
@@ -31,41 +31,19 @@
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#include <elf.h>
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#if !defined(__ANDROID__)
#include <sys/user.h>
#endif
#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(__ANDROID__)
#if defined(__i386)
typedef Elf32_auxv_t elf_aux_entry;
#else
// Android is missing this structure definition
typedef struct
{
uint32_t a_type; /* Entry type */
union
{
uint32_t a_val; /* Integer value */
} a_un;
} elf_aux_entry;
#if !defined(AT_SYSINFO_EHDR)
#define AT_SYSINFO_EHDR 33
#endif
#endif // __ANDROID__
#elif defined(__x86_64__)
typedef Elf64_auxv_t elf_aux_entry;
#endif
@@ -84,24 +62,13 @@ struct ThreadInfo {
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)
#if defined(__ANDROID__)
struct pt_regs regs;
#else
struct user_regs regs;
struct user_fpregs fpregs;
#endif // __ANDROID__
#endif
};
@@ -151,13 +118,6 @@ class LinuxDumper {
// without any slashes.
void BuildProcPath(char* path, pid_t pid, const char* node) const;
// 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)]);
// 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
@@ -168,22 +128,11 @@ class LinuxDumper {
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const;
bool EnumerateThreads(wasteful_vector<pid_t>* result) const;
// 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;
const pid_t pid_;
mutable PageAllocator allocator_;
bool threads_suspended_;
bool threads_suspened_;
wasteful_vector<pid_t> threads_; // the ids of all the threads
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
};

View File

@@ -27,47 +27,15 @@
// (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 <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/memory.h"
#include "breakpad_googletest_includes.h"
using std::string;
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxDumperTest;
string GetHelperBinary() {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
return "";
}
string helper_path(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;
}
}
TEST(LinuxDumperTest, Setup) {
@@ -87,7 +55,7 @@ TEST(LinuxDumperTest, ThreadList) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), (size_t)1);
ASSERT_GE(dumper.threads().size(), 1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
@@ -97,155 +65,6 @@ TEST(LinuxDumperTest, ThreadList) {
}
}
// 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(LinuxDumperTest, 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);
char* mapping =
reinterpret_cast<char*>(mmap(NULL,
kMappingSize,
PROT_READ,
MAP_SHARED,
fd,
0));
ASSERT_TRUE(mapping);
const u_int64_t kMappingAddress = reinterpret_cast<u_int64_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 LinuxDumper interpreted the mappings properly.
LinuxDumper 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(LinuxDumperTest, 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 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;
read(fds[0], &junk, sizeof(junk));
close(fds[0]);
// Child is ready now.
LinuxDumper 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.ThreadInfoGet(dumper.threads()[i], &one_thread));
// 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);
}
kill(child_pid, SIGKILL);
}
TEST(LinuxDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxDumper dumper(pid);
@@ -275,155 +94,25 @@ TEST(LinuxDumperTest, BuildProcPath) {
#endif
}
#if !defined(__ARM_EABI__)
// Ensure that the linux-gate VDSO is included in the mapping list.
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
ASSERT_TRUE(linux_gate_loc);
bool found_linux_gate = false;
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;
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));
}
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(LinuxDumperTest, LinuxGateMappingID) {
LinuxDumper 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(LinuxDumperTest, 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;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxDumper 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(LinuxDumperTest, 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];
ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1);
ASSERT_NE(len, -1);
exe_name[len] = '\0';
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;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxDumper 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]);
}

View File

@@ -1,85 +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 "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) {
volatile pid_t thread_id = syscall(__NR_gettid);
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 < 2) {
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;
}
pthread_t threads[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, NULL);
}
// Signal parent that this process has started all threads.
uint8_t byte = 1;
write(pipefd, &byte, sizeof(byte));
thread_function(NULL);
return 0;
}

View File

@@ -1,75 +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. */
/* minidump_extension_linux.h: A definition of exception codes for
* Linux
*
* (This is C99 source, please don't corrupt it with C++.)
*
* Author: Adam Langley
* Split into its own file: Markus Gutschke */
#ifndef SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
#define SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
#include <stddef.h>
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
// These are additional minidump stream values which are specific to the linux
// breakpad implementation.
enum {
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */
MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */
MD_LINUX_DSO_DEBUG = 0x4767000A /* DSO data */
};
typedef struct {
void* addr;
MDRVA name;
void* ld;
} MDRawLinkMap;
typedef struct {
u_int32_t version;
MDRVA map;
u_int32_t dso_count;
void* brk;
void* ldbase;
void* dynamic;
} MDRawDebug;
#endif // SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_

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
@@ -48,35 +48,33 @@
#include <errno.h>
#include <fcntl.h>
#if !defined(__ANDROID__)
#include <link.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#if !defined(__ANDROID__)
#include <sys/ucontext.h>
#include <sys/user.h>
#endif
#include <sys/utsname.h>
#include <algorithm>
#include "client/minidump_file_writer.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/common/minidump_cpu_amd64.h"
#include "google_breakpad/common/minidump_cpu_x86.h"
#if defined(__ANDROID__)
#include "client/linux/android_link.h"
#include "client/linux/android_ucontext.h"
#endif
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_extension_linux.h"
#include "client/linux/minidump_writer//linux_dumper.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
#include "common/linux/linux_syscall_support.h"
// These are additional minidump stream values which are specific to the linux
// breakpad implementation.
enum {
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */
};
// Minidump defines register structures which are different from the raw
// structures which we get from the kernel. These are platform specific
@@ -250,21 +248,21 @@ static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
out->flt_save.control_word = info.fpregs.cwd;
out->flt_save.status_word = info.fpregs.swd;
out->flt_save.tag_word = info.fpregs.ftw;
out->flt_save.tag_word = info.fpregs.twd;
out->flt_save.error_opcode = info.fpregs.fop;
out->flt_save.error_offset = info.fpregs.rip;
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.data_offset = info.fpregs.rdp;
out->flt_save.data_selector = 0; // We don't have this.
out->flt_save.data_selector = 0; // We don't have this.
out->flt_save.mx_csr = info.fpregs.mxcsr;
out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
out->flt_save.mx_csr_mask = info.fpregs.mxcsr_mask;
memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
}
static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
const struct _libc_fpstate* fpregs) {
const greg_t* regs = uc->uc_mcontext.gregs;
const greg_t* regs = uc->gregs;
out->context_flags = MD_CONTEXT_AMD64_FULL;
@@ -301,64 +299,14 @@ static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
out->flt_save.error_opcode = fpregs->fop;
out->flt_save.error_offset = fpregs->rip;
out->flt_save.data_offset = fpregs->rdp;
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.data_selector = 0; // We don't have this.
out->flt_save.mx_csr = fpregs->mxcsr;
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
out->flt_save.mx_csr_mask = fpregs->mxcsr_mask;
memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
}
#elif defined(__ARMEL__)
typedef MDRawContextARM RawContextCPU;
static void CPUFillFromThreadInfo(MDRawContextARM *out,
const google_breakpad::ThreadInfo &info) {
out->context_flags = MD_CONTEXT_ARM_FULL;
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
out->iregs[i] = info.regs.uregs[i];
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
out->cpsr = 0;
#if !defined(__ANDROID__)
out->float_save.fpscr = info.fpregs.fpsr |
(static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
// TODO: sort this out, actually collect floating point registers
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
#endif
}
static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
const struct _libc_fpstate* fpregs) {
out->context_flags = MD_CONTEXT_ARM_FULL;
out->iregs[0] = uc->uc_mcontext.arm_r0;
out->iregs[1] = uc->uc_mcontext.arm_r1;
out->iregs[2] = uc->uc_mcontext.arm_r2;
out->iregs[3] = uc->uc_mcontext.arm_r3;
out->iregs[4] = uc->uc_mcontext.arm_r4;
out->iregs[5] = uc->uc_mcontext.arm_r5;
out->iregs[6] = uc->uc_mcontext.arm_r6;
out->iregs[7] = uc->uc_mcontext.arm_r7;
out->iregs[8] = uc->uc_mcontext.arm_r8;
out->iregs[9] = uc->uc_mcontext.arm_r9;
out->iregs[10] = uc->uc_mcontext.arm_r10;
out->iregs[11] = uc->uc_mcontext.arm_fp;
out->iregs[12] = uc->uc_mcontext.arm_ip;
out->iregs[13] = uc->uc_mcontext.arm_sp;
out->iregs[14] = uc->uc_mcontext.arm_lr;
out->iregs[15] = uc->uc_mcontext.arm_pc;
out->cpsr = uc->uc_mcontext.arm_cpsr;
// TODO: fix this after fixing ExceptionHandler
out->float_save.fpscr = 0;
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
}
#else
#error "This code has not been ported to your platform yet."
#endif
@@ -369,21 +317,13 @@ class MinidumpWriter {
public:
MinidumpWriter(const char* filename,
pid_t crashing_pid,
const ExceptionHandler::CrashContext* context,
const MappingList& mappings)
const ExceptionHandler::CrashContext* context)
: filename_(filename),
siginfo_(&context->siginfo),
ucontext_(&context->context),
#if !defined(__ARM_EABI__)
float_state_(&context->float_state),
#else
// TODO: fix this after fixing ExceptionHandler
float_state_(NULL),
#endif
crashing_tid_(context->tid),
dumper_(crashing_pid),
memory_blocks_(dumper_.allocator()),
mapping_list_(mappings) {
dumper_(crashing_pid) {
}
bool Init() {
@@ -397,32 +337,9 @@ class MinidumpWriter {
}
bool Dump() {
// The dynamic linker makes information available that helps gdb find all
// DSOs loaded into the program. If we can access this information, we dump
// it to a MD_LINUX_DSO_DEBUG stream.
struct r_debug* r_debug = NULL;
uint32_t dynamic_length = 0;
#if !defined(__ANDROID__)
// The Android NDK is missing structure definitions for most of this.
// For now, it's simpler just to skip it.
for (int i = 0;;) {
ElfW(Dyn) dyn;
dynamic_length += sizeof(dyn);
dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn));
if (dyn.d_tag == DT_DEBUG) {
r_debug = (struct r_debug*)dyn.d_un.d_ptr;
continue;
} else if (dyn.d_tag == DT_NULL) {
break;
}
}
#endif
// A minidump file contains a number of tagged streams. This is the number
// of stream which we write.
unsigned kNumWriters = 12;
if (r_debug)
++kNumWriters;
static const unsigned kNumWriters = 11;
TypedMDRVA<MDRawHeader> header(&minidump_writer_);
TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
@@ -449,10 +366,6 @@ class MinidumpWriter {
return false;
dir.CopyIndex(dir_index++, &dirent);
if (!WriteMemoryListStream(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
if (!WriteExceptionStream(&dirent))
return false;
dir.CopyIndex(dir_index++, &dirent);
@@ -491,18 +404,11 @@ class MinidumpWriter {
NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent);
dirent.stream_type = MD_LINUX_MAPS;
dirent.stream_type = MD_LINUX_AUXV;
if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent);
if (r_debug) {
dirent.stream_type = MD_LINUX_DSO_DEBUG;
if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length))
NullifyDirectoryEntry(&dirent);
dir.CopyIndex(dir_index++, &dirent);
}
// If you add more directory entries, don't forget to update kNumWriters,
// above.
@@ -510,123 +416,6 @@ class MinidumpWriter {
return true;
}
// Check if the top of the stack is part of a system call that has been
// redirected by the seccomp sandbox. If so, try to pop the stack frames
// all the way back to the point where the interception happened.
void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
uint8_t* stack_copy) {
#if defined(__x86_64)
u_int64_t bp = cpu->rbp;
u_int64_t top = thread.stack.start_of_memory_range;
for (int i = 4; i--; ) {
if (bp < top ||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
thread.stack.memory.data_size ||
bp & 1) {
break;
}
uint64_t old_top = top;
top = bp;
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
memcpy(&bp, bp_addr, sizeof(bp));
if (bp == 0xDEADBEEFDEADBEEFull) {
struct {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t deadbeef;
uint64_t rbp;
uint64_t fakeret;
uint64_t ret;
/* char redzone[128]; */
} seccomp_stackframe;
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
sizeof(seccomp_stackframe) >
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
break;
}
memcpy(&seccomp_stackframe,
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
sizeof(seccomp_stackframe));
cpu->rbx = seccomp_stackframe.rbx;
cpu->rcx = seccomp_stackframe.rcx;
cpu->rdx = seccomp_stackframe.rdx;
cpu->rsi = seccomp_stackframe.rsi;
cpu->rdi = seccomp_stackframe.rdi;
cpu->rbp = seccomp_stackframe.rbp;
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
cpu->r8 = seccomp_stackframe.r8;
cpu->r9 = seccomp_stackframe.r9;
cpu->r10 = seccomp_stackframe.r10;
cpu->r11 = seccomp_stackframe.r11;
cpu->r12 = seccomp_stackframe.r12;
cpu->r13 = seccomp_stackframe.r13;
cpu->r14 = seccomp_stackframe.r14;
cpu->r15 = seccomp_stackframe.r15;
cpu->rip = seccomp_stackframe.fakeret;
return;
}
}
#elif defined(__i386)
u_int32_t bp = cpu->ebp;
u_int32_t top = thread.stack.start_of_memory_range;
for (int i = 4; i--; ) {
if (bp < top ||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
thread.stack.memory.data_size ||
bp & 1) {
break;
}
uint32_t old_top = top;
top = bp;
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
memcpy(&bp, bp_addr, sizeof(bp));
if (bp == 0xDEADBEEFu) {
struct {
uint32_t edi;
uint32_t esi;
uint32_t edx;
uint32_t ecx;
uint32_t ebx;
uint32_t deadbeef;
uint32_t ebp;
uint32_t fakeret;
uint32_t ret;
} seccomp_stackframe;
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
sizeof(seccomp_stackframe) >
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
break;
}
memcpy(&seccomp_stackframe,
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
sizeof(seccomp_stackframe));
cpu->ebx = seccomp_stackframe.ebx;
cpu->ecx = seccomp_stackframe.ecx;
cpu->edx = seccomp_stackframe.edx;
cpu->esi = seccomp_stackframe.esi;
cpu->edi = seccomp_stackframe.edi;
cpu->ebp = seccomp_stackframe.ebp;
cpu->esp = top + 4*sizeof(void*);
cpu->eip = seccomp_stackframe.fakeret;
return;
}
}
#endif
}
// Write information about the threads.
bool WriteThreadListStream(MDRawDirectory* dirent) {
const unsigned num_threads = dumper_.threads().size();
@@ -648,7 +437,7 @@ class MinidumpWriter {
// we used the actual state of the thread we would find it running in the
// signal handler with the alternative stack, which would be deeply
// unhelpful.
if ((pid_t)thread.thread_id == crashing_tid_) {
if (thread.thread_id == crashing_tid_) {
const void* stack;
size_t stack_len;
if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
@@ -661,57 +450,11 @@ class MinidumpWriter {
memory.Copy(stack_copy, stack_len);
thread.stack.start_of_memory_range = (uintptr_t) (stack);
thread.stack.memory = memory.location();
memory_blocks_.push_back(thread.stack);
// Copy 256 bytes around crashing instruction pointer to minidump.
const size_t kIPMemorySize = 256;
u_int64_t ip = GetInstructionPointer();
// Bound it to the upper and lower bounds of the memory map
// it's contained within. If it's not in mapped memory,
// don't bother trying to write it.
bool ip_is_mapped = false;
MDMemoryDescriptor ip_memory_d;
for (unsigned j = 0; j < dumper_.mappings().size(); ++j) {
const MappingInfo& mapping = *dumper_.mappings()[j];
if (ip >= mapping.start_addr &&
ip < mapping.start_addr + mapping.size) {
ip_is_mapped = true;
// Try to get 128 bytes before and after the IP, but
// settle for whatever's available.
ip_memory_d.start_of_memory_range =
std::max(mapping.start_addr,
uintptr_t(ip - (kIPMemorySize / 2)));
uintptr_t end_of_range =
std::min(uintptr_t(ip + (kIPMemorySize / 2)),
uintptr_t(mapping.start_addr + mapping.size));
ip_memory_d.memory.data_size =
end_of_range - ip_memory_d.start_of_memory_range;
break;
}
}
if (ip_is_mapped) {
UntypedMDRVA ip_memory(&minidump_writer_);
if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
return false;
uint8_t* memory_copy =
(uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size);
dumper_.CopyFromProcess(
memory_copy,
thread.thread_id,
reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
ip_memory_d.memory.data_size);
ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
ip_memory_d.memory = ip_memory.location();
memory_blocks_.push_back(ip_memory_d);
}
TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
if (!cpu.Allocate())
return false;
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
thread.thread_context = cpu.location();
crashing_thread_context_ = cpu.location();
} else {
@@ -728,14 +471,11 @@ class MinidumpWriter {
memory.Copy(stack_copy, info.stack_len);
thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
thread.stack.memory = memory.location();
memory_blocks_.push_back(thread.stack);
TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
if (!cpu.Allocate())
return false;
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
CPUFillFromThreadInfo(cpu.get(), info);
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
thread.thread_context = cpu.location();
}
@@ -746,8 +486,8 @@ class MinidumpWriter {
}
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
if (mapping.name[0] == 0 || // only want modules with filenames.
mapping.offset || // only want to include one mapping per shared lib.
if (mapping.name[0] == 0 || // we only want modules with filenames.
mapping.offset || // we only want to include one mapping per shared lib.
mapping.size < 4096) { // too small to get a signature for.
return false;
}
@@ -755,147 +495,96 @@ class MinidumpWriter {
return true;
}
// If there is caller-provided information about this mapping
// in the mapping_list_ list, return true. Otherwise, return false.
bool HaveMappingInfo(const MappingInfo& mapping) {
for (MappingList::const_iterator iter = mapping_list_.begin();
iter != mapping_list_.end();
++iter) {
// Ignore any mappings that are wholly contained within
// mappings in the mapping_info_ list.
if (mapping.start_addr >= iter->first.start_addr &&
(mapping.start_addr + mapping.size) <=
(iter->first.start_addr + iter->first.size)) {
return true;
}
}
return false;
}
// Write information about the mappings in effect. Because we are using the
// minidump format, the information about the mappings is pretty limited.
// Because of this, we also include the full, unparsed, /proc/$x/maps file in
// another stream in the file.
bool WriteMappings(MDRawDirectory* dirent) {
const unsigned num_mappings = dumper_.mappings().size();
unsigned num_output_mappings = mapping_list_.size();
unsigned num_output_mappings = 0;
for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
const MappingInfo& mapping = *dumper_.mappings()[i];
if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping))
if (ShouldIncludeMapping(mapping))
num_output_mappings++;
}
TypedMDRVA<uint32_t> list(&minidump_writer_);
if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
if (!list.AllocateObjectAndArray(num_output_mappings, sizeof(MDRawModule)))
return false;
dirent->stream_type = MD_MODULE_LIST_STREAM;
dirent->location = list.location();
*list.get() = num_output_mappings;
// First write all the mappings from the dumper
unsigned int j = 0;
for (unsigned i = 0; i < num_mappings; ++i) {
for (unsigned i = 0, j = 0; i < num_mappings; ++i) {
const MappingInfo& mapping = *dumper_.mappings()[i];
if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping))
if (!ShouldIncludeMapping(mapping))
continue;
MDRawModule mod;
if (!FillRawModule(mapping, true, i, mod, NULL))
my_memset(&mod, 0, sizeof(mod));
mod.base_of_image = mapping.start_addr;
mod.size_of_image = mapping.size;
const size_t filepath_len = my_strlen(mapping.name);
// Figure out file name from path
const char* filename_ptr = mapping.name + filepath_len - 1;
while (filename_ptr >= mapping.name) {
if (*filename_ptr == '/')
break;
filename_ptr--;
}
filename_ptr++;
const size_t filename_len = mapping.name + filepath_len - filename_ptr;
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
uint8_t* cv_ptr = cv_buf;
UntypedMDRVA cv(&minidump_writer_);
if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
return false;
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
}
// Next write all the mappings provided by the caller
for (MappingList::const_iterator iter = mapping_list_.begin();
iter != mapping_list_.end();
++iter) {
MDRawModule mod;
if (!FillRawModule(iter->first, false, 0, mod, iter->second))
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
cv_ptr += sizeof(cv_signature);
{
// We XOR the first page of the file to get a signature for it.
uint8_t xor_buf[sizeof(MDGUID)];
size_t done = 0;
uint8_t* signature = cv_ptr;
cv_ptr += sizeof(xor_buf);
my_memset(signature, 0, sizeof(xor_buf));
while (done < 4096) {
dumper_.CopyFromProcess(xor_buf, crashing_tid_,
(void *) (mod.base_of_image + done),
sizeof(xor_buf));
for (unsigned i = 0; i < sizeof(xor_buf); ++i)
signature[i] ^= xor_buf[i];
done += sizeof(xor_buf);
}
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
cv_ptr += sizeof(uint32_t);
}
// Write pdb_file_name
memcpy(cv_ptr, filename_ptr, filename_len + 1);
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
mod.cv_record = cv.location();
MDLocationDescriptor ld;
if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
return false;
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
mod.module_name_rva = ld.rva;
list.CopyIndexAfterObject(j++, &mod, sizeof(mod));
}
return true;
}
// Fill the MDRawModule |mod| with information about the provided
// |mapping|. If |identifier| is non-NULL, use it instead of calculating
// a file ID from the mapping.
bool FillRawModule(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
MDRawModule& mod,
const u_int8_t* identifier) {
my_memset(&mod, 0, MD_MODULE_SIZE);
mod.base_of_image = mapping.start_addr;
mod.size_of_image = mapping.size;
const size_t filepath_len = my_strlen(mapping.name);
// Figure out file name from path
const char* filename_ptr = mapping.name + filepath_len - 1;
while (filename_ptr >= mapping.name) {
if (*filename_ptr == '/')
break;
filename_ptr--;
}
filename_ptr++;
const size_t filename_len = mapping.name + filepath_len - filename_ptr;
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
uint8_t* cv_ptr = cv_buf;
UntypedMDRVA cv(&minidump_writer_);
if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
return false;
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
cv_ptr += sizeof(cv_signature);
uint8_t* signature = cv_ptr;
cv_ptr += sizeof(MDGUID);
if (identifier) {
// GUID was provided by caller.
memcpy(signature, identifier, sizeof(MDGUID));
} else {
dumper_.ElfFileIdentifierForMapping(mapping, member,
mapping_id, signature);
}
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
cv_ptr += sizeof(uint32_t);
// Write pdb_file_name
memcpy(cv_ptr, filename_ptr, filename_len + 1);
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
mod.cv_record = cv.location();
MDLocationDescriptor ld;
if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
return false;
mod.module_name_rva = ld.rva;
return true;
}
bool WriteMemoryListStream(MDRawDirectory* dirent) {
TypedMDRVA<uint32_t> list(&minidump_writer_);
if (!list.AllocateObjectAndArray(memory_blocks_.size(),
sizeof(MDMemoryDescriptor)))
return false;
dirent->stream_type = MD_MEMORY_LIST_STREAM;
dirent->location = list.location();
*list.get() = memory_blocks_.size();
for (size_t i = 0; i < memory_blocks_.size(); ++i) {
list.CopyIndexAfterObject(i, &memory_blocks_[i],
sizeof(MDMemoryDescriptor));
}
return true;
}
bool WriteExceptionStream(MDRawDirectory* dirent) {
TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
if (!exc.Allocate())
@@ -929,112 +618,15 @@ class MinidumpWriter {
return true;
}
bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
uint32_t dynamic_length) {
#if defined(__ANDROID__)
return false;
#else
// The caller provided us with a pointer to "struct r_debug". We can
// look up the "r_map" field to get a linked list of all loaded DSOs.
// Our list of DSOs potentially is different from the ones in the crashing
// process. So, we have to be careful to never dereference pointers
// directly. Instead, we use CopyFromProcess() everywhere.
// See <link.h> for a more detailed discussion of the how the dynamic
// loader communicates with debuggers.
// Count the number of loaded DSOs
int dso_count = 0;
struct r_debug debug_entry;
dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug,
sizeof(debug_entry));
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map;
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
ptr = map.l_next;
dso_count++;
}
MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA;
if (dso_count > 0) {
// If we have at least one DSO, create an array of MDRawLinkMap
// entries in the minidump file.
TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
if (!linkmap.AllocateArray(dso_count))
return false;
linkmap_rva = linkmap.location().rva;
int idx = 0;
// Iterate over DSOs and write their information to mini dump
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map;
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
ptr = map.l_next;
char filename[257] = { 0 };
if (map.l_name) {
dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name,
sizeof(filename) - 1);
}
MDLocationDescriptor location;
if (!minidump_writer_.WriteString(filename, 0, &location))
return false;
MDRawLinkMap entry;
entry.name = location.rva;
entry.addr = (void*)map.l_addr;
entry.ld = (void*)map.l_ld;
linkmap.CopyIndex(idx++, &entry);
}
}
// Write MD_LINUX_DSO_DEBUG record
TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
if (!debug.AllocateObjectAndArray(1, dynamic_length))
return false;
my_memset(debug.get(), 0, sizeof(MDRawDebug));
dirent->stream_type = MD_LINUX_DSO_DEBUG;
dirent->location = debug.location();
debug.get()->version = debug_entry.r_version;
debug.get()->map = linkmap_rva;
debug.get()->dso_count = dso_count;
debug.get()->brk = (void*)debug_entry.r_brk;
debug.get()->ldbase = (void*)debug_entry.r_ldbase;
debug.get()->dynamic = (void*)&_DYNAMIC;
char *dso_debug_data = new char[dynamic_length];
dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC,
dynamic_length);
debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
delete[] dso_debug_data;
return true;
#endif
}
private:
#if defined(__i386)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_ESP];
}
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.gregs[REG_EIP];
}
#elif defined(__x86_64)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_RSP];
}
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.gregs[REG_RIP];
}
#elif defined(__ARM_EABI__)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.arm_sp;
}
uintptr_t GetInstructionPointer() {
return ucontext_->uc_mcontext.arm_ip;
}
#else
#error "This code has not been ported to your platform yet."
#endif
@@ -1058,7 +650,7 @@ class MinidumpWriter {
{ "processor", -1, false },
{ "model", 0, false },
{ "stepping", 0, false },
{ "cpu family", 0, false },
{ "cpuid level", 0, false },
};
// processor_architecture should always be set, do this first
@@ -1067,8 +659,6 @@ class MinidumpWriter {
MD_CPU_ARCHITECTURE_X86;
#elif defined(__x86_64)
MD_CPU_ARCHITECTURE_AMD64;
#elif defined(__arm__)
MD_CPU_ARCHITECTURE_ARM;
#else
#error "Unknown CPU arch"
#endif
@@ -1087,7 +677,7 @@ class MinidumpWriter {
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
i++) {
CpuInfoEntry* entry = &cpu_info_table[i];
if (entry->found && i)
if (entry->found)
continue;
if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
const char* value = strchr(line, ':');
@@ -1136,7 +726,7 @@ class MinidumpWriter {
}
}
popline:
popline:
line_reader->PopLine(line_len);
}
sys_close(fd);
@@ -1173,57 +763,29 @@ class MinidumpWriter {
// We can't stat the files because several of the files that we want to
// read are kernel seqfiles, which always have a length of zero. So we have
// to read as much as we can into a buffer.
static const unsigned kBufSize = 1024 - 2*sizeof(void*);
struct Buffers {
struct Buffers* next;
size_t len;
uint8_t data[kBufSize];
} *buffers =
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
buffers->next = NULL;
buffers->len = 0;
static const unsigned kMaxFileSize = 1024;
uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
size_t total = 0;
for (struct Buffers* bufptr = buffers;;) {
size_t done = 0;
while (done < kMaxFileSize) {
ssize_t r;
do {
r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
r = sys_read(fd, data + done, kMaxFileSize - done);
} while (r == -1 && errno == EINTR);
if (r < 1)
break;
total += r;
bufptr->len += r;
if (bufptr->len == kBufSize) {
bufptr->next =
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
bufptr = bufptr->next;
bufptr->next = NULL;
bufptr->len = 0;
}
done += r;
}
sys_close(fd);
if (!total)
if (!done)
return false;
UntypedMDRVA memory(&minidump_writer_);
if (!memory.Allocate(total))
if (!memory.Allocate(done))
return false;
for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
// Check for special case of a zero-length buffer. This should only
// occur if a file's size happens to be a multiple of the buffer's
// size, in which case the final sys_read() will have resulted in
// zero bytes being read after the final buffer was just allocated.
if (buffers->len == 0) {
// This can only occur with final buffer.
assert(buffers->next == NULL);
continue;
}
memory.Copy(pos, &buffers->data, buffers->len);
pos += buffers->len;
}
memory.Copy(data, done);
*result = memory.location();
return true;
}
@@ -1276,8 +838,12 @@ class MinidumpWriter {
bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
const char* filename) {
char buf[NAME_MAX];
dumper_.BuildProcPath(buf, pid, filename);
char buf[80];
memcpy(buf, "/proc/", 6);
const unsigned pid_len = my_int_len(pid);
my_itos(buf + 6, pid, pid_len);
buf[6 + pid_len] = '/';
memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
return WriteFile(result, buf);
}
@@ -1289,28 +855,15 @@ class MinidumpWriter {
LinuxDumper dumper_;
MinidumpFileWriter minidump_writer_;
MDLocationDescriptor crashing_thread_context_;
// 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_;
// Additional information about some mappings provided by the caller.
const MappingList& mapping_list_;
};
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size) {
MappingList m;
return WriteMinidump(filename, crashing_process, blob, blob_size, m);
}
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings) {
if (blob_size != sizeof(ExceptionHandler::CrashContext))
return false;
const ExceptionHandler::CrashContext* context =
reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
MinidumpWriter writer(filename, crashing_process, context, mappings);
MinidumpWriter writer(filename, crashing_process, context);
if (!writer.Init())
return false;
return writer.Dump();

View File

@@ -33,17 +33,8 @@
#include <stdint.h>
#include <unistd.h>
#include <list>
#include <utility>
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// A list of <MappingInfo, GUID>
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
typedef std::list<MappingEntry> MappingList;
// Write a minidump to the filesystem. This function does not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
@@ -57,11 +48,6 @@ typedef std::list<MappingEntry> MappingList;
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size);
// This overload also allows passing a list of known mappings.
bool WriteMinidump(const char* filename, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings);
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_

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,34 +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.
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.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 "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "google_breakpad/processor/minidump.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
// 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;
@@ -77,10 +67,8 @@ TEST(MinidumpWriterTest, Setup) {
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
char templ[] = "/tmp/minidump-writer-unittest-XXXXXX";
mktemp(templ);
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(stat(templ, &st), 0);
@@ -89,308 +77,3 @@ TEST(MinidumpWriterTest, Setup) {
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 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 u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
ASSERT_TRUE(memory);
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;
memset(&context, 0, sizeof(context));
context.tid = 1;
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
mktemp(templ);
// Add information about the mapped memory.
MappingInfo info;
info.start_addr = kMemoryAddress;
info.size = kMemorySize;
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, 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);
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(templ);
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 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";
// mmap a file
char tempfile[] = TEMPDIR "/minidump-writer-unittest-temp-XXXXXX";
mktemp(tempfile);
int fd = open(tempfile, O_RDWR | O_CREAT, 0);
ASSERT_NE(-1, fd);
unlink(tempfile);
// fill with zeros
char buffer[kMemorySize];
memset(buffer, 0, kMemorySize);
ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize));
lseek(fd, 0, SEEK_SET);
char* memory =
reinterpret_cast<char*>(mmap(NULL,
kMemorySize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
0));
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory);
ASSERT_TRUE(memory);
close(fd);
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;
memset(&context, 0, sizeof(context));
context.tid = 1;
char dumpfile[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
mktemp(dumpfile);
// Add information about the mapped memory. Report it as being larger than
// it actually is.
MappingInfo info;
info.start_addr = kMemoryAddress - kMemorySize;
info.size = kMemorySize * 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, 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);
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());
unlink(dumpfile);
close(fds[1]);
}
TEST(MinidumpWriterTest, DeletedBinary) {
static const int kNumberOfThreadsInHelperProgram = 1;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) {
FAIL() << "readlink failed: " << strerror(errno);
exit(1);
}
string helper_path(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
FAIL() << "no trailing slash in path: " << helper_path;
exit(1);
}
helper_path.erase(pos + 1);
helper_path += "linux_dumper_unittest_helper";
// Copy binary to a temp file.
char binpath[] = TEMPDIR "/linux-dumper-unittest-helper-XXXXXX";
mktemp(binpath);
char cmdline[2 * PATH_MAX];
sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(), binpath);
ASSERT_EQ(0, system(cmdline));
ASSERT_EQ(0, chmod(binpath, 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,
binpath,
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;
read(fds[0], &junk, sizeof(junk));
close(fds[0]);
// Child is ready now.
// Unlink the test binary.
unlink(binpath);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX";
mktemp(templ);
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ, child_pid, &context, sizeof(context)));
kill(child_pid, SIGKILL);
struct stat st;
ASSERT_EQ(stat(templ, &st), 0);
ASSERT_GT(st.st_size, 0u);
Minidump minidump(templ);
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, 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
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());
unlink(templ);
}

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>
using std::string;
DEFINE_string(crash_server, "http://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() {
std::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

@@ -50,13 +50,10 @@ extern "C" {
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
// Keys for configuration file
// 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>
@@ -94,10 +91,6 @@ extern "C" {
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_"
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand"
// 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"
// Optional user-defined function to dec to decide if we should handle
// this crash or forward it along.
// Return true if you want Breakpad to handle it.
@@ -117,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

View File

@@ -55,10 +55,6 @@
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;
@@ -168,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;
}
@@ -269,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
@@ -290,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];
}
@@ -368,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;
}
@@ -401,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) {
@@ -430,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"];
@@ -515,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.
@@ -563,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,
@@ -574,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);
@@ -596,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]);
}
@@ -605,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;
@@ -614,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;
@@ -623,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_) {
@@ -716,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;
}
@@ -742,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

@@ -28,16 +28,12 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#import "OnDemandServer.h"
#import "Breakpad.h"
#if DEBUG
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ );
#else
#define PRINT_MACH_RESULT(result_, message_)
#define PRINT_BOOTSTRAP_RESULT(result_, message_)
#endif
//==============================================================================
@@ -71,44 +67,15 @@ 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 = bootstrap_register(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;
}
@@ -119,14 +86,15 @@ kern_return_t OnDemandServer::Initialize(const char *server_command,
kr = bootstrap_create_service(server_port_,
const_cast<char*>(service_name),
&service_port_);
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;
}
@@ -148,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.

View File

@@ -33,8 +33,6 @@
#import "common/mac/SimpleStringDictionary.h"
#import <Foundation/Foundation.h>
#include <mach/mach.h>
#import "client/mac/handler/minidump_generator.h"
#define VERBOSE 0
@@ -80,7 +78,7 @@ using google_breakpad::MinidumpGenerator;
namespace google_breakpad {
BOOL EnsureDirectoryPathExists(NSString *dirPath);
static BOOL EnsureDirectoryPathExists(NSString *dirPath);
//=============================================================================
class ConfigFile {
@@ -125,7 +123,7 @@ class ConfigFile {
//=============================================================================
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)) {
@@ -165,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);
@@ -188,10 +174,6 @@ class Inspector {
void SetCrashTimeParameters();
// The bootstrap port in which the inspector is registered and into which it
// must check in.
mach_port_t bootstrap_subset_port_;
mach_port_t service_rcv_port_;
int exception_type_;

View File

@@ -44,8 +44,6 @@
#import "common/mac/SimpleStringDictionary.h"
#import "common/mac/MachIPC.h"
#import "GTMDefines.h"
#import <Foundation/Foundation.h>
#if VERBOSE
@@ -57,7 +55,7 @@
namespace google_breakpad {
//=============================================================================
BOOL EnsureDirectoryPathExists(NSString *dirPath) {
static BOOL EnsureDirectoryPathExists(NSString *dirPath) {
NSFileManager *mgr = [NSFileManager defaultManager];
// If we got a relative path, prepend the current directory
@@ -93,14 +91,14 @@ BOOL EnsureDirectoryPathExists(NSString *dirPath) {
// Break up the difference into components
NSString *diff = [dirPath substringFromIndex:[common length] + 1];
NSArray *components = [diff pathComponents];
NSUInteger count = [components count];
unsigned count = [components count];
// Rebuild the path one component at a time
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
path = common;
for (NSUInteger i = 0; i < count; ++i) {
for (unsigned i = 0; i < count; ++i) {
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]];
if (![mgr createDirectoryAtPath:path attributes:attrs])
@@ -203,12 +201,7 @@ void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters
//=============================================================================
void Inspector::Inspect(const char *receive_port_name) {
kern_return_t result = ResetBootstrapPort();
if (result != KERN_SUCCESS) {
return;
}
result = ServiceCheckIn(receive_port_name);
kern_return_t result = ServiceCheckIn(receive_port_name);
if (result == KERN_SUCCESS) {
result = ReadMessages();
@@ -245,50 +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)",
bootstrap_strerror(kr), 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_);
@@ -319,7 +273,7 @@ kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
}
// Unregister the service associated with the receive port.
kr = bootstrap_register(bootstrap_subset_port_,
kr = bootstrap_register(bootstrap_port,
(char*)receive_port_name,
MACH_PORT_NULL);
@@ -375,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
@@ -422,11 +376,11 @@ void Inspector::SetCrashTimeParameters() {
if (processStartTimeString) {
time_t processStartTime = strtol(processStartTimeString, NULL, 10);
time_t processUptime = tv.tv_sec - processStartTime;
sprintf(processUptimeString, "%zd", processUptime);
sprintf(processUptimeString, "%d", processUptime);
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString);
}
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
sprintf(processCrashtimeString, "%d", tv.tv_sec);
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME,
processCrashtimeString);
}

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,160 +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,
OnClientDumpRequestCallback dump_callback,
void *dump_context,
OnClientExitingCallback exit_callback,
void *exit_context,
bool generate_dumps,
const std::string &dump_path)
: dump_callback_(dump_callback),
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_) {
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,141 +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);
// Create an instance with the given parameters.
//
// mach_port_name: Named server port to listen on.
// 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,
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();
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_

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,22 +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 <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 */
/*
@@ -106,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++) {
@@ -187,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 &&
(N_BADMAG(buf) && *((long *)&buf) != MH_MAGIC &&
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) &&
/* The following is the big-endian ppc64 check */
/* nealsid: The following is the big-endian ppc64 check */
(*((uint32_t*)&buf)) != FAT_MAGIC) {
return -1;
return (-1);
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (NXSwapBigLongToHost(*((long *)&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;
/* 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 = NXSwapBigIntToHost(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 =
NXSwapBigIntToHost(fat_archs[i].cputype);
NXSwapBigLongToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
NXSwapBigIntToHost(fat_archs[i].cpusubtype);
NXSwapBigLongToHost(fat_archs[i].cpusubtype);
fat_archs[i].offset =
NXSwapBigIntToHost(fat_archs[i].offset);
NXSwapBigLongToHost(fat_archs[i].offset);
fat_archs[i].size =
NXSwapBigIntToHost(fat_archs[i].size);
NXSwapBigLongToHost(fat_archs[i].size);
fat_archs[i].align =
NXSwapBigIntToHost(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,8 +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.
#include "client/mac/handler/dynamic_images.h"
extern "C" { // needed to compile on Leopard
#include <mach-o/nlist.h>
#include <stdlib.h>
@@ -36,42 +34,13 @@ extern "C" { // needed to compile on Leopard
}
#include "breakpad_nlist_64.h"
#include <AvailabilityMacros.h>
#include <assert.h>
#include <CoreServices/CoreServices.h>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <mach/task_info.h>
#include <sys/sysctl.h>
#include <algorithm>
#include <string>
#include <vector>
#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
#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.
@@ -81,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;
@@ -146,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
@@ -159,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;
@@ -300,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 -
@@ -330,188 +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;
}
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 IsSnowLeopardOrLater() {
#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
}
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
if (IsSnowLeopardOrLater()) {
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 NULL;
}
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());
}
}
}
@@ -533,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;
}
}
@@ -541,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,49 +41,32 @@
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <string>
#include <vector>
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__
@@ -94,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_;}
@@ -150,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_
};
//==============================================================================
@@ -225,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
@@ -239,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) {
@@ -262,51 +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;
#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

@@ -33,7 +33,6 @@
#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"
#ifndef USE_PROTECTED_ALLOCATIONS
#define USE_PROTECTED_ALLOCATIONS 0
@@ -93,15 +92,12 @@ extern "C"
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")));
mach_msg_type_number_t code_count);
kern_return_t ForwardException(mach_port_t task,
mach_port_t failed_thread,
@@ -124,10 +120,10 @@ extern "C"
exception_data_t exception_code,
mach_msg_type_number_t code_count,
thread_state_flavor_t *target_flavor,
thread_state_t in_thread_state,
mach_msg_type_number_t in_thread_state_count,
thread_state_t out_thread_state,
mach_msg_type_number_t *out_thread_state_count);
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);
kern_return_t
exception_raise_state_identity(mach_port_t target_port,
@@ -137,10 +133,10 @@ extern "C"
exception_data_t exception_code,
mach_msg_type_number_t exception_code_count,
thread_state_flavor_t *target_flavor,
thread_state_t in_thread_state,
mach_msg_type_number_t in_thread_state_count,
thread_state_t out_thread_state,
mach_msg_type_number_t *out_thread_state_count);
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);
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port,
exception_type_t exception,
@@ -222,8 +218,7 @@ 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),
@@ -239,8 +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 (port_name)
crash_generation_client_.reset(new CrashGenerationClient(port_name));
Setup(install_handler);
}
@@ -269,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;
@@ -281,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
@@ -297,50 +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,
#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,
mach_port_t thread_name,
bool exit_after_write) {
mach_port_t thread_name) {
bool result = false;
if (directCallback_) {
@@ -349,21 +306,9 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exit_after_write)
if (exception_type && exception_code)
_exit(exception_type);
}
} 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);
}
} else {
string minidump_id;
@@ -373,7 +318,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
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;
@@ -391,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);
}
}
@@ -490,9 +435,6 @@ kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
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);
}
@@ -505,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);
@@ -526,9 +467,7 @@ 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();
@@ -538,26 +477,11 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
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;
#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, thread,
false);
self->WriteMinidumpWithException(0, 0, 0, 0);
self->UninstallHandler(false);
#if USE_PROTECTED_ALLOCATIONS
if(gBreakpadAllocator)
@@ -591,15 +515,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
// Generate the minidump with the exception data.
self->WriteMinidumpWithException(receive.exception, receive.code[0],
subcode, receive.thread.name, true);
#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);
@@ -734,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)
@@ -750,25 +666,16 @@ bool ExceptionHandler::Teardown() {
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

@@ -40,25 +40,12 @@
#include <string>
#include "client/mac/crash_generation/crash_generation_client.h"
#include "processor/scoped_ptr.h"
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
@@ -99,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.
@@ -124,37 +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 {
return crash_generation_client_.get() != NULL;
}
private:
// Install the mach exception handler
bool InstallHandler();
@@ -170,16 +130,13 @@ 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
bool WriteMinidumpWithException(int exception_type,
int exception_code,
int exception_subcode,
mach_port_t thread_name,
bool exit_after_write);
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
@@ -249,9 +206,6 @@ class ExceptionHandler {
// True, if we're using the mutext to indicate when mindump writing occurs
bool use_minidump_write_mutex_;
// Client for out-of-process dump generation.
scoped_ptr<CrashGenerationClient> crash_generation_client_;
};
} // 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

@@ -27,26 +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 <algorithm>
#include <cstdio>
#include <mach/host_info.h>
#include <mach/i386/thread_status.h>
#include <mach/mach_vm.h>
#include <mach/vm_statistics.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <mach/mach_vm.h>
#include <CoreFoundation/CoreFoundation.h>
#include "client/mac/handler/minidump_generator.h"
#ifdef HAS_PPC_SUPPORT
#include <mach/ppc/thread_status.h>
#endif
#include "client/minidump_file_writer-inl.h"
#include "common/mac/file_id.h"
#include "common/mac/string_utilities.h"
@@ -56,24 +50,15 @@ using MacStringUtils::IntegerValueAtIndex;
namespace google_breakpad {
#if __LP64__
#define LC_SEGMENT_ARCH LC_SEGMENT_64
#else
#define LC_SEGMENT_ARCH LC_SEGMENT
#endif
// constructor when generating from within the crashed process
MinidumpGenerator::MinidumpGenerator()
: writer_(),
exception_type_(0),
: exception_type_(0),
exception_code_(0),
exception_subcode_(0),
exception_thread_(0),
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()),
cpu_type_(DynamicImages::GetNativeCPUType()),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
dynamic_images_(NULL) {
GatherSystemInformation();
}
@@ -81,22 +66,16 @@ MinidumpGenerator::MinidumpGenerator()
// crashed process
MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
mach_port_t handler_thread)
: writer_(),
exception_type_(0),
: exception_type_(0),
exception_code_(0),
exception_subcode_(0),
exception_thread_(0),
crashing_task_(crashing_task),
handler_thread_(handler_thread),
cpu_type_(DynamicImages::GetNativeCPUType()),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
handler_thread_(handler_thread) {
if (crashing_task != mach_task_self()) {
dynamic_images_ = new DynamicImages(crashing_task_);
cpu_type_ = dynamic_images_->GetCPUType();
} else {
dynamic_images_ = NULL;
cpu_type_ = DynamicImages::GetNativeCPUType();
}
GatherSystemInformation();
@@ -186,7 +165,6 @@ string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
bool MinidumpGenerator::Write(const char *path) {
WriteStreamFN writers[] = {
&MinidumpGenerator::WriteThreadListStream,
&MinidumpGenerator::WriteMemoryListStream,
&MinidumpGenerator::WriteSystemInfoStream,
&MinidumpGenerator::WriteModuleListStream,
&MinidumpGenerator::WriteMiscInfoStream,
@@ -208,7 +186,7 @@ bool MinidumpGenerator::Write(const char *path) {
if (!header.Allocate())
return false;
int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
int writer_count = sizeof(writers) / sizeof(writers[0]);
// If we don't have exception information, don't write out the
// exception stream
@@ -255,45 +233,35 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
kern_return_t result =
mach_vm_region_recurse(crashing_task_, &stack_region_base,
&stack_region_size, &nesting_level,
region_info, &info_count);
region_info,
&info_count);
if (result != KERN_SUCCESS || start_addr < stack_region_base) {
// Failure or stack corruption, since mach_vm_region had to go
if (start_addr < stack_region_base) {
// probably stack corruption, since mach_vm_region had to go
// higher in the process address space to find a valid region.
return 0;
}
unsigned int tag = submap_info.user_tag;
// If the user tag is VM_MEMORY_STACK, look for more readable regions with
// the same tag placed immediately above the computed stack region. Under
// some circumstances, the stack for thread 0 winds up broken up into
// multiple distinct abutting regions. This can happen for several reasons,
// including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
// the access on stack pages by calling mprotect.
if (tag == VM_MEMORY_STACK) {
while (true) {
mach_vm_address_t next_region_base = stack_region_base +
stack_region_size;
mach_vm_address_t proposed_next_region_base = next_region_base;
mach_vm_size_t next_region_size;
nesting_level = 0;
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
result = mach_vm_region_recurse(crashing_task_, &next_region_base,
&next_region_size, &nesting_level,
region_info, &info_count);
if (result != KERN_SUCCESS ||
next_region_base != proposed_next_region_base ||
submap_info.user_tag != tag ||
(submap_info.protection & VM_PROT_READ) == 0) {
break;
}
if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
// The stack for thread 0 needs to extend all the way to
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
// for many processes, the stack is first created in one page
// below this, and is then later extended to a much larger size by
// creating a new VM region immediately below the initial page.
stack_region_size += next_region_size;
}
// You can see this for yourself by running vmmap on a "hello,
// world" program
// Because of the above, we'll add 4k to include the original
// stack frame page.
// This method of finding the stack region needs to be done in
// a better way; the breakpad issue 247 is tracking this.
stack_region_size += 0x1000;
}
return stack_region_base + stack_region_size - start_addr;
return result == KERN_SUCCESS ?
stack_region_base + stack_region_size - start_addr : 0;
}
bool MinidumpGenerator::WriteStackFromStartAddress(
@@ -325,15 +293,20 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return false;
if (dynamic_images_) {
vector<uint8_t> stack_memory;
if (ReadTaskMemory(crashing_task_,
start_addr,
size,
stack_memory) != KERN_SUCCESS) {
kern_return_t kr;
void *stack_memory = ReadTaskMemory(crashing_task_,
(void*)start_addr,
size,
&kr);
if (stack_memory == NULL) {
return false;
}
result = memory.Copy(&stack_memory[0], size);
result = memory.Copy(stack_memory, size);
free(stack_memory);
} else {
result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
}
@@ -345,111 +318,52 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return result;
}
#if TARGET_CPU_PPC || TARGET_CPU_PPC64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
switch (cpu_type_) {
#ifdef HAS_PPC_SUPPORT
case CPU_TYPE_POWERPC:
return WriteStackPPC(state, stack_location);
case CPU_TYPE_POWERPC64:
return WriteStackPPC64(state, stack_location);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_PPC
mach_vm_address_t start_addr = machine_state->r1;
#else
mach_vm_address_t start_addr = machine_state->__r1;
#endif
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_PPC
return machine_state->srr0;
#else
return machine_state->__srr0;
#endif
case CPU_TYPE_I386:
return WriteStackX86(state, stack_location);
case CPU_TYPE_X86_64:
return WriteStackX86_64(state, stack_location);
default:
return false;
}
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
switch (cpu_type_) {
#ifdef HAS_PPC_SUPPORT
case CPU_TYPE_POWERPC:
return WriteContextPPC(state, register_location);
case CPU_TYPE_POWERPC64:
return WriteContextPPC64(state, register_location);
#endif
case CPU_TYPE_I386:
return WriteContextX86(state, register_location);
case CPU_TYPE_X86_64:
return WriteContextX86_64(state, register_location);
default:
return false;
}
}
u_int64_t MinidumpGenerator::CurrentPCForStack(
breakpad_thread_state_data_t state) {
switch (cpu_type_) {
#ifdef HAS_PPC_SUPPORT
case CPU_TYPE_POWERPC:
return CurrentPCForStackPPC(state);
case CPU_TYPE_POWERPC64:
return CurrentPCForStackPPC64(state);
#endif
case CPU_TYPE_I386:
return CurrentPCForStackX86(state);
case CPU_TYPE_X86_64:
return CurrentPCForStackX86_64(state);
default:
assert("Unknown CPU type!");
return 0;
}
}
#ifdef HAS_PCC_SUPPORT
bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
return WriteStackFromStartAddress(start_addr, stack_location);
}
bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}
bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location)
{
TypedMDRVA<MDRawContextPPC> context(&writer_);
ppc_thread_state_t *machine_state =
reinterpret_cast<ppc_thread_state_t *>(state);
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextPPC *context_ptr = context.get();
MinidumpContext *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
#if TARGET_CPU_PPC64
#define AddReg(a) context_ptr->a = machine_state->__ ## a
#define AddGPR(a) context_ptr->gpr[a] = machine_state->__r ## a
#else
#define AddReg(a) context_ptr->a = machine_state->a
#define AddGPR(a) context_ptr->gpr[a] = machine_state->r ## a
#endif
AddReg(srr0);
AddReg(cr);
AddReg(xer);
@@ -489,126 +403,57 @@ bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
AddGPR(29);
AddGPR(30);
AddGPR(31);
#if TARGET_CPU_PPC
/* The mq register is only for PPC */
AddReg(mq);
#undef AddReg
#undef AddGPR
return true;
}
bool MinidumpGenerator::WriteContextPPC64(
breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextPPC64> context(&writer_);
ppc_thread_state64_t *machine_state =
reinterpret_cast<ppc_thread_state64_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextPPC64 *context_ptr = context.get();
context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
AddReg(srr0);
AddReg(cr);
AddReg(xer);
AddReg(ctr);
AddReg(lr);
AddReg(vrsave);
AddGPR(0);
AddGPR(1);
AddGPR(2);
AddGPR(3);
AddGPR(4);
AddGPR(5);
AddGPR(6);
AddGPR(7);
AddGPR(8);
AddGPR(9);
AddGPR(10);
AddGPR(11);
AddGPR(12);
AddGPR(13);
AddGPR(14);
AddGPR(15);
AddGPR(16);
AddGPR(17);
AddGPR(18);
AddGPR(19);
AddGPR(20);
AddGPR(21);
AddGPR(22);
AddGPR(23);
AddGPR(24);
AddGPR(25);
AddGPR(26);
AddGPR(27);
AddGPR(28);
AddGPR(29);
AddGPR(30);
AddGPR(31);
#undef AddReg
#undef AddGPR
return true;
}
#endif
bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
return true;
}
#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
return WriteStackFromStartAddress(start_addr, stack_location);
}
bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
#else
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
#endif
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
return REGISTER_FROM_THREADSTATE(machine_state, eip);
}
u_int64_t
MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
return REGISTER_FROM_THREADSTATE(machine_state, rip);
#else
return REGISTER_FROM_THREADSTATE(machine_state, eip);
#endif
}
bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location)
{
TypedMDRVA<MDRawContextX86> context(&writer_);
i386_thread_state_t *machine_state =
reinterpret_cast<i386_thread_state_t *>(state);
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextX86 *context_ptr = context.get();
MinidumpContext *context_ptr = context.get();
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
#if TARGET_CPU_X86
context_ptr->context_flags = MD_CONTEXT_X86;
AddReg(eax);
AddReg(ebx);
@@ -628,26 +473,7 @@ bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
AddReg(eflags);
AddReg(eip);
#undef AddReg
return true;
}
bool MinidumpGenerator::WriteContextX86_64(
breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MDRawContextAMD64> context(&writer_);
x86_thread_state64_t *machine_state =
reinterpret_cast<x86_thread_state64_t *>(state);
if (!context.Allocate())
return false;
*register_location = context.location();
MDRawContextAMD64 *context_ptr = context.get();
#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
#else
context_ptr->context_flags = MD_CONTEXT_AMD64;
AddReg(rax);
AddReg(rbx);
@@ -670,53 +496,28 @@ bool MinidumpGenerator::WriteContextX86_64(
// not used in the flags register. Since the minidump format
// specifies 32 bits for the flags register, we can truncate safely
// with no loss.
context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
context_ptr->eflags = machine_state->__rflags;
AddReg(cs);
AddReg(fs);
AddReg(gs);
#undef AddReg
#endif
#undef AddReg(a)
return true;
}
bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
thread_state_t state,
mach_msg_type_number_t *count) {
thread_state_flavor_t flavor;
switch (cpu_type_) {
#ifdef HAS_PPC_SUPPORT
case CPU_TYPE_POWERPC:
flavor = PPC_THREAD_STATE;
break;
case CPU_TYPE_POWERPC64:
flavor = PPC_THREAD_STATE64;
break;
#endif
case CPU_TYPE_I386:
flavor = i386_THREAD_STATE;
break;
case CPU_TYPE_X86_64:
flavor = x86_THREAD_STATE64;
break;
default:
return false;
}
return thread_get_state(target_thread, flavor,
state, count) == KERN_SUCCESS;
}
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
MDRawThread *thread) {
breakpad_thread_state_data_t state;
mach_msg_type_number_t state_count
= static_cast<mach_msg_type_number_t>(sizeof(state));
mach_msg_type_number_t state_count = sizeof(state);
if (GetThreadState(thread_id, state, &state_count)) {
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
state, &state_count) ==
KERN_SUCCESS) {
if (!WriteStack(state, &thread->stack))
return false;
memory_blocks_.push_back(thread->stack);
if (!WriteContext(state, &thread->thread_context))
return false;
@@ -739,10 +540,7 @@ bool MinidumpGenerator::WriteThreadListStream(
return false;
// Don't include the generator thread
if (handler_thread_ != MACH_PORT_NULL)
non_generator_thread_count = thread_count - 1;
else
non_generator_thread_count = thread_count;
non_generator_thread_count = thread_count - 1;
if (!list.AllocateObjectAndArray(non_generator_thread_count,
sizeof(MDRawThread)))
return false;
@@ -769,112 +567,6 @@ bool MinidumpGenerator::WriteThreadListStream(
return true;
}
bool MinidumpGenerator::WriteMemoryListStream(
MDRawDirectory *memory_list_stream) {
TypedMDRVA<MDRawMemoryList> list(&writer_);
// If the dump has an exception, include some memory around the
// instruction pointer.
const size_t kIPMemorySize = 256; // bytes
bool have_ip_memory = false;
MDMemoryDescriptor ip_memory_d;
if (exception_thread_ && exception_type_) {
breakpad_thread_state_data_t state;
mach_msg_type_number_t stateCount
= static_cast<mach_msg_type_number_t>(sizeof(state));
if (thread_get_state(exception_thread_,
BREAKPAD_MACHINE_THREAD_STATE,
state,
&stateCount) == KERN_SUCCESS) {
u_int64_t ip = CurrentPCForStack(state);
// Bound it to the upper and lower bounds of the region
// it's contained within. If it's not in a known memory region,
// don't bother trying to write it.
mach_vm_address_t addr = ip;
mach_vm_size_t size;
natural_t nesting_level = 0;
vm_region_submap_info_64 info;
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
kern_return_t ret =
mach_vm_region_recurse(crashing_task_,
&addr,
&size,
&nesting_level,
(vm_region_recurse_info_t)&info,
&info_count);
if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
// Try to get 128 bytes before and after the IP, but
// settle for whatever's available.
ip_memory_d.start_of_memory_range =
std::max(uintptr_t(addr),
uintptr_t(ip - (kIPMemorySize / 2)));
uintptr_t end_of_range =
std::min(uintptr_t(ip + (kIPMemorySize / 2)),
uintptr_t(addr + size));
ip_memory_d.memory.data_size =
end_of_range - ip_memory_d.start_of_memory_range;
have_ip_memory = true;
// This needs to get appended to the list even though
// the memory bytes aren't filled in yet so the entire
// list can be written first. The memory bytes will get filled
// in after the memory list is written.
memory_blocks_.push_back(ip_memory_d);
}
}
}
// Now fill in the memory list and write it.
unsigned memory_count = memory_blocks_.size();
if (!list.AllocateObjectAndArray(memory_count,
sizeof(MDMemoryDescriptor)))
return false;
memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
memory_list_stream->location = list.location();
list.get()->number_of_memory_ranges = memory_count;
unsigned int i;
for (i = 0; i < memory_count; ++i) {
list.CopyIndexAfterObject(i, &memory_blocks_[i],
sizeof(MDMemoryDescriptor));
}
if (have_ip_memory) {
// Now read the memory around the instruction pointer.
UntypedMDRVA ip_memory(&writer_);
if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
return false;
if (dynamic_images_) {
// Out-of-process.
vector<uint8_t> memory;
if (ReadTaskMemory(crashing_task_,
ip_memory_d.start_of_memory_range,
ip_memory_d.memory.data_size,
memory) != KERN_SUCCESS) {
return false;
}
ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
} else {
// In-process, just copy from local memory.
ip_memory.Copy(
reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
ip_memory_d.memory.data_size);
}
ip_memory_d.memory = ip_memory.location();
// Write this again now that the data location is filled in.
list.CopyIndexAfterObject(i - 1, &ip_memory_d,
sizeof(MDMemoryDescriptor));
}
return true;
}
bool
MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
TypedMDRVA<MDRawExceptionStream> exception(&writer_);
@@ -893,10 +585,12 @@ MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
exception_ptr->exception_record.exception_flags = exception_code_;
breakpad_thread_state_data_t state;
mach_msg_type_number_t state_count
= static_cast<mach_msg_type_number_t>(sizeof(state));
mach_msg_type_number_t stateCount = sizeof(state);
if (!GetThreadState(exception_thread_, state, &state_count))
if (thread_get_state(exception_thread_,
BREAKPAD_MACHINE_THREAD_STATE,
state,
&stateCount) != KERN_SUCCESS)
return false;
if (!WriteContext(state, &exception_ptr->thread_context))
@@ -921,22 +615,20 @@ bool MinidumpGenerator::WriteSystemInfoStream(
system_info_stream->location = info.location();
// CPU Information
uint32_t cpu_type;
size_t len = sizeof(cpu_type);
sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
uint32_t number_of_processors;
size_t len = sizeof(number_of_processors);
len = sizeof(number_of_processors);
sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
MDRawSystemInfo *info_ptr = info.get();
switch (cpu_type_) {
switch (cpu_type) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
break;
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
if (cpu_type_ == CPU_TYPE_I386)
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
else
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
#ifdef __i386__
// ebx is used for PIC code, so we need
// to preserve it.
@@ -950,18 +642,6 @@ bool MinidumpGenerator::WriteSystemInfoStream(
"=c" (ecx), \
"=d" (edx) \
: "0" (op))
#elif defined(__x86_64__)
#define cpuid(op,eax,ebx,ecx,edx) \
asm ("cpuid \n\t" \
: "=a" (eax), \
"=b" (ebx), \
"=c" (ecx), \
"=d" (edx) \
: "0" (op))
#endif
#if defined(__i386__) || defined(__x86_64__)
int unused, unused2;
// get vendor id
cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
@@ -970,7 +650,6 @@ bool MinidumpGenerator::WriteSystemInfoStream(
// get version and feature info
cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
info_ptr->cpu.x86_cpu_info.feature_information);
// family
info_ptr->processor_level =
(info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
@@ -978,21 +657,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
info_ptr->processor_revision =
(info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
// decode extended model info
if (info_ptr->processor_level == 0xF ||
info_ptr->processor_level == 0x6) {
info_ptr->processor_revision |=
((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
}
// decode extended family info
if (info_ptr->processor_level == 0xF) {
info_ptr->processor_level +=
((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
}
#endif // __i386__ || __x86_64_
#endif // __i386__
break;
default:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
@@ -1025,16 +690,23 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
if (!image)
return false;
const breakpad_mach_header *header = image->GetMachHeader();
if (!header)
return false;
int cpu_type = header->cputype;
memset(module, 0, sizeof(MDRawModule));
MDLocationDescriptor string_location;
string name = image->GetFilePath();
if (!writer_.WriteString(name.c_str(), 0, &string_location))
const char* name = image->GetFilePath();
if (!writer_.WriteString(name, 0, &string_location))
return false;
module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
module->size_of_image = static_cast<u_int32_t>(image->GetVMSize());
module->size_of_image = image->GetVMSize();
module->module_name_rva = string_location.rva;
// We'll skip the executable module, because they don't have
@@ -1057,11 +729,12 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
module->version_info.file_version_lo |= (modVersion & 0xff);
}
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
if (!WriteCVRecord(module, cpu_type, name)) {
return false;
}
} else {
// Getting module info in the crashed process
// we're getting module info in the crashed process
const breakpad_mach_header *header;
header = (breakpad_mach_header*)_dyld_get_image_header(index);
if (!header)
@@ -1083,15 +756,15 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
unsigned long slide = _dyld_get_image_vmaddr_slide(index);
const char* name = _dyld_get_image_name(index);
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
reinterpret_cast<const struct load_command *>(header + 1);
memset(module, 0, sizeof(MDRawModule));
for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
if (cmd->cmd == LC_SEGMENT_ARCH) {
if (cmd->cmd == LC_SEGMENT) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
MDLocationDescriptor string_location;
@@ -1100,7 +773,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
return false;
module->base_of_image = seg->vmaddr + slide;
module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
module->size_of_image = seg->vmsize;
module->module_name_rva = string_location.rva;
if (!WriteCVRecord(module, cpu_type, name))
@@ -1237,7 +910,7 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
misc_info_stream->location = info.location();
MDRawMiscInfo *info_ptr = info.get();
info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo));
info_ptr->size_of_info = sizeof(MDRawMiscInfo);
info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
MD_MISCINFO_FLAGS1_PROCESS_TIMES |
MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
@@ -1249,39 +922,33 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
struct rusage usage;
if (getrusage(RUSAGE_SELF, &usage) != -1) {
// Omit the fractional time since the MDRawMiscInfo only wants seconds
info_ptr->process_user_time =
static_cast<u_int32_t>(usage.ru_utime.tv_sec);
info_ptr->process_kernel_time =
static_cast<u_int32_t>(usage.ru_stime.tv_sec);
info_ptr->process_user_time = usage.ru_utime.tv_sec;
info_ptr->process_kernel_time = usage.ru_stime.tv_sec;
}
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
static_cast<int>(info_ptr->process_id) };
u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id };
size_t size;
if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) {
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) {
mach_vm_address_t addr;
if (mach_vm_allocate(mach_task_self(),
&addr,
size,
true) == KERN_SUCCESS) {
struct kinfo_proc *proc = (struct kinfo_proc *)addr;
if (!sysctl(mib, mibsize, proc, &size, NULL, 0))
info_ptr->process_create_time =
static_cast<u_int32_t>(proc->kp_proc.p_starttime.tv_sec);
if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0))
info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec;
mach_vm_deallocate(mach_task_self(), addr, size);
}
}
// Speed
uint64_t speed;
const uint64_t kOneMillion = 1000 * 1000;
size = sizeof(speed);
sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion);
info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion);
info_ptr->processor_max_mhz = speed / (1000 * 1000);
info_ptr->processor_mhz_limit = speed / (1000 * 1000);
size = sizeof(speed);
sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
info_ptr->processor_current_mhz = static_cast<u_int32_t>(speed / kOneMillion);
info_ptr->processor_current_mhz = speed / (1000 * 1000);
return true;
}

View File

@@ -37,23 +37,38 @@
#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"
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
#define HAS_PPC_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
#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
@@ -104,7 +119,6 @@ class MinidumpGenerator {
// 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);
@@ -113,8 +127,6 @@ 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,
@@ -125,32 +137,10 @@ class MinidumpGenerator {
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_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
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);
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
@@ -166,9 +156,6 @@ 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];
@@ -178,15 +165,6 @@ class MinidumpGenerator {
// 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_;
// 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) 2010 Google Inc.
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -27,30 +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.
// Inline implementation of ScopedTaskSuspend, which suspends a Mach
// task for the duration of its scope.
#include <unistd.h>
#ifndef GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
#define GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
#include <pthread.h>
#include <pwd.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>
namespace google_breakpad {
#include "minidump_generator.h"
#include "minidump_file_writer.h"
class ScopedTaskSuspend {
public:
explicit ScopedTaskSuspend(mach_port_t target) : target_(target) {
task_suspend(target_);
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;
}
static void SleepyFunction() {
while (!doneWritingReport) {
usleep(100);
}
}
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");
}
~ScopedTaskSuspend() {
task_resume(target_);
}
SleepyFunction();
private:
mach_port_t target_;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_
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 */; };
@@ -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,11 +132,11 @@
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; };
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/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; };
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>"; };
@@ -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,9 +203,6 @@
08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
isa = PBXGroup;
children = (
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
F9721FA80E8B0E4800D7E813 /* md5.c */,
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
F9721F770E8B0DC700D7E813 /* dwarf2reader.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,7 +414,7 @@
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
compatibilityVersion = "Xcode 3.2";
compatibilityVersion = "Xcode 2.4";
hasScannedForEncodings = 1;
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
projectDirPath = "";
@@ -549,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;
};
@@ -561,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;
};
@@ -581,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;
};
@@ -619,7 +580,6 @@
files = (
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -629,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;
@@ -710,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;
};
@@ -732,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;
@@ -754,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.

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,6 @@
#include <Foundation/Foundation.h>
#include "client/mac/Framework/Breakpad.h"
#import "GTMDefines.h"
#define kClientIdPreferenceKey @"clientid"
@@ -54,10 +53,10 @@ extern NSString *const kDefaultServerType;
// work in the middle of a validation.
@interface LengthLimitingTextField : NSTextField {
@private
NSUInteger maximumLength_;
unsigned int maximumLength_;
}
- (void)setMaximumLength:(NSUInteger)maxLength;
- (void) setMaximumLength:(unsigned int)maxLength;
@end
@interface Reporter : NSObject {

View File

@@ -56,20 +56,20 @@ NSString *const kDefaultServerType = @"google";
@interface NSView (ResizabilityExtentions)
// Shifts the view vertically by the given amount.
- (void)breakpad_shiftVertically:(CGFloat)offset;
- (void)breakpad_shiftVertically:(float)offset;
// Shifts the view horizontally by the given amount.
- (void)breakpad_shiftHorizontally:(CGFloat)offset;
- (void)breakpad_shiftHorizontally:(float)offset;
@end
@implementation NSView (ResizabilityExtentions)
- (void)breakpad_shiftVertically:(CGFloat)offset {
- (void)breakpad_shiftVertically:(float)offset {
NSPoint origin = [self frame].origin;
origin.y += offset;
[self setFrameOrigin:origin];
}
- (void)breakpad_shiftHorizontally:(CGFloat)offset {
- (void)breakpad_shiftHorizontally:(float)offset {
NSPoint origin = [self frame].origin;
origin.x += offset;
[self setFrameOrigin:origin];
@@ -79,11 +79,11 @@ NSString *const kDefaultServerType = @"google";
@interface NSWindow (ResizabilityExtentions)
// Adjusts the window height by heightDelta relative to its current height,
// keeping all the content at the same size.
- (void)breakpad_adjustHeight:(CGFloat)heightDelta;
- (void)breakpad_adjustHeight:(float)heightDelta;
@end
@implementation NSWindow (ResizabilityExtentions)
- (void)breakpad_adjustHeight:(CGFloat)heightDelta {
- (void)breakpad_adjustHeight:(float)heightDelta {
[[self contentView] setAutoresizesSubviews:NO];
NSRect windowFrame = [self frame];
@@ -101,16 +101,16 @@ NSString *const kDefaultServerType = @"google";
// Grows or shrinks the height of the field to the minimum required to show the
// current text, preserving the existing width and origin.
// Returns the change in height.
- (CGFloat)breakpad_adjustHeightToFit;
- (float)breakpad_adjustHeightToFit;
// Grows or shrinks the width of the field to the minimum required to show the
// current text, preserving the existing height and origin.
// Returns the change in width.
- (CGFloat)breakpad_adjustWidthToFit;
- (float)breakpad_adjustWidthToFit;
@end
@implementation NSTextField (ResizabilityExtentions)
- (CGFloat)breakpad_adjustHeightToFit {
- (float)breakpad_adjustHeightToFit {
NSRect oldFrame = [self frame];
// Starting with the 10.5 SDK, height won't grow, so make it huge to start.
NSRect presizeFrame = oldFrame;
@@ -125,7 +125,7 @@ NSString *const kDefaultServerType = @"google";
return newSize.height - NSHeight(oldFrame);
}
- (CGFloat)breakpad_adjustWidthToFit {
- (float)breakpad_adjustWidthToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
return NSWidth([self frame]) - NSWidth(oldFrame);
@@ -136,11 +136,11 @@ NSString *const kDefaultServerType = @"google";
// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
// minimum width of 70, while preserving the right edge location.
// Returns the change in width.
- (CGFloat)breakpad_smartSizeToFit;
- (float)breakpad_smartSizeToFit;
@end
@implementation NSButton (ResizabilityExtentions)
- (CGFloat)breakpad_smartSizeToFit {
- (float)breakpad_smartSizeToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
NSRect newFrame = [self frame];
@@ -218,8 +218,7 @@ NSString *const kDefaultServerType = @"google";
// Run an alert window with the given timeout. Returns
// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0
// queues the message immediately in the modal run loop.
- (NSInteger)runModalWindow:(NSWindow*)window
withTimeout:(NSTimeInterval)timeout;
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout;
// Returns a unique client id (user-specific), creating a persistent
// one in the user defaults, if necessary.
@@ -255,9 +254,6 @@ NSString *const kDefaultServerType = @"google";
// in their comments/email.
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
// Records the uploaded crash ID to the log file.
- (void)logUploadWithID:(const char *)uploadID;
@end
@implementation Reporter
@@ -390,7 +386,7 @@ NSString *const kDefaultServerType = @"google";
}
// Otherwise, if we have no client id, generate one!
srandom((int)[[NSDate date] timeIntervalSince1970]);
srandom([[NSDate date] timeIntervalSince1970]);
long clientId1 = random();
long clientId2 = random();
long clientId3 = random();
@@ -407,8 +403,8 @@ NSString *const kDefaultServerType = @"google";
unsigned int logFileCounter = 0;
NSString *logPath;
size_t logFileTailSize =
[[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue];
int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]
intValue];
NSMutableArray *logFilenames; // An array of NSString, one per log file
logFilenames = [[NSMutableArray alloc] init];
@@ -548,7 +544,7 @@ NSString *const kDefaultServerType = @"google";
// Get the timeout value for the notification.
NSTimeInterval timeout = [self messageTimeout];
NSInteger buttonPressed = NSAlertAlternateReturn;
int buttonPressed = NSAlertAlternateReturn;
// Determine whether we should create a text box for user feedback.
if ([self shouldRequestComments]) {
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
@@ -596,7 +592,7 @@ NSString *const kDefaultServerType = @"google";
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
[self explanatoryDialogText],
NSLocalizedString(@"commentsMsg", @"")]];
CGFloat commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
[alertWindow_ breakpad_adjustHeight:commentHeightDelta];
@@ -604,7 +600,7 @@ NSString *const kDefaultServerType = @"google";
// section depending on whether or not we are asking for email.
if (includeEmail) {
[emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
CGFloat emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
float emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
[preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
[alertWindow_ breakpad_adjustHeight:emailHeightDelta];
} else {
@@ -613,36 +609,17 @@ NSString *const kDefaultServerType = @"google";
// Localize the email label, and shift the associated text field.
[emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
CGFloat emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
// Localize the privacy policy label, and keep it right-aligned to the arrow.
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
CGFloat privacyLabelWidthDelta =
[privacyLinkLabel_ breakpad_adjustWidthToFit];
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
[privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
// Ensure that the email field and the privacy policy link don't overlap.
CGFloat kMinControlPadding = 8;
CGFloat maxEmailFieldWidth = NSMinX([privacyLinkLabel_ frame]) -
NSMinX([emailEntryField_ frame]) -
kMinControlPadding;
if (NSWidth([emailEntryField_ bounds]) > maxEmailFieldWidth &&
maxEmailFieldWidth > 0) {
NSSize emailSize = [emailEntryField_ frame].size;
emailSize.width = maxEmailFieldWidth;
[emailEntryField_ setFrameSize:emailSize];
}
// Localize the placeholder text.
[[commentsEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")];
[[emailEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")];
// Localize the buttons, and keep the cancel button at the right distance.
[sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
CGFloat sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
float sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
[cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
[cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
[cancelButton_ breakpad_smartSizeToFit];
@@ -650,13 +627,12 @@ NSString *const kDefaultServerType = @"google";
- (void)removeEmailPrompt {
[emailSectionBox_ setHidden:YES];
CGFloat emailSectionHeight = NSHeight([emailSectionBox_ frame]);
float emailSectionHeight = NSHeight([emailSectionBox_ frame]);
[preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
[alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
}
- (NSInteger)runModalWindow:(NSWindow*)window
withTimeout:(NSTimeInterval)timeout {
- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
if (timeout > 0.001) {
remainingDialogTime_ = timeout;
@@ -671,7 +647,7 @@ NSString *const kDefaultServerType = @"google";
// Run the window modally and wait for either a |stopModal| message or a
// button click.
[NSApp activateIgnoringOtherApps:YES];
NSInteger returnMethod = [NSApp runModalForWindow:window];
int returnMethod = [NSApp runModalForWindow:window];
return returnMethod;
}
@@ -735,7 +711,7 @@ doCommandBySelector:(SEL)commandSelector {
if (remainingDialogTime_ > 59) {
// calculate minutes remaining for UI purposes
displayedTimeLeft = (int)(remainingDialogTime_ / 60);
displayedTimeLeft = (remainingDialogTime_ / 60);
if (displayedTimeLeft == 1) {
formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
@@ -743,8 +719,8 @@ doCommandBySelector:(SEL)commandSelector {
formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
}
} else {
displayedTimeLeft = (int)remainingDialogTime_;
if (displayedTimeLeft == 1) {
displayedTimeLeft = remainingDialogTime_;
if (remainingDialogTime_ == 1) {
formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
} else {
formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
@@ -815,8 +791,7 @@ doCommandBySelector:(SEL)commandSelector {
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
NSTimeInterval spanSeconds = (now - lastTime);
[programDict setObject:[NSNumber numberWithDouble:now]
forKey:kLastSubmission];
[programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission];
[ud setObject:programDict forKey:program];
[ud synchronize];
@@ -921,8 +896,8 @@ doCommandBySelector:(SEL)commandSelector {
forKey:@BREAKPAD_VERSION];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"Email"
forKey:@BREAKPAD_EMAIL];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
}
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {
@@ -1008,7 +983,6 @@ doCommandBySelector:(SEL)commandSelector {
} else {
NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String];
[self logUploadWithID:reportID];
}
// rename the minidump file according to the id returned from the server
@@ -1053,29 +1027,6 @@ doCommandBySelector:(SEL)commandSelector {
[upload release];
}
- (void)logUploadWithID:(const char *)uploadID {
NSString *minidumpDir =
[parameters_ objectForKey:@kReporterMinidumpDirectoryKey];
NSString *logFilePath = [NSString stringWithFormat:@"%@/%s",
minidumpDir, kReporterLogFilename];
NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n",
[[NSDate date] timeIntervalSince1970], uploadID];
NSData *logData = [logLine dataUsingEncoding:kCFStringEncodingUTF8];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:logFilePath]) {
NSFileHandle *logFileHandle =
[NSFileHandle fileHandleForWritingAtPath:logFilePath];
[logFileHandle seekToEndOfFile];
[logFileHandle writeData:logData];
[logFileHandle closeFile];
} else {
[fileManager createFileAtPath:logFilePath
contents:logData
attributes:nil];
}
}
//=============================================================================
- (void)dealloc {
[parameters_ release];
@@ -1098,7 +1049,7 @@ doCommandBySelector:(SEL)commandSelector {
//=============================================================================
@implementation LengthLimitingTextField
- (void)setMaximumLength:(NSUInteger)maxLength {
- (void) setMaximumLength:(unsigned int)maxLength {
maximumLength_ = maxLength;
}
@@ -1115,7 +1066,7 @@ shouldChangeTextInRange:(NSRange)affectedCharRange
}
// Figure out what the new string length would be, taking into
// account user selections.
NSUInteger newStringLength =
int newStringLength =
[[textView string] length] - affectedCharRange.length +
[replacementString length];
if (newStringLength > maximumLength_) {
@@ -1131,7 +1082,7 @@ shouldChangeTextInRange:(NSRange)affectedCharRange
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
// Check for a single "Command" modifier
NSUInteger modifiers = [event modifierFlags];
unsigned int modifiers = [event modifierFlags];
modifiers &= NSDeviceIndependentModifierFlagsMask;
if (modifiers == NSCommandKeyMask) {
// Now, check for Select All, Cut, Copy, or Paste key equivalents.

View File

@@ -31,7 +31,6 @@
#import "Controller.h"
#import "TestClass.h"
#import "GTMDefines.h"
#include <unistd.h>
#include <mach/mach.h>
@@ -52,7 +51,7 @@
}
- (IBAction)forkTestOptions:(id)sender {
NSInteger tag = [[sender selectedCell] tag];
int tag = [[sender selectedCell] tag];
NSLog(@"sender tag: %d", tag);
if (tag <= 2) {
bpForkOption = tag;
@@ -76,7 +75,7 @@
NSString *resourcePath = [[NSBundle bundleForClass:
[self class]] resourcePath];
NSString *execProgname = nil;
NSString *execProgname;
if (progCrashPoint == DURINGLAUNCH) {
execProgname = [resourcePath stringByAppendingString:@"/crashduringload"];
} else if (progCrashPoint == AFTERLAUNCH) {
@@ -130,11 +129,11 @@
}
- (IBAction)crash:(id)sender {
NSInteger tag = [sender tag];
int tag = [sender tag];
if (tag == 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10.0];
[self performSelector:@selector(causeCrash) withObject:nil afterDelay:10];
[sender setState:NSOnState];
return;
}

View File

@@ -0,0 +1,47 @@
<?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>ACTIONS</key>
<dict>
<key>crash</key>
<string>id</string>
<key>forkTestGo</key>
<string>id</string>
<key>forkTestOptions</key>
<string>id</string>
<key>generateReportWithoutCrash</key>
<string>id</string>
<key>showForkTestWindow</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Controller</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>forkTestOptions_</key>
<string>NSWindow</string>
<key>window_</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
<dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@@ -0,0 +1,22 @@
<?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>670</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../GoogleBreakpadTest.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>221</integer>
<integer>29</integer>
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,7 @@ void InternalTestClass::InternalFunction(AStruct &s) {
float InternalTestClass::kStaticFloatValue = 42;
static float PlainOldFunction() {
return 3.14145f;
return 3.14145;
}
@implementation TestClass

View File

@@ -27,7 +27,7 @@
// (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 "GTMSenTestCase.h"
#import <GTMSenTestCase.h>
#import "SimpleStringDictionary.h"
@interface SimpleStringDictionaryTest : GTMTestCase {

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