From d3eb5c3f88bf9a83883bcb3a585df844478b6cdd Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 24 Jun 2012 18:25:34 +0200 Subject: [PATCH] * Updated breakpad to latest version. --- thirdparty/breakpad/CMakeLists.txt | 6 +- .../client/apple/Framework/BreakpadDefines.h | 72 + .../breakpad/client/linux/android_link.h | 6 +- .../crash_generation_server.cc | 9 +- .../client/linux/handler/exception_handler.cc | 63 +- .../client/linux/handler/exception_handler.h | 1 - .../handler/exception_handler_unittest.cc | 69 +- thirdparty/breakpad/client/linux/log/log.cc | 48 + thirdparty/breakpad/client/linux/log/log.h | 41 + .../minidump_writer/line_reader_unittest.cc | 59 +- .../minidump_writer/linux_core_dumper.cc | 234 +++ .../linux/minidump_writer/linux_core_dumper.h | 122 ++ .../linux_core_dumper_unittest.cc | 109 + .../linux/minidump_writer/linux_dumper.cc | 369 +--- .../linux/minidump_writer/linux_dumper.h | 84 +- .../linux_dumper_unittest_helper.cc | 18 +- .../minidump_writer/linux_ptrace_dumper.cc | 293 +++ .../minidump_writer/linux_ptrace_dumper.h | 92 + ...est.cc => linux_ptrace_dumper_unittest.cc} | 162 +- .../linux/minidump_writer/minidump_writer.cc | 190 +- .../linux/minidump_writer/minidump_writer.h | 13 +- .../minidump_writer_unittest.cc | 84 +- .../sender/google_crash_report_sender.cc | 2 +- .../mac/Breakpad.xcodeproj/project.pbxproj | 200 +- .../breakpad/client/mac/Framework/Breakpad.h | 60 +- .../breakpad/client/mac/Framework/Breakpad.mm | 15 +- .../client/mac/Framework/OnDemandServer.mm | 71 +- .../client/mac/crash_generation/ConfigFile.h | 83 + .../client/mac/crash_generation/ConfigFile.mm | 190 ++ .../client/mac/crash_generation/Inspector.h | 63 +- .../client/mac/crash_generation/Inspector.mm | 238 +-- .../client/mac/handler/breakpad_exc_server.c | 1750 ----------------- .../client/mac/handler/breakpad_exc_server.h | 258 --- .../client/mac/handler/breakpad_nlist_64.cc | 29 +- .../client/mac/handler/dynamic_images.cc | 25 +- .../client/mac/handler/dynamic_images.h | 4 + .../client/mac/handler/exception_handler.cc | 353 ++-- .../client/mac/handler/exception_handler.h | 22 +- .../client/mac/handler/mach_vm_compat.h | 49 + .../client/mac/handler/minidump_generator.cc | 170 +- .../client/mac/handler/minidump_generator.h | 40 +- .../minidump_test.xcodeproj/project.pbxproj | 15 +- .../handler/testcases/DynamicImagesTests.cc | 18 +- .../client/mac/sender/crash_report_sender.h | 29 +- .../client/mac/sender/crash_report_sender.m | 591 +----- .../breakpad/client/mac/sender/uploader.h | 81 + .../breakpad/client/mac/sender/uploader.mm | 619 ++++++ .../mac/tests/crash_generation_server_test.cc | 52 +- .../mac/tests/exception_handler_test.cc | 52 +- .../mac/tests/minidump_generator_test.cc | 14 +- .../client/windows/breakpad_client.gyp | 0 .../breakpad/client/windows/build/common.gypi | 6 +- .../client/windows/build/external_code.gypi | 0 .../build/internal/release_defaults.gypi | 0 .../windows/build/internal/release_impl.gypi | 0 .../build/internal/release_impl_official.gypi | 0 .../client/windows/build/release.gypi | 0 .../windows/common/auto_critical_section.h | 22 +- .../client/windows/common/ipc_protocol.h | 18 +- .../windows/crash_generation/client_info.cc | 13 +- .../windows/crash_generation/client_info.h | 6 + .../crash_generation/crash_generation.gyp | 0 .../crash_generation_client.cc | 21 +- .../crash_generation_client.h | 4 + .../crash_generation_server.cc | 128 +- .../crash_generation_server.h | 20 +- .../windows/handler/exception_handler.cc | 114 +- .../windows/handler/exception_handler.gyp | 0 .../windows/handler/exception_handler.h | 13 +- .../windows/sender/crash_report_sender.gyp | 0 .../crash_generation_app.cc | 2 + .../crash_generation_app.gyp | 122 +- .../client/windows/unittests/client_tests.gyp | 0 .../unittests/crash_generation_server_test.cc | 604 +++--- .../client/windows/unittests/dump_analysis.cc | 368 ++-- .../client/windows/unittests/dump_analysis.h | 204 +- .../unittests/exception_handler_death_test.cc | 4 +- .../unittests/exception_handler_test.cc | 8 +- .../client/windows/unittests/minidump_test.cc | 664 +++---- thirdparty/breakpad/common/basictypes.h | 39 + .../breakpad/common/dwarf/dwarf2diehandler.cc | 12 +- .../breakpad/common/dwarf/dwarf2diehandler.h | 13 +- .../common/dwarf/dwarf2diehandler_unittest.cc | 19 + .../breakpad/common/dwarf/dwarf2enums.h | 8 +- .../breakpad/common/dwarf/dwarf2reader.cc | 123 +- .../breakpad/common/dwarf/dwarf2reader.h | 44 +- .../common/dwarf/dwarf2reader_cfi_unittest.cc | 2 + .../common/dwarf/dwarf2reader_die_unittest.cc | 486 +++++ .../common/dwarf/dwarf2reader_test_common.h | 149 ++ .../breakpad/common/dwarf/functioninfo.cc | 31 +- .../breakpad/common/dwarf/functioninfo.h | 34 +- thirdparty/breakpad/common/dwarf/types.h | 2 + .../breakpad/common/dwarf_cfi_to_module.cc | 2 +- .../common/dwarf_cfi_to_module_unittest.cc | 6 + .../breakpad/common/dwarf_cu_to_module.cc | 8 +- .../breakpad/common/dwarf_cu_to_module.h | 7 +- .../common/dwarf_cu_to_module_unittest.cc | 12 +- .../breakpad/common/dwarf_line_to_module.cc | 9 +- .../common/dwarf_line_to_module_unittest.cc | 6 +- thirdparty/breakpad/common/language.cc | 2 +- .../breakpad/common/linux/dump_symbols.cc | 22 +- .../breakpad/common/linux/dump_symbols.h | 4 +- .../common/linux/dump_symbols_unittest.cc | 26 +- .../breakpad/common/linux/elf_core_dump.cc | 179 ++ .../breakpad/common/linux/elf_core_dump.h | 153 ++ .../common/linux/elf_core_dump_unittest.cc | 245 +++ thirdparty/breakpad/common/linux/file_id.cc | 47 +- thirdparty/breakpad/common/linux/file_id.h | 2 +- .../breakpad/common/linux/file_id_unittest.cc | 119 +- .../breakpad/common/linux/guid_creator.cc | 24 +- .../breakpad/common/linux/http_upload.cc | 9 + .../breakpad/common/linux/http_upload.h | 3 + .../common/linux/linux_libc_support.h | 4 +- .../linux/linux_libc_support_unittest.cc | 8 +- .../common/linux/memory_mapped_file.cc | 108 + .../common/linux/memory_mapped_file.h | 86 + .../linux/memory_mapped_file_unittest.cc | 175 ++ .../linux/safe_readlink.cc} | 53 +- .../breakpad/common/linux/safe_readlink.h | 65 + .../common/linux/safe_readlink_unittest.cc | 89 + .../common/linux/tests/crash_generator.cc | 276 +++ .../common/linux/tests/crash_generator.h | 116 ++ .../breakpad/common/mac/Breakpad.xcconfig | 2 - .../breakpad/common/mac/HTTPMultipartUpload.h | 2 +- .../breakpad/common/mac/HTTPMultipartUpload.m | 10 +- thirdparty/breakpad/common/mac/MachIPC.mm | 8 +- .../common/mac/SimpleStringDictionary.h | 2 +- .../common/mac/SimpleStringDictionary.mm | 4 +- .../breakpad/common/mac/bootstrap_compat.cc | 42 + .../breakpad/common/mac/bootstrap_compat.h | 54 + thirdparty/breakpad/common/mac/dump_syms.h | 24 +- thirdparty/breakpad/common/mac/dump_syms.mm | 48 +- thirdparty/breakpad/common/mac/macho_id.cc | 45 +- thirdparty/breakpad/common/mac/macho_id.h | 17 +- .../breakpad/common/mac/macho_reader.cc | 6 + .../breakpad/common/mac/macho_walker.cc | 30 +- thirdparty/breakpad/common/mac/macho_walker.h | 17 +- thirdparty/breakpad/common/{md5.c => md5.cc} | 5 + thirdparty/breakpad/common/md5.h | 10 +- thirdparty/breakpad/common/memory.h | 12 + thirdparty/breakpad/common/memory_range.h | 145 ++ .../breakpad/common/memory_range_unittest.cc | 193 ++ thirdparty/breakpad/common/memory_unittest.cc | 7 +- thirdparty/breakpad/common/module.cc | 86 +- thirdparty/breakpad/common/module.h | 13 +- thirdparty/breakpad/common/module_unittest.cc | 68 +- thirdparty/breakpad/common/stabs_reader.cc | 12 +- .../breakpad/common/string_conversion.cc | 4 +- .../breakpad/common/windows/guid_string.h | 2 +- .../common/windows/pdb_source_line_writer.cc | 6 +- .../google_breakpad/common/breakpad_types.h | 2 +- .../google_breakpad/common/minidump_cpu_arm.h | 9 +- .../common/minidump_exception_mac.h | 8 +- .../google_breakpad/common/minidump_format.h | 1 + .../processor/minidump_processor.h | 2 +- .../source_line_resolver_interface.h | 2 +- .../processor/stack_frame_cpu.h | 2 +- .../google_breakpad/processor/stackwalker.h | 17 +- .../processor/symbol_supplier.h | 2 +- .../basic_source_line_resolver_unittest.cc | 4 + thirdparty/breakpad/processor/binarystream.h | 3 + .../breakpad/processor/disassembler_x86.cc | 3 + .../breakpad/processor/exploitability.cc | 1 + .../processor/fast_source_line_resolver.cc | 14 +- .../fast_source_line_resolver_unittest.cc | 3 + thirdparty/breakpad/processor/minidump.cc | 7 +- .../breakpad/processor/minidump_processor.cc | 14 +- .../breakpad/processor/module_comparer.cc | 1 + .../processor/postfix_evaluator-inl.h | 259 +-- .../breakpad/processor/postfix_evaluator.h | 14 +- .../processor/postfix_evaluator_unittest.cc | 7 +- .../processor/simple_serializer-inl.h | 3 + .../processor/source_line_resolver_base.cc | 14 +- thirdparty/breakpad/processor/stackwalker.cc | 8 +- .../breakpad/processor/stackwalker_amd64.cc | 37 +- .../processor/stackwalker_amd64_unittest.cc | 87 +- .../breakpad/processor/stackwalker_arm.cc | 79 +- .../breakpad/processor/stackwalker_arm.h | 12 +- .../processor/stackwalker_arm_unittest.cc | 202 +- .../breakpad/processor/stackwalker_x86.cc | 35 +- .../processor/stackwalker_x86_unittest.cc | 801 +++++--- .../processor/synth_minidump_unittest.cc | 2 +- .../breakpad/processor/windows_frame_info.h | 16 +- thirdparty/breakpad/third_party/curl/COPYING | 22 + .../breakpad/third_party/libdisasm/.dirstamp | 0 .../third_party/lss/linux_syscall_support.h | 47 +- 186 files changed, 9184 insertions(+), 5835 deletions(-) create mode 100644 thirdparty/breakpad/client/apple/Framework/BreakpadDefines.h create mode 100644 thirdparty/breakpad/client/linux/log/log.cc create mode 100644 thirdparty/breakpad/client/linux/log/log.h create mode 100644 thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.cc create mode 100644 thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.h create mode 100644 thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc create mode 100644 thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc create mode 100644 thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h rename thirdparty/breakpad/client/linux/minidump_writer/{linux_dumper_unittest.cc => linux_ptrace_dumper_unittest.cc} (76%) create mode 100644 thirdparty/breakpad/client/mac/crash_generation/ConfigFile.h create mode 100644 thirdparty/breakpad/client/mac/crash_generation/ConfigFile.mm delete mode 100644 thirdparty/breakpad/client/mac/handler/breakpad_exc_server.c delete mode 100644 thirdparty/breakpad/client/mac/handler/breakpad_exc_server.h create mode 100644 thirdparty/breakpad/client/mac/handler/mach_vm_compat.h create mode 100644 thirdparty/breakpad/client/mac/sender/uploader.h create mode 100644 thirdparty/breakpad/client/mac/sender/uploader.mm mode change 100755 => 100644 thirdparty/breakpad/client/windows/breakpad_client.gyp mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/common.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/external_code.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/internal/release_defaults.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/internal/release_impl.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/internal/release_impl_official.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/build/release.gypi mode change 100755 => 100644 thirdparty/breakpad/client/windows/crash_generation/crash_generation.gyp mode change 100755 => 100644 thirdparty/breakpad/client/windows/handler/exception_handler.gyp mode change 100755 => 100644 thirdparty/breakpad/client/windows/sender/crash_report_sender.gyp mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/client_tests.gyp mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/dump_analysis.cc mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/dump_analysis.h mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc mode change 100755 => 100644 thirdparty/breakpad/client/windows/unittests/minidump_test.cc create mode 100644 thirdparty/breakpad/common/basictypes.h create mode 100644 thirdparty/breakpad/common/dwarf/dwarf2reader_die_unittest.cc create mode 100644 thirdparty/breakpad/common/dwarf/dwarf2reader_test_common.h create mode 100644 thirdparty/breakpad/common/linux/elf_core_dump.cc create mode 100644 thirdparty/breakpad/common/linux/elf_core_dump.h create mode 100644 thirdparty/breakpad/common/linux/elf_core_dump_unittest.cc create mode 100644 thirdparty/breakpad/common/linux/memory_mapped_file.cc create mode 100644 thirdparty/breakpad/common/linux/memory_mapped_file.h create mode 100644 thirdparty/breakpad/common/linux/memory_mapped_file_unittest.cc rename thirdparty/breakpad/{client/mac/tests/auto_tempdir.h => common/linux/safe_readlink.cc} (62%) create mode 100644 thirdparty/breakpad/common/linux/safe_readlink.h create mode 100644 thirdparty/breakpad/common/linux/safe_readlink_unittest.cc create mode 100644 thirdparty/breakpad/common/linux/tests/crash_generator.cc create mode 100644 thirdparty/breakpad/common/linux/tests/crash_generator.h create mode 100644 thirdparty/breakpad/common/mac/bootstrap_compat.cc create mode 100644 thirdparty/breakpad/common/mac/bootstrap_compat.h rename thirdparty/breakpad/common/{md5.c => md5.cc} (99%) create mode 100644 thirdparty/breakpad/common/memory_range.h create mode 100644 thirdparty/breakpad/common/memory_range_unittest.cc create mode 100644 thirdparty/breakpad/third_party/curl/COPYING delete mode 100644 thirdparty/breakpad/third_party/libdisasm/.dirstamp diff --git a/thirdparty/breakpad/CMakeLists.txt b/thirdparty/breakpad/CMakeLists.txt index b590216b5..97dd7e3ab 100644 --- a/thirdparty/breakpad/CMakeLists.txt +++ b/thirdparty/breakpad/CMakeLists.txt @@ -39,6 +39,7 @@ IF(UNIX) ) ELSE(APPLE) SET( breakpadSources + common/linux/safe_readlink.cc client/linux/crash_generation/crash_generation_client.cc client/linux/crash_generation/crash_generation_server.cc client/linux/minidump_writer/minidump_writer.cc @@ -52,9 +53,10 @@ IF(UNIX) common/linux/http_upload.cc common/linux/guid_creator.cc common/linux/elf_symbols_to_module.cc - common/string_conversion.cc - common/convert_UTF.c client/minidump_file_writer.cc + client/linux/minidump_writer/linux_ptrace_dumper.cc + common/linux/memory_mapped_file.cc + client/linux/log/log.cc ) ENDIF(APPLE) diff --git a/thirdparty/breakpad/client/apple/Framework/BreakpadDefines.h b/thirdparty/breakpad/client/apple/Framework/BreakpadDefines.h new file mode 100644 index 000000000..8986cd1b9 --- /dev/null +++ b/thirdparty/breakpad/client/apple/Framework/BreakpadDefines.h @@ -0,0 +1,72 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Keys for configuration file +#define kReporterMinidumpDirectoryKey "MinidumpDir" +#define kReporterMinidumpIDKey "MinidumpID" + +// Filename for recording uploaded IDs +#define kReporterLogFilename "uploads.log" + +// The default subdirectory of the Library to put crash dumps in +// The subdirectory is +// ~/Library// +#define kDefaultLibrarySubdirectory "Breakpad" + +// Specify some special keys to be used in the configuration file that is +// generated by Breakpad and consumed by the crash_sender. +#define BREAKPAD_PRODUCT "BreakpadProduct" +#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" +#define BREAKPAD_VERSION "BreakpadVersion" +#define BREAKPAD_VENDOR "BreakpadVendor" +#define BREAKPAD_URL "BreakpadURL" +#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" +#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" +#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout" +#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" +#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation" +#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" +#define BREAKPAD_REPORTER_EXE_LOCATION \ + "BreakpadReporterExeLocation" +#define BREAKPAD_LOGFILES "BreakpadLogFiles" +#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" +#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" +#define BREAKPAD_COMMENTS "BreakpadComments" +#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail" +#define BREAKPAD_EMAIL "BreakpadEmail" +#define BREAKPAD_SERVER_TYPE "BreakpadServerType" +#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters" + +// The keys below are NOT user supplied, and are used internally. +#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime" +#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime" +#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" +#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" +#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" +#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" diff --git a/thirdparty/breakpad/client/linux/android_link.h b/thirdparty/breakpad/client/linux/android_link.h index 430ac3b6b..32261fc21 100644 --- a/thirdparty/breakpad/client/linux/android_link.h +++ b/thirdparty/breakpad/client/linux/android_link.h @@ -33,7 +33,11 @@ #ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_ #define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_ -#include +// TODO(zhenghao): exec_elf.h conflicts with linux/elf.h. +// But we still need ELFSIZE. +//#include +#include +#define ELFSIZE ARCH_ELFSIZE #ifndef ElfW #define ElfW(type) _ElfW (Elf, ELFSIZE, type) diff --git a/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc b/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc index 2f7edb697..e0272f94e 100644 --- a/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc +++ b/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc @@ -47,6 +47,7 @@ #include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/eintr_wrapper.h" #include "common/linux/guid_creator.h" +#include "common/linux/safe_readlink.h" static const char kCommandQuit = 'x'; @@ -79,12 +80,10 @@ 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) { + char buf[PATH_MAX]; + if (!google_breakpad::SafeReadLink(path, buf)) { return false; } - buf[n] = 0; if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { return false; @@ -130,7 +129,7 @@ FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) for (std::vector::const_iterator i = pids.begin(); i != pids.end(); ++i) { const pid_t current_pid = *i; - char buf[256]; + char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); DIR* fd = opendir(buf); if (!fd) diff --git a/thirdparty/breakpad/client/linux/handler/exception_handler.cc b/thirdparty/breakpad/client/linux/handler/exception_handler.cc index c2336ccf2..5d9e93f7f 100644 --- a/thirdparty/breakpad/client/linux/handler/exception_handler.cc +++ b/thirdparty/breakpad/client/linux/handler/exception_handler.cc @@ -73,19 +73,16 @@ #include #include #include +#include +#include +#include + #if !defined(__ANDROID__) #include -#endif -#include -#if !defined(__ANDROID__) #include #include -#endif -#include -#if !defined(__ANDROID__) #include #endif -#include #include #include @@ -93,6 +90,7 @@ #include "common/linux/linux_libc_support.h" #include "common/memory.h" +#include "client/linux/log/log.h" #include "client/linux/minidump_writer/linux_dumper.h" #include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/guid_creator.h" @@ -190,14 +188,18 @@ bool ExceptionHandler::InstallHandlers() { // such a small stack. static const unsigned kSigStackSize = 8192; - signal_stack = malloc(kSigStackSize); stack_t stack; - memset(&stack, 0, sizeof(stack)); - stack.ss_sp = signal_stack; - stack.ss_size = kSigStackSize; + // Only set an alternative stack if there isn't already one, or if the current + // one is too small. + if (sys_sigaltstack(NULL, &stack) == -1 || !stack.ss_sp || + stack.ss_size < kSigStackSize) { + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = malloc(kSigStackSize); + stack.ss_size = kSigStackSize; - if (sys_sigaltstack(&stack, NULL) == -1) - return false; + if (sys_sigaltstack(&stack, NULL) == -1) + return false; + } struct sigaction sa; memset(&sa, 0, sizeof(sa)); @@ -390,17 +392,23 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { // is the write() and read() calls will fail with EBADF static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ sys_pipe failed:"; - sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1); - sys_write(2, strerror(errno), strlen(strerror(errno))); - sys_write(2, "\n", 1); + logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); } +#if defined(__ANDROID__) + const pid_t child = clone( + ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + &thread_arg); +#else const pid_t child = sys_clone( ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL, NULL); +#endif int r, status; // Allow the child to ptrace us - prctl(PR_SET_PTRACER, child, 0, 0, 0); + sys_prctl(PR_SET_PTRACER, child); SendContinueSignalToChild(); do { r = sys_waitpid(child, &status, __WALL); @@ -411,9 +419,9 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { if (r == -1) { static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; - sys_write(2, msg, sizeof(msg) - 1); - sys_write(2, strerror(errno), strlen(strerror(errno))); - sys_write(2, "\n", 1); + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); } bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; @@ -433,9 +441,9 @@ void ExceptionHandler::SendContinueSignalToChild() { if(r == -1) { static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ sys_write failed:"; - sys_write(2, msg, sizeof(msg) - 1); - sys_write(2, strerror(errno), strlen(strerror(errno))); - sys_write(2, "\n", 1); + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); } } @@ -448,9 +456,9 @@ void ExceptionHandler::WaitForContinueSignal() { if(r == -1) { static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ sys_read failed:"; - sys_write(2, msg, sizeof(msg) - 1); - sys_write(2, strerror(errno), strlen(strerror(errno))); - sys_write(2, "\n", 1); + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); } } @@ -503,7 +511,8 @@ void ExceptionHandler::AddMappingInfo(const std::string& name, info.start_addr = start_address; info.size = mapping_size; info.offset = file_offset; - strncpy(info.name, name.c_str(), std::min(name.size(), sizeof(info))); + strncpy(info.name, name.c_str(), sizeof(info.name) - 1); + info.name[sizeof(info.name) - 1] = '\0'; MappingEntry mapping; mapping.first = info; diff --git a/thirdparty/breakpad/client/linux/handler/exception_handler.h b/thirdparty/breakpad/client/linux/handler/exception_handler.h index e75517ead..94766eba9 100644 --- a/thirdparty/breakpad/client/linux/handler/exception_handler.h +++ b/thirdparty/breakpad/client/linux/handler/exception_handler.h @@ -228,7 +228,6 @@ class ExceptionHandler { const char* next_minidump_id_c_; const bool handler_installed_; - void* signal_stack; // the handler stack. HandlerCallback crash_handler_; // The global exception handler stack. This is need becuase there may exist diff --git a/thirdparty/breakpad/client/linux/handler/exception_handler_unittest.cc b/thirdparty/breakpad/client/linux/handler/exception_handler_unittest.cc index 7186db9f0..6fcf251b1 100644 --- a/thirdparty/breakpad/client/linux/handler/exception_handler_unittest.cc +++ b/thirdparty/breakpad/client/linux/handler/exception_handler_unittest.cc @@ -44,17 +44,12 @@ #include "common/linux/eintr_wrapper.h" #include "common/linux/file_id.h" #include "common/linux/linux_libc_support.h" +#include "common/tests/auto_tempdir.h" #include "third_party/lss/linux_syscall_support.h" #include "google_breakpad/processor/minidump.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; @@ -79,7 +74,8 @@ class ExceptionHandlerTest : public ::testing::Test { }; TEST(ExceptionHandlerTest, Simple) { - ExceptionHandler handler(TEMPDIR, NULL, NULL, NULL, true); + AutoTempDir temp_dir; + ExceptionHandler handler(temp_dir.path(), NULL, NULL, NULL, true); } static bool DoneCallback(const char* dump_path, @@ -99,13 +95,14 @@ static bool DoneCallback(const char* dump_path, } TEST(ExceptionHandlerTest, ChildCrash) { + AutoTempDir temp_dir; 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], + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, (void*) fds[1], true); *reinterpret_cast(NULL) = 0; } @@ -133,7 +130,7 @@ TEST(ExceptionHandlerTest, ChildCrash) { filename[len] = 0; close(fds[0]); - const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + + const std::string minidump_filename = temp_dir.path() + "/" + filename + ".dmp"; struct stat st; @@ -145,6 +142,7 @@ TEST(ExceptionHandlerTest, ChildCrash) { // Test that memory around the instruction pointer is written // to the dump as a MinidumpMemoryRegion. TEST(ExceptionHandlerTest, InstructionPointerMemory) { + AutoTempDir temp_dir; int fds[2]; ASSERT_NE(pipe(fds), -1); @@ -158,8 +156,8 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], - true); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -206,7 +204,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { filename[len] = 0; close(fds[0]); - const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + + const std::string minidump_filename = temp_dir.path() + "/" + filename + ".dmp"; struct stat st; @@ -269,6 +267,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { // Test that the memory region around the instruction pointer is // bounded correctly on the low end. TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { + AutoTempDir temp_dir; int fds[2]; ASSERT_NE(pipe(fds), -1); @@ -282,8 +281,8 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], - true); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -330,7 +329,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { filename[len] = 0; close(fds[0]); - const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + + const std::string minidump_filename = temp_dir.path() + "/" + filename + ".dmp"; struct stat st; @@ -390,6 +389,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { // Test that the memory region around the instruction pointer is // bounded correctly on the high end. TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { + AutoTempDir temp_dir; int fds[2]; ASSERT_NE(pipe(fds), -1); @@ -406,8 +406,8 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], - true); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -454,7 +454,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { filename[len] = 0; close(fds[0]); - const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + + const std::string minidump_filename = temp_dir.path() + "/" + filename + ".dmp"; struct stat st; @@ -515,6 +515,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { // Ensure that an extra memory block doesn't get added when the // instruction pointer is not in mapped memory. TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { + AutoTempDir temp_dir; int fds[2]; ASSERT_NE(pipe(fds), -1); @@ -522,8 +523,8 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { const pid_t child = fork(); if (child == 0) { close(fds[0]); - ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], - true); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); // Try calling a NULL pointer. typedef void (*void_function)(void); void_function memory_function = @@ -554,7 +555,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { filename[len] = 0; close(fds[0]); - const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + + const std::string minidump_filename = temp_dir.path() + "/" + filename + ".dmp"; struct stat st; @@ -625,11 +626,12 @@ TEST(ExceptionHandlerTest, ModuleInfo) { MAP_PRIVATE | MAP_ANON, -1, 0)); - const u_int64_t kMemoryAddress = reinterpret_cast(memory); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); ASSERT_TRUE(memory); string minidump_filename; - ExceptionHandler handler(TEMPDIR, NULL, SimpleCallback, + AutoTempDir temp_dir; + ExceptionHandler handler(temp_dir.path(), NULL, SimpleCallback, (void*)&minidump_filename, true); // Add info about the anonymous memory mapping. handler.AddMappingInfo(kMemoryName, @@ -667,7 +669,14 @@ CrashHandler(const void* crash_context, size_t crash_context_size, void* context) { const int fd = (intptr_t) context; int fds[2]; - pipe(fds); + if (pipe(fds) == -1) { + // There doesn't seem to be any way to reliably handle + // this failure without the parent process hanging + // At least make sure that this process doesn't access + // unexpected file descriptors + fds[0] = -1; + fds[1] = -1; + } struct kernel_msghdr msg = {0}; struct kernel_iovec iov; iov.iov_base = const_cast(crash_context); @@ -734,6 +743,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) { ASSERT_EQ(n, kCrashContextSize); ASSERT_EQ(msg.msg_controllen, kControlMsgSize); ASSERT_EQ(msg.msg_flags, 0); + ASSERT_EQ(close(fds[0]), 0); pid_t crashing_pid = -1; int signal_fd = -1; @@ -756,12 +766,13 @@ TEST(ExceptionHandlerTest, ExternalDumper) { ASSERT_NE(crashing_pid, -1); ASSERT_NE(signal_fd, -1); - char templ[] = TEMPDIR "/exception-handler-unittest-XXXXXX"; - mktemp(templ); - ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/exception-handler-unittest"; + ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context, kCrashContextSize)); static const char b = 0; HANDLE_EINTR(write(signal_fd, &b, 1)); + ASSERT_EQ(close(signal_fd), 0); int status; ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); @@ -769,7 +780,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) { ASSERT_EQ(WTERMSIG(status), SIGSEGV); struct stat st; - ASSERT_EQ(stat(templ, &st), 0); + ASSERT_EQ(stat(templ.c_str(), &st), 0); ASSERT_GT(st.st_size, 0u); - unlink(templ); + unlink(templ.c_str()); } diff --git a/thirdparty/breakpad/client/linux/log/log.cc b/thirdparty/breakpad/client/linux/log/log.cc new file mode 100644 index 000000000..186359139 --- /dev/null +++ b/thirdparty/breakpad/client/linux/log/log.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2012 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "client/linux/log/log.h" + +#if defined(__ANDROID__) +#include +#else +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace logger { + +int write(const char* buf, size_t nbytes) { +#if defined(__ANDROID__) + return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf); +#else + return sys_write(2, buf, nbytes); +#endif +} + +} // namespace logger diff --git a/thirdparty/breakpad/client/linux/log/log.h b/thirdparty/breakpad/client/linux/log/log.h new file mode 100644 index 000000000..a50e30dcd --- /dev/null +++ b/thirdparty/breakpad/client/linux/log/log.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_LOG_LOG_H_ +#define CLIENT_LINUX_LOG_LOG_H_ + +#include + +namespace logger { + +int write(const char* buf, size_t nbytes); + +} // namespace logger + +#endif // CLIENT_LINUX_LOG_LOG_H_ diff --git a/thirdparty/breakpad/client/linux/minidump_writer/line_reader_unittest.cc b/thirdparty/breakpad/client/linux/minidump_writer/line_reader_unittest.cc index a2ea17f71..17a54679d 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/line_reader_unittest.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/line_reader_unittest.cc @@ -33,6 +33,7 @@ #include "client/linux/minidump_writer/line_reader.h" #include "breakpad_googletest_includes.h" +#include "common/linux/eintr_wrapper.h" using namespace google_breakpad; @@ -70,16 +71,17 @@ TEST(LineReaderTest, EmptyFile) { TEST(LineReaderTest, OneLineTerminated) { const int fd = TemporaryFile(); - write(fd, "a\n", 2); + const int r = HANDLE_EINTR(write(fd, "a\n", 2)); + ASSERT_EQ(2, r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); const char *line; unsigned int len; ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned int)1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned int)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_FALSE(reader.GetNextLine(&line, &len)); @@ -89,16 +91,17 @@ TEST(LineReaderTest, OneLineTerminated) { TEST(LineReaderTest, OneLine) { const int fd = TemporaryFile(); - write(fd, "a", 1); + const int r = HANDLE_EINTR(write(fd, "a", 1)); + ASSERT_EQ(1, r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); const char *line; unsigned len; ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned)1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_FALSE(reader.GetNextLine(&line, &len)); @@ -108,22 +111,23 @@ TEST(LineReaderTest, OneLine) { TEST(LineReaderTest, TwoLinesTerminated) { const int fd = TemporaryFile(); - write(fd, "a\nb\n", 4); + const int r = HANDLE_EINTR(write(fd, "a\nb\n", 4)); + ASSERT_EQ(4, r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); const char *line; unsigned len; ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned)1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned)1); - ASSERT_EQ(line[0], 'b'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_FALSE(reader.GetNextLine(&line, &len)); @@ -133,22 +137,23 @@ TEST(LineReaderTest, TwoLinesTerminated) { TEST(LineReaderTest, TwoLines) { const int fd = TemporaryFile(); - write(fd, "a\nb", 3); + const int r = HANDLE_EINTR(write(fd, "a\nb", 3)); + ASSERT_EQ(3, r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); const char *line; unsigned len; ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned)1); - ASSERT_EQ(line[0], 'a'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, (unsigned)1); - ASSERT_EQ(line[0], 'b'); - ASSERT_EQ(line[1], 0); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); reader.PopLine(len); ASSERT_FALSE(reader.GetNextLine(&line, &len)); @@ -160,16 +165,17 @@ TEST(LineReaderTest, MaxLength) { const int fd = TemporaryFile(); char l[LineReader::kMaxLineLen - 1]; memset(l, 'a', sizeof(l)); - write(fd, l, sizeof(l)); + const int r = HANDLE_EINTR(write(fd, l, sizeof(l))); + ASSERT_EQ(sizeof(l), r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); const char *line; unsigned len; ASSERT_TRUE(reader.GetNextLine(&line, &len)); - ASSERT_EQ(len, sizeof(l)); + ASSERT_EQ(sizeof(l), len); ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); - ASSERT_EQ(line[len], 0); + ASSERT_EQ('\0', line[len]); close(fd); } @@ -178,7 +184,8 @@ TEST(LineReaderTest, TooLong) { const int fd = TemporaryFile(); char l[LineReader::kMaxLineLen]; memset(l, 'a', sizeof(l)); - write(fd, l, sizeof(l)); + const int r = HANDLE_EINTR(write(fd, l, sizeof(l))); + ASSERT_EQ(sizeof(l), r); lseek(fd, 0, SEEK_SET); LineReader reader(fd); diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.cc new file mode 100644 index 000000000..3e8c92fea --- /dev/null +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.cc @@ -0,0 +1,234 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. +// See linux_core_dumper.h for details. + +#include "client/linux/minidump_writer/linux_core_dumper.h" + +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_libc_support.h" + +namespace google_breakpad { + +LinuxCoreDumper::LinuxCoreDumper(pid_t pid, + const char* core_path, + const char* procfs_path) + : LinuxDumper(pid), + core_path_(core_path), + procfs_path_(procfs_path), + thread_infos_(&allocator_, 8) { + assert(core_path_); +} + +bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + size_t procfs_path_len = my_strlen(procfs_path_); + size_t total_length = procfs_path_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + memcpy(path, procfs_path_, procfs_path_len); + path[procfs_path_len] = '/'; + memcpy(path + procfs_path_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + ElfCoreDump::Addr virtual_address = reinterpret_cast(src); + // TODO(benchan): Investigate whether the data to be copied could span + // across multiple segments in the core dump file. ElfCoreDump::CopyData + // and this method do not handle that case yet. + if (!core_.CopyData(dest, virtual_address, length)) { + // If the data segment is not found in the core dump, fill the result + // with marker characters. + memset(dest, 0xab, length); + } +} + +bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= thread_infos_.size()) + return false; + + *info = thread_infos_[index]; + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + return GetStackInfo(&info->stack, &info->stack_len, + reinterpret_cast(stack_pointer)); +} + +bool LinuxCoreDumper::IsPostMortem() const { + return true; +} + +bool LinuxCoreDumper::ThreadsSuspend() { + return true; +} + +bool LinuxCoreDumper::ThreadsResume() { + return true; +} + +bool LinuxCoreDumper::EnumerateThreads() { + if (!mapped_core_file_.Map(core_path_)) { + fprintf(stderr, "Could not map core dump file into memory\n"); + return false; + } + + core_.SetContent(mapped_core_file_.content()); + if (!core_.IsValid()) { + fprintf(stderr, "Invalid core dump file\n"); + return false; + } + + ElfCoreDump::Note note = core_.GetFirstNote(); + if (!note.IsValid()) { + fprintf(stderr, "PT_NOTE section not found\n"); + return false; + } + + bool first_thread = true; + do { + ElfCoreDump::Word type = note.GetType(); + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + + if (type == 0 || name.IsEmpty() || description.IsEmpty()) { + fprintf(stderr, "Could not found a valid PT_NOTE.\n"); + return false; + } + + // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are + // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): + // Thread Name Type + // ------------------------------------------------------------------- + // 1st thread CORE NT_PRSTATUS + // process-wide CORE NT_PRPSINFO + // process-wide CORE NT_AUXV + // 1st thread CORE NT_FPREGSET + // 1st thread LINUX NT_PRXFPREG + // 1st thread LINUX NT_386_TLS + // + // 2nd thread CORE NT_PRSTATUS + // 2nd thread CORE NT_FPREGSET + // 2nd thread LINUX NT_PRXFPREG + // 2nd thread LINUX NT_386_TLS + // + // 3rd thread CORE NT_PRSTATUS + // 3rd thread CORE NT_FPREGSET + // 3rd thread LINUX NT_PRXFPREG + // 3rd thread LINUX NT_386_TLS + // + // The following code only works if notes are ordered as expected. + switch (type) { + case NT_PRSTATUS: { + if (description.length() != sizeof(elf_prstatus)) { + fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); + return false; + } + + const elf_prstatus* status = + reinterpret_cast(description.data()); + pid_t pid = status->pr_pid; + ThreadInfo info; + memset(&info, 0, sizeof(ThreadInfo)); + info.tgid = status->pr_pgrp; + info.ppid = status->pr_ppid; + memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); + if (first_thread) { + crash_thread_ = pid; + crash_signal_ = status->pr_info.si_signo; + } + first_thread = false; + threads_.push_back(pid); + thread_infos_.push_back(info); + break; + } +#if defined(__i386) || defined(__x86_64) + case NT_FPREGSET: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpregs)) { + fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); + break; + } +#endif +#if defined(__i386) + case NT_PRXFPREG: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpxregs)) { + fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); + break; + } +#endif + } + note = note.GetNextNote(); + } while (note.IsValid()); + + return true; +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.h b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.h new file mode 100644 index 000000000..edb9e738d --- /dev/null +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via its core dump and proc files. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" + +namespace google_breakpad { + +class LinuxCoreDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid| via its core dump file at |core_path| and + // its proc files at |procfs_path|. If |procfs_path| is a copy of + // /proc/, it should contain the following files: + // auxv, cmdline, environ, exe, maps, status + LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Return true on + // success. + // + // As this dumper performs a post-mortem dump and makes use of a copy + // of the proc files of the crashed process, this derived method does + // not actually make use of |pid| and always returns a subpath of + // |procfs_path_| regardless of whether |pid| corresponds to the main + // process or a thread of the process, i.e. assuming both the main process + // and its threads have the following proc files with the same content: + // auxv, cmdline, environ, exe, maps, status + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method extracts the content + // the core dump and fills |dest| with a sequence of marker bytes + // if the expected data is not found in the core dump. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns true to indicate that this dumper performs a + // post-mortem dump of a crashed process via a core dump file. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to suspend. This method does nothing and + // always returns true. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to resume. This method does nothing and + // always returns true. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Path of the core dump file. + const char* core_path_; + + // Path of the directory containing the proc files of the given process, + // which is usually a copy of /proc/. + const char* procfs_path_; + + // Memory-mapped core dump file at |core_path_|. + MemoryMappedFile mapped_core_file_; + + // Content of the core dump file. + ElfCoreDump core_; + + // Thread info found in the core dump file. + wasteful_vector thread_infos_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_ diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc new file mode 100644 index 000000000..1fb26edef --- /dev/null +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// linux_core_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxCoreDumoer. + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_core_dumper.h" +#include "common/linux/tests/crash_generator.h" + +using std::string; +using namespace google_breakpad; + +TEST(LinuxCoreDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + const char procfs_path[] = "/procfs_copy"; + LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); + + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "%s/maps", procfs_path); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); + + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); + + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); +} + +TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to non-default core pattern\n"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + pid_t child_pid; + // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in + // CrashGenerator is identified and fixed. + if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, &child_pid)) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to no core dump generated\n"); + return; + } + + pid_t pid = getpid(); + const string core_file = crash_generator.GetCoreFilePath(); + const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); + LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); + dumper.Init(); + + EXPECT_TRUE(dumper.IsPostMortem()); + + // These are no-ops and should always return true. + EXPECT_TRUE(dumper.ThreadsSuspend()); + EXPECT_TRUE(dumper.ThreadsResume()); + + // LinuxCoreDumper cannot determine the crash address and thus it always + // sets the crash address to 0. + EXPECT_EQ(0, dumper.crash_address()); + EXPECT_EQ(kCrashSignal, dumper.crash_signal()); + EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), + dumper.crash_thread()); + + EXPECT_EQ(kNumOfThreads, dumper.threads().size()); + for (unsigned i = 0; i < kNumOfThreads; ++i) { + ThreadInfo info; + EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); + EXPECT_EQ(getpid(), info.ppid); + } +} diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.cc index ab3a5ea54..e9d211ec7 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.cc @@ -27,6 +27,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// linux_dumper.cc: Implement google_breakpad::LinuxDumper. +// See linux_dumper.h for details. + // This code deals with the mechanics of getting information about a crashed // process. Since this code may run in a compromised address space, the same // rules apply as detailed at the top of minidump_writer.h: no libc calls and @@ -34,75 +37,22 @@ #include "client/linux/minidump_writer/linux_dumper.h" -#include #include -#include #include #include -#if !defined(__ANDROID__) -#include -#endif #include -#include -#include #include -#include -#include -#include -#include - -#include "client/linux/minidump_writer/directory_reader.h" #include "client/linux/minidump_writer/line_reader.h" #include "common/linux/file_id.h" #include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" +#include "common/linux/safe_readlink.h" #include "third_party/lss/linux_syscall_support.h" static const char kMappedFileUnsafePrefix[] = "/dev/"; static const char kDeletedSuffix[] = " (deleted)"; -// Suspend a thread by attaching to it. -static bool SuspendThread(pid_t pid) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (sys_waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } -#if defined(__i386) || defined(__x86_64) - // On x86, the stack pointer is NULL or -1, when executing trusted code in - // the seccomp sandbox. Not only does this cause difficulties down the line - // when trying to dump the thread's stack, it also results in the minidumps - // containing information about the trusted threads. This information is - // generally completely meaningless and just pollutes the minidumps. - // We thus test the stack pointer and exclude any threads that are part of - // the seccomp sandbox's trusted code. - user_regs_struct regs; - if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || -#if defined(__i386) - !regs.esp -#elif defined(__x86_64) - !regs.rsp -#endif - ) { - sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } -#endif - return true; -} - -// Resume a thread by detaching from it. -static bool ResumeThread(pid_t pid) { - return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - inline static bool IsMappedFileOpenUnsafe( const google_breakpad::MappingInfo& mapping) { // It is unsafe to attempt to open a mapped file that lives under /dev, @@ -116,90 +66,20 @@ inline static bool IsMappedFileOpenUnsafe( namespace google_breakpad { -LinuxDumper::LinuxDumper(int pid) +LinuxDumper::LinuxDumper(pid_t pid) : pid_(pid), - threads_suspended_(false), + crash_address_(0), + crash_signal_(0), + crash_thread_(0), threads_(&allocator_, 8), mappings_(&allocator_) { } +LinuxDumper::~LinuxDumper() { +} + bool LinuxDumper::Init() { - return EnumerateThreads(&threads_) && - EnumerateMappings(&mappings_); -} - -bool LinuxDumper::ThreadsSuspend() { - if (threads_suspended_) - return true; - for (size_t i = 0; i < threads_.size(); ++i) { - if (!SuspendThread(threads_[i])) { - // If the thread either disappeared before we could attach to it, or if - // it was part of the seccomp sandbox's trusted code, it is OK to - // silently drop it from the minidump. - memmove(&threads_[i], &threads_[i+1], - (threads_.size() - i - 1) * sizeof(threads_[i])); - threads_.resize(threads_.size() - 1); - --i; - } - } - threads_suspended_ = true; - return threads_.size() > 0; -} - -bool LinuxDumper::ThreadsResume() { - if (!threads_suspended_) - return false; - bool good = true; - for (size_t i = 0; i < threads_.size(); ++i) - good &= ResumeThread(threads_[i]); - threads_suspended_ = false; - return good; -} - -void -LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { - assert(path); - if (!path) { - return; - } - - path[0] = '\0'; - - const unsigned pid_len = my_int_len(pid); - - assert(node); - if (!node) { - return; - } - - size_t node_len = my_strlen(node); - assert(node_len < NAME_MAX); - if (node_len >= NAME_MAX) { - return; - } - - assert(node_len > 0); - if (node_len == 0) { - return; - } - - assert(pid > 0); - if (pid <= 0) { - return; - } - - const size_t total_length = 6 + pid_len + 1 + node_len; - - assert(total_length < NAME_MAX); - if (total_length >= NAME_MAX) { - return; - } - - memcpy(path, "/proc/", 6); - my_itos(path + 6, pid, pid_len); - memcpy(path + 6 + pid_len, "/", 1); - memcpy(path + 6 + pid_len + 1, node, node_len); - path[total_length] = '\0'; + return EnumerateThreads() && EnumerateMappings(); } bool @@ -237,24 +117,12 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, filename[filename_len] = '\0'; bool filename_modified = HandleDeletedFileInMapping(filename); - int fd = sys_open(filename, O_RDONLY, 0); - if (fd < 0) - return false; - struct kernel_stat st; - if (sys_fstat(fd, &st) != 0) { - sys_close(fd); - return false; - } -#if defined(__x86_64) -#define sys_mmap2 sys_mmap -#endif - void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - sys_close(fd); - if (base == MAP_FAILED) + MemoryMappedFile mapped_file(filename); + if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? return false; - bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier); - sys_munmap(base, st.st_size); + bool success = + FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); if (success && member && filename_modified) { mappings_[mapping_id]->name[filename_len - sizeof(kDeletedSuffix) + 1] = '\0'; @@ -264,12 +132,10 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, } void* -LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const { char auxv_path[NAME_MAX]; - BuildProcPath(auxv_path, pid, "auxv"); - - // If BuildProcPath errors out due to invalid input, we'll handle it when - // we try to sys_open the file. + if (!BuildProcPath(auxv_path, pid, "auxv")) + return NULL; // Find the AT_SYSINFO_EHDR entry for linux-gate.so // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more @@ -293,10 +159,36 @@ LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { return NULL; } -bool -LinuxDumper::EnumerateMappings(wasteful_vector* result) const { +void* +LinuxDumper::FindEntryPoint(pid_t pid) const { + char auxv_path[NAME_MAX]; + if (!BuildProcPath(auxv_path, pid, "auxv")) + return NULL; + + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + // Find the AT_ENTRY entry + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_ENTRY) { + close(fd); + return reinterpret_cast(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + +bool LinuxDumper::EnumerateMappings() { char maps_path[NAME_MAX]; - BuildProcPath(maps_path, pid_, "maps"); + if (!BuildProcPath(maps_path, pid_, "maps")) + return false; // linux_gate_loc is the beginning of the kernel's mapping of // linux-gate.so in the process. It doesn't actually show up in the @@ -305,6 +197,10 @@ LinuxDumper::EnumerateMappings(wasteful_vector* result) const { // of mappings. const void* linux_gate_loc; linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); + // Although the initial executable is usually the first mapping, it's not + // guaranteed (see http://crosbug.com/25355); therefore, try to use the + // actual entry point to find the mapping. + const void* entry_point_loc = FindEntryPoint(pid_); const int fd = sys_open(maps_path, O_RDONLY, 0); if (fd < 0) @@ -333,8 +229,8 @@ LinuxDumper::EnumerateMappings(wasteful_vector* result) const { } // Merge adjacent mappings with the same name into one module, // assuming they're a single library mapped by the dynamic linker - if (name && result->size()) { - MappingInfo* module = (*result)[result->size() - 1]; + if (name && !mappings_.empty()) { + MappingInfo* module = mappings_.back(); if ((start_addr == module->start_addr + module->size) && (my_strlen(name) == my_strlen(module->name)) && (my_strncmp(name, module->name, my_strlen(name)) == 0)) { @@ -353,7 +249,25 @@ LinuxDumper::EnumerateMappings(wasteful_vector* result) const { if (l < sizeof(module->name)) memcpy(module->name, name, l); } - result->push_back(module); + // If this is the entry-point mapping, and it's not already the + // first one, then we need to make it be first. This is because + // the minidump format assumes the first module is the one that + // corresponds to the main executable (as codified in + // processor/minidump.cc:MinidumpModuleList::GetMainModule()). + if (entry_point_loc && + (entry_point_loc >= + reinterpret_cast(module->start_addr)) && + (entry_point_loc < + reinterpret_cast(module->start_addr+module->size)) && + !mappings_.empty()) { + // push the module onto the front of the list. + mappings_.resize(mappings_.size() + 1); + for (size_t idx = mappings_.size() - 1; idx > 0; idx--) + mappings_[idx] = mappings_[idx - 1]; + mappings_[0] = module; + } else { + mappings_.push_back(module); + } } } } @@ -362,114 +276,7 @@ LinuxDumper::EnumerateMappings(wasteful_vector* result) const { sys_close(fd); - return result->size() > 0; -} - -// Parse /proc/$pid/task to list all the threads of the process identified by -// pid. -bool LinuxDumper::EnumerateThreads(wasteful_vector* result) const { - char task_path[NAME_MAX]; - BuildProcPath(task_path, pid_, "task"); - - const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); - if (fd < 0) - return false; - DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); - - // The directory may contain duplicate entries which we filter by assuming - // that they are consecutive. - int last_tid = -1; - const char* dent_name; - while (dir_reader->GetNextEntry(&dent_name)) { - if (my_strcmp(dent_name, ".") && - my_strcmp(dent_name, "..")) { - int tid = 0; - if (my_strtoui(&tid, dent_name) && - last_tid != tid) { - last_tid = tid; - result->push_back(tid); - } - } - dir_reader->PopEntry(); - } - - sys_close(fd); - return true; -} - -// Read thread info from /proc/$pid/status. -// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, -// these members are set to -1. Returns true iff all three members are -// available. -bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { - assert(info != NULL); - char status_path[NAME_MAX]; - BuildProcPath(status_path, tid, "status"); - - const int fd = open(status_path, O_RDONLY); - if (fd < 0) - return false; - - LineReader* const line_reader = new(allocator_) LineReader(fd); - const char* line; - unsigned line_len; - - info->ppid = info->tgid = -1; - - while (line_reader->GetNextLine(&line, &line_len)) { - if (my_strncmp("Tgid:\t", line, 6) == 0) { - my_strtoui(&info->tgid, line + 6); - } else if (my_strncmp("PPid:\t", line, 6) == 0) { - my_strtoui(&info->ppid, line + 6); - } - - line_reader->PopLine(line_len); - } - - if (info->ppid == -1 || info->tgid == -1) - return false; - - if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { - return false; - } - -#if !defined(__ANDROID__) - if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { - return false; - } -#endif - -#if defined(__i386) - if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) - return false; -#endif - -#if defined(__i386) || defined(__x86_64) - for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { - if (sys_ptrace( - PTRACE_PEEKUSER, tid, - reinterpret_cast (offsetof(struct user, - u_debugreg[0]) + i * - sizeof(debugreg_t)), - &info->dregs[i]) == -1) { - return false; - } - } -#endif - - const uint8_t* stack_pointer; -#if defined(__i386) - memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); -#elif defined(__x86_64) - memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); -#elif defined(__ARM_EABI__) - memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); -#else -#error "This code hasn't been ported to your platform yet." -#endif - - return GetStackInfo(&info->stack, &info->stack_len, - (uintptr_t) stack_pointer); + return !mappings_.empty(); } // Get information about the stack, given the stack pointer. We don't try to @@ -498,25 +305,6 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, return true; } -// static -void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length) { - unsigned long tmp = 55; - size_t done = 0; - static const size_t word_size = sizeof(tmp); - uint8_t* const local = (uint8_t*) dest; - uint8_t* const remote = (uint8_t*) src; - - while (done < length) { - const size_t l = length - done > word_size ? word_size : length - done; - if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { - tmp = 0; - } - memcpy(local + done, &tmp, l); - done += l; - } -} - // Find the mapping which the given memory address falls in. const MappingInfo* LinuxDumper::FindMapping(const void* address) const { const uintptr_t addr = (uintptr_t) address; @@ -546,11 +334,10 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { // Check |path| against the /proc/pid/exe 'symlink'. char exe_link[NAME_MAX]; char new_path[NAME_MAX]; - BuildProcPath(exe_link, pid_, "exe"); - ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX); - if (new_path_len <= 0 || new_path_len == NAME_MAX) + if (!BuildProcPath(exe_link, pid_, "exe")) + return false; + if (!SafeReadLink(exe_link, new_path)) return false; - new_path[new_path_len] = '\0'; if (my_strcmp(path, new_path) != 0) return false; diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.h b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.h index 152802006..42b2a9915 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.h +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper.h @@ -27,6 +27,14 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which +// is a base class for extracting information of a crashed process. It +// was originally a complete implementation using the ptrace API, but +// has been refactored to allow derived implementations supporting both +// ptrace and core dump. A portion of the original implementation is now +// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for +// details). + #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ @@ -66,7 +74,7 @@ typedef struct #define AT_SYSINFO_EHDR 33 #endif #endif // __ANDROID__ -#elif defined(__x86_64__) +#elif defined(__x86_64) typedef Elf64_auxv_t elf_aux_entry; #endif // When we find the VDSO mapping in the process's address space, this @@ -118,16 +126,21 @@ class LinuxDumper { public: explicit LinuxDumper(pid_t pid); + virtual ~LinuxDumper(); + // Parse the data for |threads| and |mappings|. - bool Init(); + virtual bool Init(); + + // Return true if the dumper performs a post-mortem dump. + virtual bool IsPostMortem() const = 0; // Suspend/resume all threads in the given process. - bool ThreadsSuspend(); - bool ThreadsResume(); + virtual bool ThreadsSuspend() = 0; + virtual bool ThreadsResume() = 0; - // Read information about the given thread. Returns true on success. One must - // have called |ThreadsSuspend| first. - bool ThreadInfoGet(pid_t tid, ThreadInfo* info); + // Read information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; // These are only valid after a call to |Init|. const wasteful_vector &threads() { return threads_; } @@ -142,14 +155,16 @@ class LinuxDumper { PageAllocator* allocator() { return &allocator_; } - // memcpy from a remote process. - static void CopyFromProcess(void* dest, pid_t child, const void* src, - size_t length); + // Copy content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length) = 0; - // Builds a proc path for a certain pid for a node. path is a - // character array that is overwritten, and node is the final node - // without any slashes. - void BuildProcPath(char* path, pid_t pid, const char* node) const; + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; // Generate a File ID from the .text section of a mapped entry. // If not a member, mapping_id is ignored. @@ -163,10 +178,25 @@ class LinuxDumper { // [vdso], but we can't guarantee that it's the only virtual dynamic // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR // is the safest way to go.) - void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; - private: - bool EnumerateMappings(wasteful_vector* result) const; - bool EnumerateThreads(wasteful_vector* result) const; + void* FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const; + // Utility method to find the entry point location. + void* FindEntryPoint(pid_t pid) const; + + uintptr_t crash_address() const { return crash_address_; } + void set_crash_address(uintptr_t crash_address) { + crash_address_ = crash_address; + } + + int crash_signal() const { return crash_signal_; } + void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } + + pid_t crash_thread() const { return crash_thread_; } + void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } + + protected: + virtual bool EnumerateMappings(); + + virtual bool EnumerateThreads() = 0; // For the case where a running program has been deleted, it'll show up in // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then @@ -179,13 +209,25 @@ class LinuxDumper { // Returns true if |path| is modified. bool HandleDeletedFileInMapping(char* path) const; + // ID of the crashed process. const pid_t pid_; + // Virtual address at which the process crashed. + uintptr_t crash_address_; + + // Signal that terminated the crashed process. + int crash_signal_; + + // ID of the crashed thread. + pid_t crash_thread_; + mutable PageAllocator allocator_; - bool threads_suspended_; - wasteful_vector threads_; // the ids of all the threads - wasteful_vector mappings_; // info from /proc//maps + // IDs of all the threads. + wasteful_vector threads_; + + // Info from /proc//maps. + wasteful_vector mappings_; }; } // namespace google_breakpad diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc index 27c2e9945..418e7e67a 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -51,7 +51,14 @@ #endif void *thread_function(void *data) { + int pipefd = *static_cast(data); volatile pid_t thread_id = syscall(__NR_gettid); + // Signal parent that a thread has started. + uint8_t byte = 1; + if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { + perror("ERROR: parent notification failed"); + return NULL; + } register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; while (true) asm volatile ("" : : "r" (thread_id_ptr)); @@ -59,9 +66,9 @@ void *thread_function(void *data) { } int main(int argc, char *argv[]) { - if (argc < 2) { + if (argc < 3) { fprintf(stderr, - "usage: linux_dumper_unittest_helper <# of threads\n"); + "usage: linux_dumper_unittest_helper <# of threads>\n"); return 1; } int pipefd = atoi(argv[1]); @@ -75,11 +82,8 @@ int main(int argc, char *argv[]) { 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); + pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd); } - // Signal parent that this process has started all threads. - uint8_t byte = 1; - write(pipefd, &byte, sizeof(byte)); - thread_function(NULL); + thread_function(&pipefd); return 0; } diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc new file mode 100644 index 000000000..864bbad36 --- /dev/null +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper. +// See linux_ptrace_dumper.h for detals. +// This class was originally splitted from google_breakpad::LinuxDumper. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +// Suspends a thread by attaching to it. +static bool SuspendThread(pid_t pid) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (sys_waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } +#if defined(__i386) || defined(__x86_64) + // On x86, the stack pointer is NULL or -1, when executing trusted code in + // the seccomp sandbox. Not only does this cause difficulties down the line + // when trying to dump the thread's stack, it also results in the minidumps + // containing information about the trusted threads. This information is + // generally completely meaningless and just pollutes the minidumps. + // We thus test the stack pointer and exclude any threads that are part of + // the seccomp sandbox's trusted code. + user_regs_struct regs; + if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || +#if defined(__i386) + !regs.esp +#elif defined(__x86_64) + !regs.rsp +#endif + ) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } +#endif + return true; +} + +// Resumes a thread by detaching from it. +static bool ResumeThread(pid_t pid) { + return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +namespace google_breakpad { + +LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) + : LinuxDumper(pid), + threads_suspended_(false) { +} + +bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node || pid <= 0) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + const unsigned pid_len = my_int_len(pid); + const size_t total_length = 6 + pid_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + path[6 + pid_len] = '/'; + memcpy(path + 6 + pid_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + unsigned long tmp = 55; + size_t done = 0; + static const size_t word_size = sizeof(tmp); + uint8_t* const local = (uint8_t*) dest; + uint8_t* const remote = (uint8_t*) src; + + while (done < length) { + const size_t l = (length - done > word_size) ? word_size : (length - done); + if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { + tmp = 0; + } + memcpy(local + done, &tmp, l); + done += l; + } +} + +// Read thread info from /proc/$pid/status. +// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, +// these members are set to -1. Returns true iff all three members are +// available. +bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= threads_.size()) + return false; + + pid_t tid = threads_[index]; + + assert(info != NULL); + char status_path[NAME_MAX]; + if (!BuildProcPath(status_path, tid, "status")) + return false; + + const int fd = sys_open(status_path, O_RDONLY, 0); + if (fd < 0) + return false; + + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; + unsigned line_len; + + info->ppid = info->tgid = -1; + + while (line_reader->GetNextLine(&line, &line_len)) { + if (my_strncmp("Tgid:\t", line, 6) == 0) { + my_strtoui(&info->tgid, line + 6); + } else if (my_strncmp("PPid:\t", line, 6) == 0) { + my_strtoui(&info->ppid, line + 6); + } + + line_reader->PopLine(line_len); + } + sys_close(fd); + + if (info->ppid == -1 || info->tgid == -1) + return false; + + if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { + return false; + } + +#if !defined(__ANDROID__) + if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { + return false; + } +#endif + +#if defined(__i386) + if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) + return false; +#endif + +#if defined(__i386) || defined(__x86_64) + for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { + if (sys_ptrace( + PTRACE_PEEKUSER, tid, + reinterpret_cast (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), + &info->dregs[i]) == -1) { + return false; + } + } +#endif + + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + return GetStackInfo(&info->stack, &info->stack_len, + (uintptr_t) stack_pointer); +} + +bool LinuxPtraceDumper::IsPostMortem() const { + return false; +} + +bool LinuxPtraceDumper::ThreadsSuspend() { + if (threads_suspended_) + return true; + for (size_t i = 0; i < threads_.size(); ++i) { + if (!SuspendThread(threads_[i])) { + // If the thread either disappeared before we could attach to it, or if + // it was part of the seccomp sandbox's trusted code, it is OK to + // silently drop it from the minidump. + memmove(&threads_[i], &threads_[i+1], + (threads_.size() - i - 1) * sizeof(threads_[i])); + threads_.resize(threads_.size() - 1); + --i; + } + } + threads_suspended_ = true; + return threads_.size() > 0; +} + +bool LinuxPtraceDumper::ThreadsResume() { + if (!threads_suspended_) + return false; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= ResumeThread(threads_[i]); + threads_suspended_ = false; + return good; +} + +// Parse /proc/$pid/task to list all the threads of the process identified by +// pid. +bool LinuxPtraceDumper::EnumerateThreads() { + char task_path[NAME_MAX]; + if (!BuildProcPath(task_path, pid_, "task")) + return false; + + const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + return false; + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); + + // The directory may contain duplicate entries which we filter by assuming + // that they are consecutive. + int last_tid = -1; + const char* dent_name; + while (dir_reader->GetNextEntry(&dent_name)) { + if (my_strcmp(dent_name, ".") && + my_strcmp(dent_name, "..")) { + int tid = 0; + if (my_strtoui(&tid, dent_name) && + last_tid != tid) { + last_tid = tid; + threads_.push_back(tid); + } + } + dir_reader->PopEntry(); + } + + sys_close(fd); + return true; +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h new file mode 100644 index 000000000..1e9bcfdf8 --- /dev/null +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.h @@ -0,0 +1,92 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via ptrace. +// This class was originally splitted from google_breakpad::LinuxDumper. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" + +namespace google_breakpad { + +class LinuxPtraceDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid|. + explicit LinuxPtraceDumper(pid_t pid); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result. |node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method uses ptrace to extract + // the content from the target process. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns false to indicate this dumper performs a dump of + // a crashed process via ptrace. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // Suspends all threads in the given process. Returns true on success. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // Resumes all threads in the given process. Returns true on success. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Set to true if all threads of the crashed process are suspended. + bool threads_suspended_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_ diff --git a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest.cc b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc similarity index 76% rename from thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest.cc rename to thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc index 1eee9a185..ef47da9f9 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/linux_dumper_unittest.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -27,7 +27,11 @@ // (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 +// linux_ptrace_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxPtraceDumoer. +// +// This file was renamed from linux_dumper_unittest.cc and modified due +// to LinuxDumper being splitted into two classes. #include #include @@ -39,22 +43,26 @@ #include #include +#include + #include "breakpad_googletest_includes.h" -#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" #include "common/linux/eintr_wrapper.h" #include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" #include "common/memory.h" using std::string; using namespace google_breakpad; namespace { -typedef testing::Test LinuxDumperTest; + +typedef testing::Test LinuxPtraceDumperTest; 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) { + if (!SafeReadLink("/proc/self/exe", self_path)) { return ""; } string helper_path(self_path); @@ -68,14 +76,14 @@ string GetHelperBinary() { return helper_path; } +} // namespace + +TEST(LinuxPtraceDumperTest, Setup) { + LinuxPtraceDumper dumper(getpid()); } -TEST(LinuxDumperTest, Setup) { - LinuxDumper dumper(getpid()); -} - -TEST(LinuxDumperTest, FindMappings) { - LinuxDumper dumper(getpid()); +TEST(LinuxPtraceDumperTest, FindMappings) { + LinuxPtraceDumper dumper(getpid()); ASSERT_TRUE(dumper.Init()); ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); @@ -83,8 +91,8 @@ TEST(LinuxDumperTest, FindMappings) { ASSERT_FALSE(dumper.FindMapping(NULL)); } -TEST(LinuxDumperTest, ThreadList) { - LinuxDumper dumper(getpid()); +TEST(LinuxPtraceDumperTest, ThreadList) { + LinuxPtraceDumper dumper(getpid()); ASSERT_TRUE(dumper.Init()); ASSERT_GE(dumper.threads().size(), (size_t)1); @@ -100,7 +108,7 @@ TEST(LinuxDumperTest, ThreadList) { // Helper stack class to close a file descriptor and unmap // a mmap'ed mapping. class StackHelper { -public: + public: StackHelper(int fd, char* mapping, size_t size) : fd_(fd), mapping_(mapping), size_(size) {} ~StackHelper() { @@ -108,13 +116,13 @@ public: close(fd_); } -private: + private: int fd_; char* mapping_; size_t size_; }; -TEST(LinuxDumperTest, MergedMappings) { +TEST(LinuxPtraceDumperTest, MergedMappings) { string helper_path(GetHelperBinary()); if (helper_path.empty()) { FAIL() << "Couldn't find helper binary"; @@ -136,24 +144,25 @@ TEST(LinuxDumperTest, MergedMappings) { 0)); ASSERT_TRUE(mapping); - const u_int64_t kMappingAddress = reinterpret_cast(mapping); + const uintptr_t kMappingAddress = reinterpret_cast(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(mmap(mapping + 2 *kPageSize, - kPageSize, - PROT_NONE, - MAP_SHARED | MAP_FIXED, - fd, - // Map a different offset just to - // better test real-world conditions. - kPageSize)); + char* inside_mapping = reinterpret_cast( + 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()); + // Now check that LinuxPtraceDumper interpreted the mappings properly. + LinuxPtraceDumper dumper(getpid()); ASSERT_TRUE(dumper.Init()); int mapping_count = 0; for (unsigned i = 0; i < dumper.mappings().size(); ++i) { @@ -170,7 +179,7 @@ TEST(LinuxDumperTest, MergedMappings) { EXPECT_EQ(1, mapping_count); } -TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { +TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { static const int kNumberOfThreadsInHelperProgram = 5; char kNumberOfThreadsArgument[2]; sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); @@ -203,28 +212,36 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { 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)); + // Wait for all child threads to indicate that they have started + for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), sizeof(junk)); + } close(fds[0]); - // Child is ready now. - LinuxDumper dumper(child_pid); + // There is a race here because we may stop a child thread before + // it is actually running the busy loop. Empirically this sleep + // is sufficient to avoid the race. + usleep(100000); + + // Children are ready now. + LinuxPtraceDumper dumper(child_pid); ASSERT_TRUE(dumper.Init()); EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); EXPECT_TRUE(dumper.ThreadsSuspend()); ThreadInfo one_thread; - for(size_t i = 0; i < dumper.threads().size(); ++i) { - EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread)); + for (size_t i = 0; i < dumper.threads().size(); ++i) { + EXPECT_TRUE(dumper.GetThreadInfoByIndex(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__) @@ -243,42 +260,43 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { 4); EXPECT_EQ(dumper.threads()[i], one_thread_id); } + EXPECT_TRUE(dumper.ThreadsResume()); kill(child_pid, SIGKILL); + + // Reap child + int status; + ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGKILL, WTERMSIG(status)); } -TEST(LinuxDumperTest, BuildProcPath) { +TEST(LinuxPtraceDumperTest, BuildProcPath) { const pid_t pid = getpid(); - LinuxDumper dumper(pid); + LinuxPtraceDumper dumper(pid); - char maps_path[256] = "dummymappath"; - char maps_path_expected[256]; + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; snprintf(maps_path_expected, sizeof(maps_path_expected), "/proc/%d/maps", pid); - dumper.BuildProcPath(maps_path, pid, "maps"); - ASSERT_STREQ(maps_path, maps_path_expected); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); - // In release mode, we expect BuildProcPath to handle the invalid - // parameters correctly and fill map_path with an empty - // NULL-terminated string. -#ifdef NDEBUG - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, 0, "maps"); - EXPECT_STREQ(maps_path, ""); + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, getpid(), ""); - EXPECT_STREQ(maps_path, ""); - - snprintf(maps_path, sizeof(maps_path), "dummymappath"); - dumper.BuildProcPath(maps_path, getpid(), NULL); - EXPECT_STREQ(maps_path, ""); -#endif + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); } #if !defined(__ARM_EABI__) // Ensure that the linux-gate VDSO is included in the mapping list. -TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { - LinuxDumper dumper(getpid()); +TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) { + LinuxPtraceDumper dumper(getpid()); ASSERT_TRUE(dumper.Init()); void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); @@ -300,8 +318,8 @@ TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { } // Ensure that the linux-gate VDSO can generate a non-zeroed File ID. -TEST(LinuxDumperTest, LinuxGateMappingID) { - LinuxDumper dumper(getpid()); +TEST(LinuxPtraceDumperTest, LinuxGateMappingID) { + LinuxPtraceDumper dumper(getpid()); ASSERT_TRUE(dumper.Init()); bool found_linux_gate = false; @@ -328,7 +346,7 @@ TEST(LinuxDumperTest, LinuxGateMappingID) { // Ensure that the linux-gate VDSO can generate a non-zeroed File ID // from a child process. -TEST(LinuxDumperTest, LinuxGateMappingIDChild) { +TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) { int fds[2]; ASSERT_NE(-1, pipe(fds)); @@ -344,7 +362,7 @@ TEST(LinuxDumperTest, LinuxGateMappingIDChild) { } close(fds[0]); - LinuxDumper dumper(child); + LinuxPtraceDumper dumper(child); ASSERT_TRUE(dumper.Init()); bool found_linux_gate = false; @@ -374,14 +392,12 @@ TEST(LinuxDumperTest, LinuxGateMappingIDChild) { } #endif -TEST(LinuxDumperTest, FileIDsMatch) { +TEST(LinuxPtraceDumperTest, FileIDsMatch) { // Calculate the File ID of our binary using both // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping // and ensure that we get the same result from both. char exe_name[PATH_MAX]; - ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1); - ASSERT_NE(len, -1); - exe_name[len] = '\0'; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); int fds[2]; ASSERT_NE(-1, pipe(fds)); @@ -398,7 +414,7 @@ TEST(LinuxDumperTest, FileIDsMatch) { } close(fds[0]); - LinuxDumper dumper(child); + LinuxPtraceDumper dumper(child); ASSERT_TRUE(dumper.Init()); const wasteful_vector mappings = dumper.mappings(); bool found_exe = false; diff --git a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.cc b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.cc index 77ff7ea23..35a291db8 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.cc @@ -46,26 +46,24 @@ #include "client/linux/minidump_writer/minidump_writer.h" #include "client/minidump_file_writer-inl.h" +#include #include #include #if !defined(__ANDROID__) #include #endif #include -#include -#include #if !defined(__ANDROID__) #include #include #endif #include +#include #include #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" @@ -74,8 +72,11 @@ #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/linux_ptrace_dumper.h" #include "client/linux/minidump_writer/minidump_extension_linux.h" +#include "client/minidump_file_writer.h" #include "common/linux/linux_libc_support.h" +#include "google_breakpad/common/minidump_format.h" #include "third_party/lss/linux_syscall_support.h" // Minidump defines register structures which are different from the raw @@ -368,32 +369,30 @@ namespace google_breakpad { class MinidumpWriter { public: MinidumpWriter(const char* filename, - pid_t crashing_pid, const ExceptionHandler::CrashContext* context, - const MappingList& mappings) + const MappingList& mappings, + LinuxDumper* dumper) : filename_(filename), - siginfo_(&context->siginfo), - ucontext_(&context->context), + ucontext_(context ? &context->context : NULL), #if !defined(__ARM_EABI__) - float_state_(&context->float_state), + float_state_(context ? &context->float_state : NULL), #else // TODO: fix this after fixing ExceptionHandler float_state_(NULL), #endif - crashing_tid_(context->tid), - dumper_(crashing_pid), - memory_blocks_(dumper_.allocator()), + dumper_(dumper), + memory_blocks_(dumper_->allocator()), mapping_list_(mappings) { } bool Init() { - return dumper_.Init() && minidump_writer_.Open(filename_) && - dumper_.ThreadsSuspend(); + return dumper_->Init() && minidump_writer_.Open(filename_) && + dumper_->ThreadsSuspend(); } ~MinidumpWriter() { minidump_writer_.Close(); - dumper_.ThreadsResume(); + dumper_->ThreadsResume(); } bool Dump() { @@ -403,17 +402,25 @@ class MinidumpWriter { 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; + // This code assumes the crashing process is the same as this process and + // may hang or take a long time to complete if not so. + // Thus, we skip this code for a post-mortem based dump. + if (!dumper_->IsPostMortem()) { + // 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); + // NOTE: Use of _DYNAMIC assumes this is the same process as the + // crashing process. This loop will go forever if it's out of bounds. + dumper_->CopyFromProcess(&dyn, GetCrashThread(), _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 @@ -467,7 +474,7 @@ class MinidumpWriter { dir.CopyIndex(dir_index++, &dirent); dirent.stream_type = MD_LINUX_PROC_STATUS; - if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) + if (!WriteProcFile(&dirent.location, GetCrashThread(), "status")) NullifyDirectoryEntry(&dirent); dir.CopyIndex(dir_index++, &dirent); @@ -477,22 +484,22 @@ class MinidumpWriter { dir.CopyIndex(dir_index++, &dirent); dirent.stream_type = MD_LINUX_CMD_LINE; - if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) + if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline")) NullifyDirectoryEntry(&dirent); dir.CopyIndex(dir_index++, &dirent); dirent.stream_type = MD_LINUX_ENVIRON; - if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) + if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ")) NullifyDirectoryEntry(&dirent); dir.CopyIndex(dir_index++, &dirent); dirent.stream_type = MD_LINUX_AUXV; - if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) + if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv")) NullifyDirectoryEntry(&dirent); dir.CopyIndex(dir_index++, &dirent); dirent.stream_type = MD_LINUX_MAPS; - if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) + if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps")) NullifyDirectoryEntry(&dirent); dir.CopyIndex(dir_index++, &dirent); @@ -506,7 +513,7 @@ class MinidumpWriter { // If you add more directory entries, don't forget to update kNumWriters, // above. - dumper_.ThreadsResume(); + dumper_->ThreadsResume(); return true; } @@ -629,7 +636,7 @@ class MinidumpWriter { // Write information about the threads. bool WriteThreadListStream(MDRawDirectory* dirent) { - const unsigned num_threads = dumper_.threads().size(); + const unsigned num_threads = dumper_->threads().size(); TypedMDRVA list(&minidump_writer_); if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) @@ -643,21 +650,23 @@ class MinidumpWriter { for (unsigned i = 0; i < num_threads; ++i) { MDRawThread thread; my_memset(&thread, 0, sizeof(thread)); - thread.thread_id = dumper_.threads()[i]; + thread.thread_id = dumper_->threads()[i]; // We have a different source of information for the crashing thread. If // 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 (static_cast(thread.thread_id) == GetCrashThread() && + !dumper_->IsPostMortem()) { const void* stack; size_t stack_len; - if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) + if (!dumper_->GetStackInfo(&stack, &stack_len, GetStackPointer())) return false; UntypedMDRVA memory(&minidump_writer_); if (!memory.Allocate(stack_len)) return false; - uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); - dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); + uint8_t* stack_copy = reinterpret_cast(Alloc(stack_len)); + dumper_->CopyFromProcess(stack_copy, thread.thread_id, stack, + stack_len); memory.Copy(stack_copy, stack_len); thread.stack.start_of_memory_range = (uintptr_t) (stack); thread.stack.memory = memory.location(); @@ -671,8 +680,8 @@ class MinidumpWriter { // don't bother trying to write it. bool ip_is_mapped = false; MDMemoryDescriptor ip_memory_d; - for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { - const MappingInfo& mapping = *dumper_.mappings()[i]; + 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; @@ -695,12 +704,12 @@ class MinidumpWriter { 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(ip_memory_d.start_of_memory_range), - ip_memory_d.memory.data_size); + reinterpret_cast(Alloc(ip_memory_d.memory.data_size)); + dumper_->CopyFromProcess( + memory_copy, + thread.thread_id, + reinterpret_cast(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); @@ -716,15 +725,14 @@ class MinidumpWriter { crashing_thread_context_ = cpu.location(); } else { ThreadInfo info; - if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) + if (!dumper_->GetThreadInfoByIndex(i, &info)) return false; UntypedMDRVA memory(&minidump_writer_); if (!memory.Allocate(info.stack_len)) return false; - uint8_t* stack_copy = - (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); - dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, - info.stack_len); + uint8_t* stack_copy = reinterpret_cast(Alloc(info.stack_len)); + dumper_->CopyFromProcess(stack_copy, thread.thread_id, info.stack, + info.stack_len); memory.Copy(stack_copy, info.stack_len); thread.stack.start_of_memory_range = (uintptr_t)(info.stack); thread.stack.memory = memory.location(); @@ -737,6 +745,10 @@ class MinidumpWriter { CPUFillFromThreadInfo(cpu.get(), info); PopSeccompStackFrame(cpu.get(), thread, stack_copy); thread.thread_context = cpu.location(); + if (dumper_->threads()[i] == GetCrashThread()) { + assert(dumper_->IsPostMortem()); + crashing_thread_context_ = cpu.location(); + } } list.CopyIndexAfterObject(i, &thread, sizeof(thread)); @@ -777,11 +789,11 @@ class MinidumpWriter { // 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(); + const unsigned num_mappings = dumper_->mappings().size(); unsigned num_output_mappings = mapping_list_.size(); - for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { - const MappingInfo& mapping = *dumper_.mappings()[i]; + for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping)) num_output_mappings++; } @@ -797,7 +809,7 @@ class MinidumpWriter { // First write all the mappings from the dumper unsigned int j = 0; for (unsigned i = 0; i < num_mappings; ++i) { - const MappingInfo& mapping = *dumper_.mappings()[i]; + const MappingInfo& mapping = *dumper_->mappings()[i]; if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping)) continue; @@ -859,8 +871,8 @@ class MinidumpWriter { // GUID was provided by caller. memcpy(signature, identifier, sizeof(MDGUID)); } else { - dumper_.ElfFileIdentifierForMapping(mapping, member, - mapping_id, signature); + 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); @@ -905,10 +917,9 @@ class MinidumpWriter { dirent->stream_type = MD_EXCEPTION_STREAM; dirent->location = exc.location(); - exc.get()->thread_id = crashing_tid_; - exc.get()->exception_record.exception_code = siginfo_->si_signo; - exc.get()->exception_record.exception_address = - (uintptr_t) siginfo_->si_addr; + exc.get()->thread_id = GetCrashThread(); + exc.get()->exception_record.exception_code = dumper_->crash_signal(); + exc.get()->exception_record.exception_address = dumper_->crash_address(); exc.get()->thread_context = crashing_thread_context_; return true; @@ -945,11 +956,11 @@ class MinidumpWriter { // 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)); + dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), 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)); + dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)); ptr = map.l_next; dso_count++; } @@ -967,12 +978,12 @@ class MinidumpWriter { // 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)); + dumper_->CopyFromProcess(&map, GetCrashThread(), 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); + dumper_->CopyFromProcess(filename, GetCrashThread(), map.l_name, + sizeof(filename) - 1); } MDLocationDescriptor location; if (!minidump_writer_.WriteString(filename, 0, &location)) @@ -1001,8 +1012,8 @@ class MinidumpWriter { debug.get()->dynamic = (void*)&_DYNAMIC; char *dso_debug_data = new char[dynamic_length]; - dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC, - dynamic_length); + dumper_->CopyFromProcess(dso_debug_data, GetCrashThread(), &_DYNAMIC, + dynamic_length); debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length); delete[] dso_debug_data; @@ -1011,6 +1022,14 @@ class MinidumpWriter { } private: + void* Alloc(unsigned bytes) { + return dumper_->allocator()->Alloc(bytes); + } + + pid_t GetCrashThread() const { + return dumper_->crash_thread(); + } + #if defined(__i386) uintptr_t GetStackPointer() { return ucontext_->uc_mcontext.gregs[REG_ESP]; @@ -1175,16 +1194,15 @@ class MinidumpWriter { // to read as much as we can into a buffer. static const unsigned kBufSize = 1024 - 2*sizeof(void*); struct Buffers { - struct Buffers* next; + Buffers* next; size_t len; uint8_t data[kBufSize]; - } *buffers = - (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers)); + } *buffers = reinterpret_cast(Alloc(sizeof(Buffers))); buffers->next = NULL; buffers->len = 0; size_t total = 0; - for (struct Buffers* bufptr = buffers;;) { + for (Buffers* bufptr = buffers;;) { ssize_t r; do { r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); @@ -1196,8 +1214,7 @@ class MinidumpWriter { total += r; bufptr->len += r; if (bufptr->len == kBufSize) { - bufptr->next = - (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers)); + bufptr->next = reinterpret_cast(Alloc(sizeof(Buffers))); bufptr = bufptr->next; bufptr->next = NULL; bufptr->len = 0; @@ -1277,16 +1294,15 @@ class MinidumpWriter { bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, const char* filename) { char buf[NAME_MAX]; - dumper_.BuildProcPath(buf, pid, filename); + if (!dumper_->BuildProcPath(buf, pid, filename)) + return false; return WriteFile(result, buf); } const char* const filename_; // output filename - const siginfo_t* const siginfo_; // from the signal handler (see sigaction) const struct ucontext* const ucontext_; // also from the signal handler const struct _libc_fpstate* const float_state_; // ditto - const pid_t crashing_tid_; // the process which actually crashed - LinuxDumper dumper_; + LinuxDumper* dumper_; MinidumpFileWriter minidump_writer_; MDLocationDescriptor crashing_thread_context_; // Blocks of memory written to the dump. These are all currently @@ -1310,7 +1326,21 @@ bool WriteMinidump(const char* filename, pid_t crashing_process, return false; const ExceptionHandler::CrashContext* context = reinterpret_cast(blob); - MinidumpWriter writer(filename, crashing_process, context, mappings); + LinuxPtraceDumper dumper(crashing_process); + dumper.set_crash_address( + reinterpret_cast(context->siginfo.si_addr)); + dumper.set_crash_signal(context->siginfo.si_signo); + dumper.set_crash_thread(context->tid); + MinidumpWriter writer(filename, context, mappings, &dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +bool WriteMinidump(const char* filename, + const MappingList& mappings, + LinuxDumper* dumper) { + MinidumpWriter writer(filename, NULL, mappings, dumper); if (!writer.Init()) return false; return writer.Dump(); diff --git a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.h b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.h index 156ca35fa..e79eb79be 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.h +++ b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer.h @@ -36,12 +36,19 @@ #include #include +#include "client/linux/minidump_writer/linux_dumper.h" #include "google_breakpad/common/minidump_format.h" namespace google_breakpad { +class ExceptionHandler; + +struct MappingEntry { + MappingInfo first; + u_int8_t second[sizeof(MDGUID)]; +}; + // A list of -typedef std::pair MappingEntry; typedef std::list MappingList; // Write a minidump to the filesystem. This function does not malloc nor use @@ -62,6 +69,10 @@ bool WriteMinidump(const char* filename, pid_t crashing_process, const void* blob, size_t blob_size, const MappingList& mappings); +bool WriteMinidump(const char* filename, + const MappingList& mappings, + LinuxDumper* dumper); + } // namespace google_breakpad #endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff --git a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc index 5f6bb34ae..91aafed70 100644 --- a/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -42,16 +42,12 @@ #include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/eintr_wrapper.h" #include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" +#include "common/tests/auto_tempdir.h" #include "google_breakpad/processor/minidump.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; @@ -77,15 +73,14 @@ TEST(MinidumpWriterTest, Setup) { ExceptionHandler::CrashContext context; memset(&context, 0, sizeof(context)); - char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; - mktemp(templ); + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; // Set a non-zero tid to avoid tripping asserts. context.tid = 1; - ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context))); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); struct stat st; - ASSERT_EQ(stat(templ, &st), 0); + ASSERT_EQ(stat(templ.c_str(), &st), 0); ASSERT_GT(st.st_size, 0u); - unlink(templ); close(fds[1]); } @@ -126,7 +121,7 @@ TEST(MinidumpWriterTest, MappingInfo) { MAP_PRIVATE | MAP_ANON, -1, 0)); - const u_int64_t kMemoryAddress = reinterpret_cast(memory); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); ASSERT_TRUE(memory); const pid_t child = fork(); @@ -143,8 +138,8 @@ TEST(MinidumpWriterTest, MappingInfo) { memset(&context, 0, sizeof(context)); context.tid = 1; - char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; - mktemp(templ); + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; // Add information about the mapped memory. MappingInfo info; @@ -158,12 +153,13 @@ TEST(MinidumpWriterTest, MappingInfo) { mapping.first = info; memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); mappings.push_back(mapping); - ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context), mappings)); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + mappings)); // Read the minidump. Load the module list, and ensure that // the mmap'ed |memory| is listed with the given module name // and debug ID. - Minidump minidump(templ); + Minidump minidump(templ.c_str()); ASSERT_TRUE(minidump.Read()); MinidumpModuleList* module_list = minidump.GetModuleList(); @@ -177,7 +173,6 @@ TEST(MinidumpWriterTest, MappingInfo) { EXPECT_EQ(kMemoryName, module->code_file()); EXPECT_EQ(module_identifier, module->debug_identifier()); - unlink(templ); close(fds[1]); } @@ -211,11 +206,11 @@ TEST(MinidumpWriterTest, MappingInfoContained) { 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); + AutoTempDir temp_dir; + std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; + int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); ASSERT_NE(-1, fd); - unlink(tempfile); + unlink(tempfile.c_str()); // fill with zeros char buffer[kMemorySize]; memset(buffer, 0, kMemorySize); @@ -229,7 +224,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) { MAP_PRIVATE, fd, 0)); - const u_int64_t kMemoryAddress = reinterpret_cast(memory); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); ASSERT_TRUE(memory); close(fd); @@ -247,8 +242,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) { memset(&context, 0, sizeof(context)); context.tid = 1; - char dumpfile[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; - mktemp(dumpfile); + std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest"; // Add information about the mapped memory. Report it as being larger than // it actually is. @@ -264,12 +258,13 @@ TEST(MinidumpWriterTest, MappingInfoContained) { memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); mappings.push_back(mapping); ASSERT_TRUE( - WriteMinidump(dumpfile, child, &context, sizeof(context), mappings)); + WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), + mappings)); // Read the minidump. Load the module list, and ensure that // the mmap'ed |memory| is listed with the given module name // and debug ID. - Minidump minidump(dumpfile); + Minidump minidump(dumpfile.c_str()); ASSERT_TRUE(minidump.Read()); MinidumpModuleList* module_list = minidump.GetModuleList(); @@ -283,7 +278,6 @@ TEST(MinidumpWriterTest, MappingInfoContained) { EXPECT_EQ(kMemoryName, module->code_file()); EXPECT_EQ(module_identifier, module->debug_identifier()); - unlink(dumpfile); close(fds[1]); } @@ -294,8 +288,8 @@ TEST(MinidumpWriterTest, DeletedBinary) { // 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); + if (!SafeReadLink("/proc/self/exe", self_path)) { + FAIL() << "readlink failed"; exit(1); } string helper_path(self_path); @@ -308,12 +302,13 @@ TEST(MinidumpWriterTest, DeletedBinary) { helper_path += "linux_dumper_unittest_helper"; // Copy binary to a temp file. - char binpath[] = TEMPDIR "/linux-dumper-unittest-helper-XXXXXX"; - mktemp(binpath); + AutoTempDir temp_dir; + std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; char cmdline[2 * PATH_MAX]; - sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(), binpath); + sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(), + binpath.c_str()); ASSERT_EQ(0, system(cmdline)); - ASSERT_EQ(0, chmod(binpath, 0755)); + ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); int fds[2]; ASSERT_NE(-1, pipe(fds)); @@ -326,8 +321,8 @@ TEST(MinidumpWriterTest, DeletedBinary) { // 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, + execl(binpath.c_str(), + binpath.c_str(), pipe_fd_string, kNumberOfThreadsArgument, NULL); @@ -343,37 +338,38 @@ TEST(MinidumpWriterTest, DeletedBinary) { ASSERT_EQ(1, r); ASSERT_TRUE(pfd.revents & POLLIN); uint8_t junk; - read(fds[0], &junk, sizeof(junk)); + const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); + ASSERT_EQ(sizeof(junk), nr); close(fds[0]); // Child is ready now. // Unlink the test binary. - unlink(binpath); + unlink(binpath.c_str()); ExceptionHandler::CrashContext context; memset(&context, 0, sizeof(context)); - char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; - mktemp(templ); + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; // Set a non-zero tid to avoid tripping asserts. context.tid = 1; - ASSERT_TRUE(WriteMinidump(templ, child_pid, &context, sizeof(context))); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, + sizeof(context))); kill(child_pid, SIGKILL); struct stat st; - ASSERT_EQ(stat(templ, &st), 0); + ASSERT_EQ(stat(templ.c_str(), &st), 0); ASSERT_GT(st.st_size, 0u); - Minidump minidump(templ); + Minidump minidump(templ.c_str()); ASSERT_TRUE(minidump.Read()); // Check that the main module filename is correct. MinidumpModuleList* module_list = minidump.GetModuleList(); ASSERT_TRUE(module_list); const MinidumpModule* module = module_list->GetMainModule(); - EXPECT_STREQ(binpath, module->code_file().c_str()); + EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); // Check that the file ID is correct. FileID fileid(helper_path.c_str()); uint8_t identifier[sizeof(MDGUID)]; @@ -391,6 +387,4 @@ TEST(MinidumpWriterTest, DeletedBinary) { // which is always zero on Linux. module_identifier += "0"; EXPECT_EQ(module_identifier, module->debug_identifier()); - - unlink(templ); } diff --git a/thirdparty/breakpad/client/linux/sender/google_crash_report_sender.cc b/thirdparty/breakpad/client/linux/sender/google_crash_report_sender.cc index c1641b4fb..be897adb3 100644 --- a/thirdparty/breakpad/client/linux/sender/google_crash_report_sender.cc +++ b/thirdparty/breakpad/client/linux/sender/google_crash_report_sender.cc @@ -34,7 +34,7 @@ using std::string; -DEFINE_string(crash_server, "http://clients2.google.com/cr", +DEFINE_string(crash_server, "https://clients2.google.com/cr", "The crash server to upload minidumps to."); DEFINE_string(product_name, "", "The product name that the minidump corresponds to."); diff --git a/thirdparty/breakpad/client/mac/Breakpad.xcodeproj/project.pbxproj b/thirdparty/breakpad/client/mac/Breakpad.xcodeproj/project.pbxproj index e60fe71ac..3c364c375 100644 --- a/thirdparty/breakpad/client/mac/Breakpad.xcodeproj/project.pbxproj +++ b/thirdparty/breakpad/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -35,14 +35,25 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 163201D41443019E00C4DBF5 /* ConfigFile.h */; }; + 163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; }; + 163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; }; + 16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C917147D45AE00776EAD /* BreakpadDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 16E02DB8147410F0008C604D /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16E02DB4147410D4008C604D /* uploader.mm */; }; 3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; }; 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; }; 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; }; - 4D72CA0E13DFAD5C006CABE3 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.c */; }; - 4D72CA2513DFAE1C006CABE3 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.c */; }; - 4D72CA2F13DFAE65006CABE3 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.c */; }; - 4D72CA3813DFAE91006CABE3 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.c */; }; - 4D72CA3913DFAE92006CABE3 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.c */; }; + 4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; 4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; 4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; 4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; @@ -544,11 +555,18 @@ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 163201D41443019E00C4DBF5 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigFile.h; path = crash_generation/ConfigFile.h; sourceTree = ""; }; + 163201D51443019E00C4DBF5 /* ConfigFile.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ConfigFile.mm; path = crash_generation/ConfigFile.mm; sourceTree = ""; }; + 163202431443201300C4DBF5 /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uploader.h; path = sender/uploader.h; sourceTree = ""; }; + 16C7C917147D45AE00776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = ""; }; + 16E02DB4147410D4008C604D /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = uploader.mm; path = sender/uploader.mm; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = ""; }; 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Breakpad.xib; path = sender/Breakpad.xib; sourceTree = ""; }; 33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = ""; }; 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = ""; }; - 4D72CA0D13DFAD5C006CABE3 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../common/md5.c; sourceTree = SOURCE_ROOT; }; + 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bootstrap_compat.cc; path = ../../common/mac/bootstrap_compat.cc; sourceTree = SOURCE_ROOT; }; + 4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap_compat.h; path = ../../common/mac/bootstrap_compat.h; sourceTree = SOURCE_ROOT; }; + 4D72CA0D13DFAD5C006CABE3 /* md5.cc */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = md5.cc; path = ../../common/md5.cc; sourceTree = SOURCE_ROOT; }; 4DBE4769134A4F080072546A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; 8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = ../../common/mac/GTMDefines.h; sourceTree = SOURCE_ROOT; }; 8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -577,28 +595,28 @@ D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server.cc; path = crash_generation/crash_generation_server.cc; sourceTree = ""; }; D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server_test.cc; path = tests/crash_generation_server_test.cc; sourceTree = ""; }; D2F9A546121383A1002747C1 /* crash_generation_server_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_generation_server_test; sourceTree = BUILT_PRODUCTS_DIR; }; - DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = ""; }; - DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = ""; }; - DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = ""; }; - DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/Localizable.strings; sourceTree = ""; }; - DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/Localizable.strings; sourceTree = ""; }; - DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/Localizable.strings; sourceTree = ""; }; - DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/Localizable.strings; sourceTree = ""; }; - DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/Localizable.strings; sourceTree = ""; }; - DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/Localizable.strings; sourceTree = ""; }; - DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/Localizable.strings; sourceTree = ""; }; - DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/Localizable.strings; sourceTree = ""; }; - DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/InfoPlist.strings; sourceTree = ""; }; - DE43469011C72976004F095F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = ""; }; + DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = ""; }; + DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = ""; }; + DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/Localizable.strings; sourceTree = ""; }; + DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/Localizable.strings; sourceTree = ""; }; + DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/Localizable.strings; sourceTree = ""; }; + DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/Localizable.strings; sourceTree = ""; }; + DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/Localizable.strings; sourceTree = ""; }; + DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/Localizable.strings; sourceTree = ""; }; + DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/Localizable.strings; sourceTree = ""; }; + DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/Localizable.strings; sourceTree = ""; }; + DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43469011C72976004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/InfoPlist.strings; sourceTree = ""; }; F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BreakpadFramework_Test.mm; path = tests/BreakpadFramework_Test.mm; sourceTree = ""; }; F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = ""; }; F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = ""; }; @@ -666,7 +684,7 @@ F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = ""; }; F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = ""; }; - F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = ""; }; + F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = ""; }; F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = testapp/English.lproj/MainMenu.xib; sourceTree = ""; }; F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = ""; }; F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = ""; }; @@ -848,6 +866,23 @@ name = Frameworks; sourceTree = ""; }; + 16C7C915147D45AE00776EAD /* apple */ = { + isa = PBXGroup; + children = ( + 16C7C916147D45AE00776EAD /* Framework */, + ); + name = apple; + path = ../apple; + sourceTree = SOURCE_ROOT; + }; + 16C7C916147D45AE00776EAD /* Framework */ = { + isa = PBXGroup; + children = ( + 16C7C917147D45AE00776EAD /* BreakpadDefines.h */, + ); + path = Framework; + sourceTree = ""; + }; D244536912426EE7009BBCE0 /* processor */ = { isa = PBXGroup; children = ( @@ -894,7 +929,7 @@ D244540A12439BA0009BBCE0 /* memory_unittest.cc */, F92C53870ECCE6C0009BE4BA /* convert_UTF.c */, F92C53880ECCE6C0009BE4BA /* convert_UTF.h */, - 4D72CA0D13DFAD5C006CABE3 /* md5.c */, + 4D72CA0D13DFAD5C006CABE3 /* md5.cc */, F92C53850ECCE6AD009BE4BA /* string_conversion.cc */, F92C53860ECCE6AD009BE4BA /* string_conversion.h */, F92C53840ECCE68D009BE4BA /* mac */, @@ -916,6 +951,8 @@ F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */, F92C53780ECCE635009BE4BA /* MachIPC.h */, F92C53790ECCE635009BE4BA /* MachIPC.mm */, + 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */, + 4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */, F92C537A0ECCE635009BE4BA /* macho_id.cc */, F92C537B0ECCE635009BE4BA /* macho_id.h */, F92C537C0ECCE635009BE4BA /* macho_utilities.cc */, @@ -933,6 +970,7 @@ F92C538D0ECCE6F2009BE4BA /* client */ = { isa = PBXGroup; children = ( + 16C7C915147D45AE00776EAD /* apple */, F92C53990ECCE78E009BE4BA /* mac */, F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */, F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */, @@ -958,6 +996,8 @@ F92C53B50ECCE799009BE4BA /* crash_generation */ = { isa = PBXGroup; children = ( + 163201D41443019E00C4DBF5 /* ConfigFile.h */, + 163201D51443019E00C4DBF5 /* ConfigFile.mm */, D2F9A4C4121336C7002747C1 /* client_info.h */, D2F9A4C5121336C7002747C1 /* crash_generation_client.h */, D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */, @@ -985,6 +1025,8 @@ F92C56A60ECE04B6009BE4BA /* sender */ = { isa = PBXGroup; children = ( + 16E02DB4147410D4008C604D /* uploader.mm */, + 163202431443201300C4DBF5 /* uploader.h */, F9B6309F100FF96B00D0F4AC /* goArrow.png */, F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, @@ -1098,6 +1140,8 @@ D2F9A4C9121336C7002747C1 /* client_info.h in Headers */, D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */, D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */, + 163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */, + 16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1621,6 +1665,7 @@ F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */, D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */, D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */, + 163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1643,7 +1688,8 @@ D246419612BAA55A005170D0 /* macho_id.cc in Sources */, D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */, D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */, - 4D72CA2513DFAE1C006CABE3 /* md5.c in Sources */, + 4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */, + 4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1681,7 +1727,8 @@ D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */, D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */, D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */, - 4D72CA3913DFAE92006CABE3 /* md5.c in Sources */, + 4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */, + 4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1691,6 +1738,8 @@ files = ( F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */, F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */, + 163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */, + 4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1706,12 +1755,13 @@ F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */, F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */, F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */, - 4D72CA0E13DFAD5C006CABE3 /* md5.c in Sources */, + 4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */, F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */, F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */, F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */, F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */, F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */, + 4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1722,6 +1772,7 @@ F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */, F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */, F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */, + 16E02DB8147410F0008C604D /* uploader.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1749,7 +1800,8 @@ F93803D60F8083B7004D428B /* macho_walker.cc in Sources */, F93803D70F8083B7004D428B /* string_utilities.cc in Sources */, D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */, - 4D72CA2F13DFAE65006CABE3 /* md5.c in Sources */, + 4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */, + 4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1789,7 +1841,8 @@ F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */, D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */, D244540B12439BA0009BBCE0 /* memory_unittest.cc in Sources */, - 4D72CA3813DFAE91006CABE3 /* md5.c in Sources */, + 4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */, + 4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2055,9 +2108,6 @@ buildSettings = { GCC_TREAT_WARNINGS_AS_ERRORS = NO; SDKROOT = macosx10.5; - "SDKROOT[arch=i386]" = macosx10.4; - "SDKROOT[arch=ppc]" = macosx10.4; - "SDKROOT[arch=x86_64]" = macosx10.6; }; name = Debug; }; @@ -2091,6 +2141,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ../..; INSTALL_PATH = /usr/local/bin; PREBINDING = NO; PRODUCT_NAME = minidump_generator_test_helper; @@ -2105,6 +2156,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ../..; INSTALL_PATH = /usr/local/bin; PREBINDING = NO; PRODUCT_NAME = minidump_generator_test_helper; @@ -2120,7 +2172,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; HEADER_SEARCH_PATHS = ( - $inherited, + "$(inherited)", ../../testing, ../../testing/include, ../../testing/gtest, @@ -2135,6 +2187,13 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); PREBINDING = NO; PRODUCT_NAME = gtest; }; @@ -2147,6 +2206,13 @@ COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_ENABLE_FIX_AND_CONTINUE = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); PREBINDING = NO; PRODUCT_NAME = gtest; ZERO_LINK = NO; @@ -2178,24 +2244,36 @@ D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\\\"$(SRCROOT)/build/Debug\\\"", ); - PRODUCT_NAME = handler_test; + PRODUCT_NAME = crash_generation_server_test; }; name = "Debug With Code Coverage"; }; D2F9A545121383A1002747C1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\\\"$(SRCROOT)/build/Debug\\\"", ); - PRODUCT_NAME = handler_test; + PRODUCT_NAME = crash_generation_server_test; }; name = Release; }; @@ -2282,7 +2360,14 @@ F93803C10F808210004D428B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../.., + ../../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); PRODUCT_NAME = generator_test; }; name = Release; @@ -2329,7 +2414,14 @@ F93DE32F0F82C55700608B94 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../../.., + ../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/build/Debug\"", @@ -2428,7 +2520,14 @@ F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../.., + ../../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); PRODUCT_NAME = generator_test; }; name = "Debug With Code Coverage"; @@ -2444,7 +2543,14 @@ F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = { isa = XCBuildConfiguration; buildSettings = { - HEADER_SEARCH_PATHS = ../..; + HEADER_SEARCH_PATHS = ( + ../../.., + ../.., + ../../testing, + ../../testing/include, + ../../testing/gtest, + ../../testing/gtest/include, + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)/build/Debug\"", diff --git a/thirdparty/breakpad/client/mac/Framework/Breakpad.h b/thirdparty/breakpad/client/mac/Framework/Breakpad.h index fa8614315..dbc620ebf 100644 --- a/thirdparty/breakpad/client/mac/Framework/Breakpad.h +++ b/thirdparty/breakpad/client/mac/Framework/Breakpad.h @@ -41,6 +41,10 @@ // modify the default behavior to suit your needs and wants and // desires. +// A service name associated with the original bootstrap parent port, saved in +// OnDemandServer and restored in Inspector. +#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent" + typedef void *BreakpadRef; #ifdef __cplusplus @@ -50,49 +54,7 @@ extern "C" { #include #include -// 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// -#define kDefaultLibrarySubdirectory "Breakpad" - -// Specify some special keys to be used in the configuration file that is -// generated by Breakpad and consumed by the crash_sender. -#define BREAKPAD_PRODUCT "BreakpadProduct" -#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" -#define BREAKPAD_VERSION "BreakpadVersion" -#define BREAKPAD_VENDOR "BreakpadVendor" -#define BREAKPAD_URL "BreakpadURL" -#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" -#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" -#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout" -#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" -#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation" -#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" -#define BREAKPAD_REPORTER_EXE_LOCATION \ - "BreakpadReporterExeLocation" -#define BREAKPAD_LOGFILES "BreakpadLogFiles" -#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" -#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" -#define BREAKPAD_COMMENTS "BreakpadComments" -#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail" -#define BREAKPAD_EMAIL "BreakpadEmail" -#define BREAKPAD_SERVER_TYPE "BreakpadServerType" -#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters" - -// The keys below are NOT user supplied, and are used internally. -#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime" -#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime" -#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" -#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" -#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" -#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" +#include "BreakpadDefines.h" // Optional user-defined function to dec to decide if we should handle // this crash or forward it along. @@ -224,13 +186,15 @@ typedef bool (*BreakpadFilterCallback)(int exception_type, // completeness. They are calculated by Breakpad during initialization & // crash-dump generation, or entered in by the user. // -// BREAKPAD_PROCESS_START_TIME The time the process started. +// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the +// process started // -// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed. +// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the +// process crashed. // -// BREAKPAD_PROCESS_UP_TIME The total time the process has been -// running. This parameter is not set -// until the crash-dump-generation phase. +// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process +// has been running. This parameter is not +// set until the crash-dump-generation phase. // // BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the // parameter dictionary correspond to log diff --git a/thirdparty/breakpad/client/mac/Framework/Breakpad.mm b/thirdparty/breakpad/client/mac/Framework/Breakpad.mm index 12b5f283c..2817f3809 100644 --- a/thirdparty/breakpad/client/mac/Framework/Breakpad.mm +++ b/thirdparty/breakpad/client/mac/Framework/Breakpad.mm @@ -39,20 +39,19 @@ #define DEBUGLOG if (gDebugLog) fprintf #define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" -#import "common/mac/MachIPC.h" -#import "common/mac/SimpleStringDictionary.h" +#import "client/mac/Framework/Breakpad.h" + +#import +#import +#import #import "client/mac/crash_generation/Inspector.h" #import "client/mac/handler/exception_handler.h" #import "client/mac/Framework/Breakpad.h" #import "client/mac/Framework/OnDemandServer.h" #import "client/mac/handler/protected_memory_allocator.h" - -#import -#import - -#import - +#import "common/mac/MachIPC.h" +#import "common/mac/SimpleStringDictionary.h" using google_breakpad::KeyValueEntry; using google_breakpad::MachPortSender; diff --git a/thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm b/thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm index 386854602..4e779c89a 100644 --- a/thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm +++ b/thirdparty/breakpad/client/mac/Framework/OnDemandServer.mm @@ -29,11 +29,23 @@ #import "OnDemandServer.h" +#import "Breakpad.h" +#include "common/mac/bootstrap_compat.h" + #if DEBUG #define PRINT_MACH_RESULT(result_, message_) \ printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); +#if defined(MAC_OS_X_VERSION_10_5) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ + printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ ); +#else + #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ + PRINT_MACH_RESULT(result_, message_) +#endif #else #define PRINT_MACH_RESULT(result_, message_) + #define PRINT_BOOTSTRAP_RESULT(result_, message_) #endif //============================================================================== @@ -67,15 +79,45 @@ kern_return_t OnDemandServer::Initialize(const char *server_command, bool unregister_on_cleanup) { unregister_on_cleanup_ = unregister_on_cleanup; - kern_return_t kr = - bootstrap_create_server(bootstrap_port, - const_cast(server_command), - geteuid(), // server uid - true, - &server_port_); + mach_port_t self_task = mach_task_self(); + mach_port_t bootstrap_port; + kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port); if (kr != KERN_SUCCESS) { - PRINT_MACH_RESULT(kr, "bootstrap_create_server() : "); + PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): "); + return kr; + } + + mach_port_t bootstrap_subset_port; + kr = bootstrap_subset(bootstrap_port, self_task, &bootstrap_subset_port); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): "); + return kr; + } + + // The inspector will be invoked with its bootstrap port set to the subset, + // but the sender will need access to the original bootstrap port. Although + // the original port is the subset's parent, bootstrap_parent can't be used + // because it requires extra privileges. Stash the original bootstrap port + // in the subset by registering it under a known name. The inspector will + // recover this port and set it as its own bootstrap port in Inspector.mm + // Inspector::ResetBootstrapPort. + kr = breakpad::BootstrapRegister( + bootstrap_subset_port, + const_cast(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(server_command), + geteuid(), // server uid + true, + &server_port_); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_server(): "); return kr; } @@ -86,15 +128,14 @@ kern_return_t OnDemandServer::Initialize(const char *server_command, kr = bootstrap_create_service(server_port_, const_cast(service_name), &service_port_); - - if (kr != KERN_SUCCESS) { - PRINT_MACH_RESULT(kr, "bootstrap_create_service() : "); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): "); // perhaps the service has already been created - try to look it up kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_); - if (kr != KERN_SUCCESS) { - PRINT_MACH_RESULT(kr, "bootstrap_look_up() : "); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): "); Unregister(); // clean up server port return kr; } @@ -131,9 +172,9 @@ void OnDemandServer::Unregister() { if (server_port_ != MACH_PORT_NULL) { // unregister the service - kern_return_t kr = bootstrap_register(server_port_, - service_name_, - MACH_PORT_NULL); + kern_return_t kr = breakpad::BootstrapRegister(server_port_, + service_name_, + MACH_PORT_NULL); if (kr != KERN_SUCCESS) { PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); diff --git a/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.h b/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.h new file mode 100644 index 000000000..f05753393 --- /dev/null +++ b/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.h @@ -0,0 +1,83 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Utility class that can persist a SimpleStringDictionary to disk. + +#import + +#import "common/mac/SimpleStringDictionary.h" + +namespace google_breakpad { + +BOOL EnsureDirectoryPathExists(NSString *dirPath); + +//============================================================================= +class ConfigFile { + public: + ConfigFile() { + config_file_ = -1; + config_file_path_[0] = 0; + has_created_file_ = false; + }; + + ~ConfigFile() { + }; + + void WriteFile(const char* directory, + const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id); + + const char *GetFilePath() { return config_file_path_; } + + void Unlink() { + if (config_file_ != -1) + unlink(config_file_path_); + + config_file_ = -1; + } + + private: + BOOL WriteData(const void *data, size_t length); + + BOOL AppendConfigData(const char *key, + const void *data, + size_t length); + + BOOL AppendConfigString(const char *key, + const char *value); + + BOOL AppendCrashTimeParameters(const char *processStartTimeString); + + int config_file_; // descriptor for config file + char config_file_path_[PATH_MAX]; // Path to configuration file + bool has_created_file_; +}; + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.mm b/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.mm new file mode 100644 index 000000000..ee2d94484 --- /dev/null +++ b/thirdparty/breakpad/client/mac/crash_generation/ConfigFile.mm @@ -0,0 +1,190 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Utility class that can persist a SimpleStringDictionary to disk. + +#import "client/mac/crash_generation/ConfigFile.h" + +#import +#include +#include + +#import "client/apple/Framework/BreakpadDefines.h" +#import "common/mac/SimpleStringDictionary.h" +#import "GTMDefines.h" + +#define VERBOSE 0 + +#if VERBOSE + bool gDebugLog = true; +#else + bool gDebugLog = false; +#endif + +#define DEBUGLOG if (gDebugLog) fprintf + +namespace google_breakpad { + +//============================================================================= +BOOL EnsureDirectoryPathExists(NSString *dirPath) { + NSFileManager *mgr = [NSFileManager defaultManager]; + + NSDictionary *attrs = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] + forKey:NSFilePosixPermissions]; + + return [mgr createDirectoryAtPath:dirPath + withIntermediateDirectories:YES + attributes:attrs + error:nil]; +} + +//============================================================================= +BOOL ConfigFile::WriteData(const void *data, size_t length) { + size_t result = write(config_file_, data, length); + + return result == length; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigData(const char *key, + const void *data, size_t length) { + assert(config_file_ != -1); + + if (!key) { + DEBUGLOG(stderr, "Breakpad: Missing Key\n"); + return NO; + } + + if (!data) { + DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : + ""); + return NO; + } + + // Write the key, \n, length of data (ascii integer), \n, data + char buffer[16]; + char nl = '\n'; + BOOL result = WriteData(key, strlen(key)); + + snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); + result &= WriteData(buffer, strlen(buffer)); + result &= WriteData(data, length); + result &= WriteData(&nl, 1); + return result; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigString(const char *key, + const char *value) { + return AppendConfigData(key, value, strlen(value)); +} + +//============================================================================= +BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) { + // Set process uptime parameter + struct timeval tv; + gettimeofday(&tv, NULL); + + char processUptimeString[32], processCrashtimeString[32]; + // Set up time if we've received the start time. + if (processStartTimeString) { + time_t processStartTime = strtol(processStartTimeString, NULL, 10); + time_t processUptime = tv.tv_sec - processStartTime; + // Store the uptime in milliseconds. + sprintf(processUptimeString, "%llu", + static_cast(processUptime) * 1000); + if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString)) + return false; + } + + sprintf(processCrashtimeString, "%zd", tv.tv_sec); + return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME, + processCrashtimeString); +} + +//============================================================================= +void ConfigFile::WriteFile(const char* directory, + const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id) { + + assert(config_file_ == -1); + + // Open and write out configuration file preamble + if (directory) { + snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX", + directory); + } else { + strlcpy(config_file_path_, "/tmp/Config-XXXXXX", + sizeof(config_file_path_)); + } + config_file_ = mkstemp(config_file_path_); + + if (config_file_ == -1) { + DEBUGLOG(stderr, + "mkstemp(config_file_path_) == -1 (%s)\n", + strerror(errno)); + return; + } + else { + DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_); + } + + has_created_file_ = true; + + // Add the minidump dir + AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); + AppendConfigString(kReporterMinidumpIDKey, minidump_id); + + // Write out the configuration parameters + BOOL result = YES; + const SimpleStringDictionary &dictionary = *configurationParameters; + + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(dictionary); + + while ((entry = iter.Next())) { + DEBUGLOG(stderr, + "config: (%s) -> (%s)\n", + entry->GetKey(), + entry->GetValue()); + result = AppendConfigString(entry->GetKey(), entry->GetValue()); + + if (!result) + break; + } + AppendCrashTimeParameters( + configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME)); + + close(config_file_); + config_file_ = -1; +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/client/mac/crash_generation/Inspector.h b/thirdparty/breakpad/client/mac/crash_generation/Inspector.h index 9d93b2a74..11a0dec6e 100644 --- a/thirdparty/breakpad/client/mac/crash_generation/Inspector.h +++ b/thirdparty/breakpad/client/mac/crash_generation/Inspector.h @@ -33,9 +33,10 @@ #import "common/mac/SimpleStringDictionary.h" #import -#import "client/mac/handler/minidump_generator.h" +#include -#define VERBOSE 0 +#import "client/mac/crash_generation/ConfigFile.h" +#import "client/mac/handler/minidump_generator.h" extern bool gDebugLog; @@ -78,48 +79,6 @@ using google_breakpad::MinidumpGenerator; namespace google_breakpad { -BOOL EnsureDirectoryPathExists(NSString *dirPath); - -//============================================================================= -class ConfigFile { - public: - ConfigFile() { - config_file_ = -1; - config_file_path_[0] = 0; - has_created_file_ = false; - }; - - ~ConfigFile() { - }; - - void WriteFile(const SimpleStringDictionary *configurationParameters, - const char *dump_dir, - const char *minidump_id); - - const char *GetFilePath() { return config_file_path_; } - - void Unlink() { - if (config_file_ != -1) - unlink(config_file_path_); - - config_file_ = -1; - } - - private: - BOOL WriteData(const void *data, size_t length); - - BOOL AppendConfigData(const char *key, - const void *data, - size_t length); - - BOOL AppendConfigString(const char *key, - const char *value); - - int config_file_; // descriptor for config file - char config_file_path_[PATH_MAX]; // Path to configuration file - bool has_created_file_; -}; - //============================================================================= class MinidumpLocation { public: @@ -163,6 +122,18 @@ class Inspector { void Inspect(const char *receive_port_name); private: + // The Inspector is invoked with its bootstrap port set to the bootstrap + // subset established in OnDemandServer.mm OnDemandServer::Initialize. + // For proper communication with the system, the sender (which will inherit + // the Inspector's bootstrap port) needs the per-session bootstrap namespace + // available directly in its bootstrap port. OnDemandServer stashed this + // port into the subset namespace under a special name. ResetBootstrapPort + // recovers this port and switches this task to use it as its own bootstrap + // (ensuring that children like the sender will inherit it), and saves the + // subset in bootstrap_subset_port_ for use by ServiceCheckIn and + // ServiceCheckOut. + kern_return_t ResetBootstrapPort(); + kern_return_t ServiceCheckIn(const char *receive_port_name); kern_return_t ServiceCheckOut(const char *receive_port_name); @@ -172,7 +143,9 @@ class Inspector { kern_return_t SendAcknowledgement(); void LaunchReporter(const char *inConfigFilePath); - void SetCrashTimeParameters(); + // The bootstrap port in which the inspector is registered and into which it + // must check in. + mach_port_t bootstrap_subset_port_; mach_port_t service_rcv_port_; diff --git a/thirdparty/breakpad/client/mac/crash_generation/Inspector.mm b/thirdparty/breakpad/client/mac/crash_generation/Inspector.mm index 1b0b97cb9..ea5d19a64 100644 --- a/thirdparty/breakpad/client/mac/crash_generation/Inspector.mm +++ b/thirdparty/breakpad/client/mac/crash_generation/Inspector.mm @@ -31,10 +31,10 @@ #include #include +#include #include #include #include -#include #import "client/mac/crash_generation/Inspector.h" @@ -43,167 +43,22 @@ #import "common/mac/SimpleStringDictionary.h" #import "common/mac/MachIPC.h" +#include "common/mac/bootstrap_compat.h" #import "GTMDefines.h" #import -#if VERBOSE - bool gDebugLog = true; -#else - bool gDebugLog = false; -#endif - namespace google_breakpad { //============================================================================= -BOOL EnsureDirectoryPathExists(NSString *dirPath) { - NSFileManager *mgr = [NSFileManager defaultManager]; - - // If we got a relative path, prepend the current directory - if (![dirPath isAbsolutePath]) - dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath]; - - NSString *path = dirPath; - - // Ensure that no file exists within the path which would block creation - while (1) { - BOOL isDir; - if ([mgr fileExistsAtPath:path isDirectory:&isDir]) { - if (isDir) - break; - - return NO; - } - - path = [path stringByDeletingLastPathComponent]; - } - - // Path now contains the first valid directory (or is empty) - if (![path length]) - return NO; - - NSString *common = - [dirPath commonPrefixWithString:path options:NSLiteralSearch]; - - // If everything is good - if ([common isEqualToString:dirPath]) - return YES; - - // Break up the difference into components - NSString *diff = [dirPath substringFromIndex:[common length] + 1]; - NSArray *components = [diff pathComponents]; - NSUInteger count = [components count]; - - // Rebuild the path one component at a time - NSDictionary *attrs = - [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] - forKey:NSFilePosixPermissions]; - path = common; - for (NSUInteger i = 0; i < count; ++i) { - path = [path stringByAppendingPathComponent:[components objectAtIndex:i]]; - - if (![mgr createDirectoryAtPath:path attributes:attrs]) - return NO; - } - - return YES; -} - -//============================================================================= -BOOL ConfigFile::WriteData(const void *data, size_t length) { - size_t result = write(config_file_, data, length); - - return result == length; -} - -//============================================================================= -BOOL ConfigFile::AppendConfigData(const char *key, - const void *data, size_t length) { - assert(config_file_ != -1); - - if (!key) { - DEBUGLOG(stderr, "Breakpad: Missing Key\n"); - return NO; - } - - if (!data) { - DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : - ""); - return NO; - } - - // Write the key, \n, length of data (ascii integer), \n, data - char buffer[16]; - char nl = '\n'; - BOOL result = WriteData(key, strlen(key)); - - snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); - result &= WriteData(buffer, strlen(buffer)); - result &= WriteData(data, length); - result &= WriteData(&nl, 1); - return result; -} - -//============================================================================= -BOOL ConfigFile::AppendConfigString(const char *key, - const char *value) { - return AppendConfigData(key, value, strlen(value)); -} - -//============================================================================= -void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters, - const char *dump_dir, - const char *minidump_id) { - - assert(config_file_ == -1); - - // Open and write out configuration file preamble - strlcpy(config_file_path_, "/tmp/Config-XXXXXX", - sizeof(config_file_path_)); - config_file_ = mkstemp(config_file_path_); - - if (config_file_ == -1) { - DEBUGLOG(stderr, - "mkstemp(config_file_path_) == -1 (%s)\n", - strerror(errno)); +void Inspector::Inspect(const char *receive_port_name) { + kern_return_t result = ResetBootstrapPort(); + if (result != KERN_SUCCESS) { return; } - else { - DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_); - } - has_created_file_ = true; - - // Add the minidump dir - AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); - AppendConfigString(kReporterMinidumpIDKey, minidump_id); - - // Write out the configuration parameters - BOOL result = YES; - const SimpleStringDictionary &dictionary = *configurationParameters; - - const KeyValueEntry *entry = NULL; - SimpleStringDictionaryIterator iter(dictionary); - - while ((entry = iter.Next())) { - DEBUGLOG(stderr, - "config: (%s) -> (%s)\n", - entry->GetKey(), - entry->GetValue()); - result = AppendConfigString(entry->GetKey(), entry->GetValue()); - - if (!result) - break; - } - - close(config_file_); - config_file_ = -1; -} - -//============================================================================= -void Inspector::Inspect(const char *receive_port_name) { - kern_return_t result = ServiceCheckIn(receive_port_name); + result = ServiceCheckIn(receive_port_name); if (result == KERN_SUCCESS) { result = ReadMessages(); @@ -240,11 +95,56 @@ void Inspector::Inspect(const char *receive_port_name) { } } +//============================================================================= +kern_return_t Inspector::ResetBootstrapPort() { + // A reasonable default, in case anything fails. + bootstrap_subset_port_ = bootstrap_port; + + mach_port_t self_task = mach_task_self(); + + kern_return_t kr = task_get_bootstrap_port(self_task, + &bootstrap_subset_port_); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + mach_port_t bootstrap_parent_port; + kr = bootstrap_look_up(bootstrap_subset_port_, + const_cast(BREAKPAD_BOOTSTRAP_PARENT_PORT), + &bootstrap_parent_port); + if (kr != BOOTSTRAP_SUCCESS) { + NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)", +#if defined(MAC_OS_X_VERSION_10_5) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + bootstrap_strerror(kr), +#else + mach_error_string(kr), +#endif + kr); + return kr; + } + + kr = task_set_bootstrap_port(self_task, bootstrap_parent_port); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + // Some things access the bootstrap port through this global variable + // instead of calling task_get_bootstrap_port. + bootstrap_port = bootstrap_parent_port; + + return KERN_SUCCESS; +} + //============================================================================= kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) { // We need to get the mach port representing this service, so we can // get information from the crashed process. - kern_return_t kr = bootstrap_check_in(bootstrap_port, + kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_, (char*)receive_port_name, &service_rcv_port_); @@ -275,9 +175,9 @@ kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) { } // Unregister the service associated with the receive port. - kr = bootstrap_register(bootstrap_port, - (char*)receive_port_name, - MACH_PORT_NULL); + kr = breakpad::BootstrapRegister(bootstrap_subset_port_, + (char*)receive_port_name, + MACH_PORT_NULL); if (kr != KERN_SUCCESS) { PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); @@ -363,30 +263,6 @@ kern_return_t Inspector::ReadMessages() { } //============================================================================= -// Sets keys in the parameters dictionary that are specific to process uptime. -// The two we set are process up time, and process crash time. -void Inspector::SetCrashTimeParameters() { - // Set process uptime parameter - struct timeval tv; - gettimeofday(&tv, NULL); - - char processUptimeString[32], processCrashtimeString[32]; - const char *processStartTimeString = - config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME); - - // Set up time if we've received the start time. - if (processStartTimeString) { - time_t processStartTime = strtol(processStartTimeString, NULL, 10); - time_t processUptime = tv.tv_sec - processStartTime; - sprintf(processUptimeString, "%zd", processUptime); - config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString); - } - - sprintf(processCrashtimeString, "%zd", tv.tv_sec); - config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME, - processCrashtimeString); -} - bool Inspector::InspectTask() { // keep the task quiet while we're looking at it task_suspend(remote_task_); @@ -397,7 +273,6 @@ bool Inspector::InspectTask() { const char *minidumpDirectory = config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY); - SetCrashTimeParameters(); // If the client app has not specified a minidump directory, // use a default of Library// if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { @@ -448,7 +323,8 @@ bool Inspector::InspectTask() { [minidumpPath UTF8String]); - config_file_.WriteFile( &config_params_, + config_file_.WriteFile( 0, + &config_params_, minidumpLocation.GetPath(), minidumpLocation.GetID()); diff --git a/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.c b/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.c deleted file mode 100644 index 19f36b19d..000000000 --- a/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.c +++ /dev/null @@ -1,1750 +0,0 @@ -/* - * IDENTIFICATION: - * stub generated Mon Jun 16 18:08:10 2008 - * with a MiG generated Tue Feb 19 02:01:43 PST 2008 by root@b75.local - * OPTIONS: - */ - -/* Module exc */ - -#define __MIG_check__Request__exc_subsystem__ 1 -#define __NDR_convert__Request__exc_subsystem__ 1 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifndef mig_internal -#define mig_internal static __inline__ -#endif /* mig_internal */ - -#ifndef mig_external -#define mig_external -#endif /* mig_external */ - -#if !defined(__MigTypeCheck) && defined(TypeCheck) -#define __MigTypeCheck TypeCheck /* Legacy setting */ -#endif /* !defined(__MigTypeCheck) */ - -#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_) -#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */ -#endif /* !defined(__MigKernelSpecificCode) */ - -#ifndef LimitCheck -#define LimitCheck 0 -#endif /* LimitCheck */ - -#ifndef min -#define min(a,b) ( ((a) < (b))? (a): (b) ) -#endif /* min */ - -#if !defined(_WALIGN_) -#define _WALIGN_(x) (((x) + 3) & ~3) -#endif /* !defined(_WALIGN_) */ - -#if !defined(_WALIGNSZ_) -#define _WALIGNSZ_(x) _WALIGN_(sizeof(x)) -#endif /* !defined(_WALIGNSZ_) */ - -#ifndef UseStaticTemplates -#define UseStaticTemplates 0 -#endif /* UseStaticTemplates */ - -#ifndef __DeclareRcvRpc -#define __DeclareRcvRpc(_NUM_, _NAME_) -#endif /* __DeclareRcvRpc */ - -#ifndef __BeforeRcvRpc -#define __BeforeRcvRpc(_NUM_, _NAME_) -#endif /* __BeforeRcvRpc */ - -#ifndef __AfterRcvRpc -#define __AfterRcvRpc(_NUM_, _NAME_) -#endif /* __AfterRcvRpc */ - -#ifndef __DeclareRcvSimple -#define __DeclareRcvSimple(_NUM_, _NAME_) -#endif /* __DeclareRcvSimple */ - -#ifndef __BeforeRcvSimple -#define __BeforeRcvSimple(_NUM_, _NAME_) -#endif /* __BeforeRcvSimple */ - -#ifndef __AfterRcvSimple -#define __AfterRcvSimple(_NUM_, _NAME_) -#endif /* __AfterRcvSimple */ - -#define novalue void - -#define msgh_request_port msgh_local_port -#define MACH_MSGH_BITS_REQUEST(bits) MACH_MSGH_BITS_LOCAL(bits) -#define msgh_reply_port msgh_remote_port -#define MACH_MSGH_BITS_REPLY(bits) MACH_MSGH_BITS_REMOTE(bits) - -#define MIG_RETURN_ERROR(X, code) {\ - ((mig_reply_error_t *)X)->RetCode = code;\ - ((mig_reply_error_t *)X)->NDR = NDR_record;\ - return;\ - } - -/* typedefs for all requests */ - -#ifndef __Request__exc_subsystem__defined -#define __Request__exc_subsystem__defined - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - } __Request__exception_raise_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - } __Request__exception_raise_state_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - } __Request__exception_raise_state_identity_t; -#ifdef __MigPackStructs -#pragma pack() -#endif -#endif /* !__Request__exc_subsystem__defined */ - -/* typedefs for all replies */ - -#ifndef __Reply__exc_subsystem__defined -#define __Reply__exc_subsystem__defined - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - } __Reply__exception_raise_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - int flavor; - mach_msg_type_number_t new_stateCnt; - natural_t new_state[144]; - } __Reply__exception_raise_state_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - int flavor; - mach_msg_type_number_t new_stateCnt; - natural_t new_state[144]; - } __Reply__exception_raise_state_identity_t; -#ifdef __MigPackStructs -#pragma pack() -#endif -#endif /* !__Reply__exc_subsystem__defined */ - - -/* union of all replies */ - -#ifndef __ReplyUnion__breakpad_exc_subsystem__defined -#define __ReplyUnion__breakpad_exc_subsystem__defined -union __ReplyUnion__breakpad_exc_subsystem { - __Reply__exception_raise_t Reply_exception_raise; - __Reply__exception_raise_state_t Reply_exception_raise_state; - __Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity; -}; -#endif /* __RequestUnion__breakpad_exc_subsystem__defined */ -/* Forward Declarations */ - - -mig_internal novalue _Xexception_raise - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); - -mig_internal novalue _Xexception_raise_state - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); - -mig_internal novalue _Xexception_raise_state_identity - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); - - -#if ( __MigTypeCheck || __NDR_convert__ ) -#if __MIG_check__Request__exc_subsystem__ -#if !defined(__MIG_check__Request__exception_raise_t__defined) -#define __MIG_check__Request__exception_raise_t__defined -#ifndef __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#if defined(__NDR_convert__int_rep__exc__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__int_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__exception__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#if defined(__NDR_convert__int_rep__exc__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__int_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__int_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exc__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__exc__integer_t) -#elif defined(__NDR_convert__int_rep__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__integer_t) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__exc__int32_t) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__code__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined -#if defined(__NDR_convert__int_rep__exc__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__codeCnt(a, f) \ - __NDR_convert__int_rep__exc__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#elif defined(__NDR_convert__int_rep__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_t__codeCnt(a, f) \ - __NDR_convert__int_rep__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#if defined(__NDR_convert__char_rep__exc__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__char_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_t__exception__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#if defined(__NDR_convert__char_rep__exc__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__char_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__char_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exc__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__exc__integer_t) -#elif defined(__NDR_convert__char_rep__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__integer_t) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__exc__int32_t) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_t__code__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#if defined(__NDR_convert__float_rep__exc__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__exception(a, f) \ - __NDR_convert__float_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_t__exception__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#if defined(__NDR_convert__float_rep__exc__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__float_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__float_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exc__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__exc__integer_t) -#elif defined(__NDR_convert__float_rep__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__integer_t) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__exc__int32_t) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_t__code__defined */ - - -mig_internal kern_return_t __MIG_check__Request__exception_raise_t(__attribute__((__unused__)) __Request__exception_raise_t *In0P) -{ - - typedef __Request__exception_raise_t __Request; -#if __MigTypeCheck - unsigned int msgh_size; -#endif /* __MigTypeCheck */ - -#if __MigTypeCheck - msgh_size = In0P->Head.msgh_size; - if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || - (In0P->msgh_body.msgh_descriptor_count != 2) || - (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 8)) || (msgh_size > (mach_msg_size_t)sizeof(__Request)) -) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if __MigTypeCheck - if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR || - In0P->thread.disposition != 17) - return MIG_TYPE_ERROR; -#endif /* __MigTypeCheck */ - -#if __MigTypeCheck - if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR || - In0P->task.disposition != 17) - return MIG_TYPE_ERROR; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) - __NDR_convert__int_rep__Request__exception_raise_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined */ -#if __MigTypeCheck - if (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 8) + ((4 * In0P->codeCnt))) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_t__exception__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_t__code__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_t__codeCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) { -#if defined(__NDR_convert__int_rep__Request__exception_raise_t__exception__defined) - __NDR_convert__int_rep__Request__exception_raise_t__exception(&In0P->exception, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__exception__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_t__code__defined) - __NDR_convert__int_rep__Request__exception_raise_t__code(&In0P->code, In0P->NDR.int_rep, In0P->codeCnt); -#endif /* __NDR_convert__int_rep__Request__exception_raise_t__code__defined */ - } -#endif /* defined(__NDR_convert__int_rep...) */ - -#if defined(__NDR_convert__char_rep__Request__exception_raise_t__exception__defined) || \ - defined(__NDR_convert__char_rep__Request__exception_raise_t__code__defined) || \ - 0 - if (In0P->NDR.char_rep != NDR_record.char_rep) { -#if defined(__NDR_convert__char_rep__Request__exception_raise_t__exception__defined) - __NDR_convert__char_rep__Request__exception_raise_t__exception(&In0P->exception, In0P->NDR.char_rep); -#endif /* __NDR_convert__char_rep__Request__exception_raise_t__exception__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_t__code__defined) - __NDR_convert__char_rep__Request__exception_raise_t__code(&In0P->code, In0P->NDR.char_rep, In0P->codeCnt); -#endif /* __NDR_convert__char_rep__Request__exception_raise_t__code__defined */ - } -#endif /* defined(__NDR_convert__char_rep...) */ - -#if defined(__NDR_convert__float_rep__Request__exception_raise_t__exception__defined) || \ - defined(__NDR_convert__float_rep__Request__exception_raise_t__code__defined) || \ - 0 - if (In0P->NDR.float_rep != NDR_record.float_rep) { -#if defined(__NDR_convert__float_rep__Request__exception_raise_t__exception__defined) - __NDR_convert__float_rep__Request__exception_raise_t__exception(&In0P->exception, In0P->NDR.float_rep); -#endif /* __NDR_convert__float_rep__Request__exception_raise_t__exception__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_t__code__defined) - __NDR_convert__float_rep__Request__exception_raise_t__code(&In0P->code, In0P->NDR.float_rep, In0P->codeCnt); -#endif /* __NDR_convert__float_rep__Request__exception_raise_t__code__defined */ - } -#endif /* defined(__NDR_convert__float_rep...) */ - - return MACH_MSG_SUCCESS; -} -#endif /* !defined(__MIG_check__Request__exception_raise_t__defined) */ -#endif /* __MIG_check__Request__exc_subsystem__ */ -#endif /* ( __MigTypeCheck || __NDR_convert__ ) */ - - -/* Routine exception_raise */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t breakpad_exception_raise -( - mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt -); - -/* Routine exception_raise */ -mig_internal novalue _Xexception_raise - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) -{ - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - mach_msg_trailer_t trailer; - } Request; -#ifdef __MigPackStructs -#pragma pack() -#endif - typedef __Request__exception_raise_t __Request; - typedef __Reply__exception_raise_t Reply; - - /* - * typedef struct { - * mach_msg_header_t Head; - * NDR_record_t NDR; - * kern_return_t RetCode; - * } mig_reply_error_t; - */ - - Request *In0P = (Request *) InHeadP; - Reply *OutP = (Reply *) OutHeadP; -#ifdef __MIG_check__Request__exception_raise_t__defined - kern_return_t check_result; -#endif /* __MIG_check__Request__exception_raise_t__defined */ - - __DeclareRcvRpc(2401, "exception_raise") - __BeforeRcvRpc(2401, "exception_raise") - -#if defined(__MIG_check__Request__exception_raise_t__defined) - check_result = __MIG_check__Request__exception_raise_t((__Request *)In0P); - if (check_result != MACH_MSG_SUCCESS) - { MIG_RETURN_ERROR(OutP, check_result); } -#endif /* defined(__MIG_check__Request__exception_raise_t__defined) */ - - OutP->RetCode = breakpad_exception_raise(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt); - - OutP->NDR = NDR_record; - - - __AfterRcvRpc(2401, "exception_raise") -} - -#if ( __MigTypeCheck || __NDR_convert__ ) -#if __MIG_check__Request__exc_subsystem__ -#if !defined(__MIG_check__Request__exception_raise_state_t__defined) -#define __MIG_check__Request__exception_raise_state_t__defined -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#if defined(__NDR_convert__int_rep__exc__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__int_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#if defined(__NDR_convert__int_rep__exc__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__int_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__int_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exc__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__exc__integer_t) -#elif defined(__NDR_convert__int_rep__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__integer_t) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__exc__int32_t) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined -#if defined(__NDR_convert__int_rep__exc__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt(a, f) \ - __NDR_convert__int_rep__exc__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#elif defined(__NDR_convert__int_rep__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt(a, f) \ - __NDR_convert__int_rep__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined -#if defined(__NDR_convert__int_rep__exc__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__int_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__int_rep__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__int_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__int_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#if defined(__NDR_convert__int_rep__exc__thread_state_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__int_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__thread_state_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__int_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exc__natural_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__int_rep__exc__natural_t) -#elif defined(__NDR_convert__int_rep__natural_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__int_rep__natural_t) -#elif defined(__NDR_convert__int_rep__exc__uint32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__int_rep__exc__uint32_t) -#elif defined(__NDR_convert__int_rep__uint32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__int_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined -#if defined(__NDR_convert__int_rep__exc__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt(a, f) \ - __NDR_convert__int_rep__exc__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#elif defined(__NDR_convert__int_rep__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt(a, f) \ - __NDR_convert__int_rep__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#if defined(__NDR_convert__char_rep__exc__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__char_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#if defined(__NDR_convert__char_rep__exc__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__char_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__char_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exc__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__exc__integer_t) -#elif defined(__NDR_convert__char_rep__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__integer_t) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__exc__int32_t) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined -#if defined(__NDR_convert__char_rep__exc__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__char_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__char_rep__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__char_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__char_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#if defined(__NDR_convert__char_rep__exc__thread_state_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__char_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__thread_state_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__char_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exc__natural_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__char_rep__exc__natural_t) -#elif defined(__NDR_convert__char_rep__natural_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__char_rep__natural_t) -#elif defined(__NDR_convert__char_rep__exc__uint32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__char_rep__exc__uint32_t) -#elif defined(__NDR_convert__char_rep__uint32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__char_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#if defined(__NDR_convert__float_rep__exc__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__exception(a, f) \ - __NDR_convert__float_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#if defined(__NDR_convert__float_rep__exc__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__float_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__float_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exc__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__exc__integer_t) -#elif defined(__NDR_convert__float_rep__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__integer_t) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__exc__int32_t) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined -#if defined(__NDR_convert__float_rep__exc__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__float_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__float_rep__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__float_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__flavor(a, f) \ - __NDR_convert__float_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#if defined(__NDR_convert__float_rep__exc__thread_state_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__float_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__thread_state_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__float_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exc__natural_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__float_rep__exc__natural_t) -#elif defined(__NDR_convert__float_rep__natural_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__float_rep__natural_t) -#elif defined(__NDR_convert__float_rep__exc__uint32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__float_rep__exc__uint32_t) -#elif defined(__NDR_convert__float_rep__uint32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__float_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined */ - - -mig_internal kern_return_t __MIG_check__Request__exception_raise_state_t(__attribute__((__unused__)) __Request__exception_raise_state_t *In0P, __attribute__((__unused__)) __Request__exception_raise_state_t **In1PP) -{ - - typedef __Request__exception_raise_state_t __Request; - __Request *In1P; -#if __MigTypeCheck - unsigned int msgh_size; -#endif /* __MigTypeCheck */ - unsigned int msgh_size_delta; - -#if __MigTypeCheck - msgh_size = In0P->Head.msgh_size; - if ((In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || - (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 584)) || (msgh_size > (mach_msg_size_t)sizeof(__Request))) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) - __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined */ - msgh_size_delta = (4 * In0P->codeCnt); -#if __MigTypeCheck - if (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 584) + msgh_size_delta) - return MIG_BAD_ARGUMENTS; - msgh_size -= msgh_size_delta; -#endif /* __MigTypeCheck */ - - *In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 8); - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) - __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined */ -#if __MigTypeCheck - if (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 584) + ((4 * In1P->old_stateCnt))) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_t__code__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_t__codeCnt__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_t__old_stateCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) { -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined) - __NDR_convert__int_rep__Request__exception_raise_state_t__exception(&In0P->exception, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__exception__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__code__defined) - __NDR_convert__int_rep__Request__exception_raise_state_t__code(&In0P->code, In0P->NDR.int_rep, In0P->codeCnt); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__code__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined) - __NDR_convert__int_rep__Request__exception_raise_state_t__flavor(&In1P->flavor, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__flavor__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined) - __NDR_convert__int_rep__Request__exception_raise_state_t__old_state(&In1P->old_state, In0P->NDR.int_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__int_rep...) */ - -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined) || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_t__code__defined) || \ - 0 || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined) || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined) || \ - 0 - if (In0P->NDR.char_rep != NDR_record.char_rep) { -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined) - __NDR_convert__char_rep__Request__exception_raise_state_t__exception(&In0P->exception, In0P->NDR.char_rep); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__exception__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_t__code__defined) - __NDR_convert__char_rep__Request__exception_raise_state_t__code(&In0P->code, In0P->NDR.char_rep, In0P->codeCnt); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__code__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined) - __NDR_convert__char_rep__Request__exception_raise_state_t__flavor(&In1P->flavor, In0P->NDR.char_rep); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__flavor__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined) - __NDR_convert__char_rep__Request__exception_raise_state_t__old_state(&In1P->old_state, In0P->NDR.char_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__char_rep...) */ - -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined) || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_t__code__defined) || \ - 0 || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined) || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined) || \ - 0 - if (In0P->NDR.float_rep != NDR_record.float_rep) { -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined) - __NDR_convert__float_rep__Request__exception_raise_state_t__exception(&In0P->exception, In0P->NDR.float_rep); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__exception__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_t__code__defined) - __NDR_convert__float_rep__Request__exception_raise_state_t__code(&In0P->code, In0P->NDR.float_rep, In0P->codeCnt); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__code__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined) - __NDR_convert__float_rep__Request__exception_raise_state_t__flavor(&In1P->flavor, In0P->NDR.float_rep); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__flavor__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined) - __NDR_convert__float_rep__Request__exception_raise_state_t__old_state(&In1P->old_state, In0P->NDR.float_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__float_rep...) */ - - return MACH_MSG_SUCCESS; -} -#endif /* !defined(__MIG_check__Request__exception_raise_state_t__defined) */ -#endif /* __MIG_check__Request__exc_subsystem__ */ -#endif /* ( __MigTypeCheck || __NDR_convert__ ) */ - - -/* Routine exception_raise_state */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t breakpad_exception_raise_state -( - mach_port_t exception_port, - exception_type_t exception, - const exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - const thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt -); - -/* Routine exception_raise_state */ -mig_internal novalue _Xexception_raise_state - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) -{ - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - mach_msg_trailer_t trailer; - } Request; -#ifdef __MigPackStructs -#pragma pack() -#endif - typedef __Request__exception_raise_state_t __Request; - typedef __Reply__exception_raise_state_t Reply; - - /* - * typedef struct { - * mach_msg_header_t Head; - * NDR_record_t NDR; - * kern_return_t RetCode; - * } mig_reply_error_t; - */ - - Request *In0P = (Request *) InHeadP; - Request *In1P; - Reply *OutP = (Reply *) OutHeadP; -#ifdef __MIG_check__Request__exception_raise_state_t__defined - kern_return_t check_result; -#endif /* __MIG_check__Request__exception_raise_state_t__defined */ - - __DeclareRcvRpc(2402, "exception_raise_state") - __BeforeRcvRpc(2402, "exception_raise_state") - -#if defined(__MIG_check__Request__exception_raise_state_t__defined) - check_result = __MIG_check__Request__exception_raise_state_t((__Request *)In0P, (__Request **)&In1P); - if (check_result != MACH_MSG_SUCCESS) - { MIG_RETURN_ERROR(OutP, check_result); } -#endif /* defined(__MIG_check__Request__exception_raise_state_t__defined) */ - - OutP->new_stateCnt = 144; - - OutP->RetCode = breakpad_exception_raise_state(In0P->Head.msgh_request_port, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt); - if (OutP->RetCode != KERN_SUCCESS) { - MIG_RETURN_ERROR(OutP, OutP->RetCode); - } - - OutP->NDR = NDR_record; - - - OutP->flavor = In1P->flavor; - OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 576) + (((4 * OutP->new_stateCnt))); - - __AfterRcvRpc(2402, "exception_raise_state") -} - -#if ( __MigTypeCheck || __NDR_convert__ ) -#if __MIG_check__Request__exc_subsystem__ -#if !defined(__MIG_check__Request__exception_raise_state_identity_t__defined) -#define __MIG_check__Request__exception_raise_state_identity_t__defined -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#if defined(__NDR_convert__int_rep__exc__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exception_type_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__int_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#if defined(__NDR_convert__int_rep__exc__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__int_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exception_data_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__int_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exc__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__exc__integer_t) -#elif defined(__NDR_convert__int_rep__integer_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__int_rep__integer_t) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__exc__int32_t) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__int_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined -#if defined(__NDR_convert__int_rep__exc__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt(a, f) \ - __NDR_convert__int_rep__exc__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#elif defined(__NDR_convert__int_rep__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt(a, f) \ - __NDR_convert__int_rep__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined -#if defined(__NDR_convert__int_rep__exc__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__int_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__int__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__int_rep__int((int *)(a), f) -#elif defined(__NDR_convert__int_rep__exc__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__int_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__int_rep__int32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__int_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#if defined(__NDR_convert__int_rep__exc__thread_state_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__int_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__thread_state_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__int_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__int_rep__exc__natural_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__int_rep__exc__natural_t) -#elif defined(__NDR_convert__int_rep__natural_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__int_rep__natural_t) -#elif defined(__NDR_convert__int_rep__exc__uint32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__int_rep__exc__uint32_t) -#elif defined(__NDR_convert__int_rep__uint32_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__int_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined */ - -#ifndef __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined -#if defined(__NDR_convert__int_rep__exc__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt(a, f) \ - __NDR_convert__int_rep__exc__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#elif defined(__NDR_convert__int_rep__mach_msg_type_number_t__defined) -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined -#define __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt(a, f) \ - __NDR_convert__int_rep__mach_msg_type_number_t((mach_msg_type_number_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#if defined(__NDR_convert__char_rep__exc__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exception_type_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__char_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#if defined(__NDR_convert__char_rep__exc__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__char_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exception_data_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__char_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exc__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__exc__integer_t) -#elif defined(__NDR_convert__char_rep__integer_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__char_rep__integer_t) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__exc__int32_t) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__char_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined -#if defined(__NDR_convert__char_rep__exc__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__char_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__int__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__char_rep__int((int *)(a), f) -#elif defined(__NDR_convert__char_rep__exc__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__char_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__char_rep__int32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__char_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined */ - -#ifndef __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#if defined(__NDR_convert__char_rep__exc__thread_state_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__char_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__thread_state_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__char_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__char_rep__exc__natural_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__char_rep__exc__natural_t) -#elif defined(__NDR_convert__char_rep__natural_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__char_rep__natural_t) -#elif defined(__NDR_convert__char_rep__exc__uint32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__char_rep__exc__uint32_t) -#elif defined(__NDR_convert__char_rep__uint32_t__defined) -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__char_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#if defined(__NDR_convert__float_rep__exc__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__exc__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exception_type_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__exception_type_t((exception_type_t *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(a, f) \ - __NDR_convert__float_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#if defined(__NDR_convert__float_rep__exc__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__float_rep__exc__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exception_data_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__float_rep__exception_data_t((exception_data_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exc__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__exc__integer_t) -#elif defined(__NDR_convert__float_rep__integer_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((integer_t *)(a), f, c, __NDR_convert__float_rep__integer_t) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__exc__int32_t) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(a, f, c) \ - __NDR_convert__ARRAY((int32_t *)(a), f, c, __NDR_convert__float_rep__int32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined -#if defined(__NDR_convert__float_rep__exc__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__float_rep__exc__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__int__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__float_rep__int((int *)(a), f) -#elif defined(__NDR_convert__float_rep__exc__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__float_rep__exc__int32_t((int32_t *)(a), f) -#elif defined(__NDR_convert__float_rep__int32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor(a, f) \ - __NDR_convert__float_rep__int32_t((int32_t *)(a), f) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined */ - -#ifndef __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#if defined(__NDR_convert__float_rep__exc__thread_state_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__float_rep__exc__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__thread_state_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__float_rep__thread_state_t((thread_state_t *)(a), f, c) -#elif defined(__NDR_convert__float_rep__exc__natural_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__float_rep__exc__natural_t) -#elif defined(__NDR_convert__float_rep__natural_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((natural_t *)(a), f, c, __NDR_convert__float_rep__natural_t) -#elif defined(__NDR_convert__float_rep__exc__uint32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__float_rep__exc__uint32_t) -#elif defined(__NDR_convert__float_rep__uint32_t__defined) -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined -#define __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(a, f, c) \ - __NDR_convert__ARRAY((uint32_t *)(a), f, c, __NDR_convert__float_rep__uint32_t) -#endif /* defined(__NDR_convert__*__defined) */ -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined */ - - -mig_internal kern_return_t __MIG_check__Request__exception_raise_state_identity_t(__attribute__((__unused__)) __Request__exception_raise_state_identity_t *In0P, __attribute__((__unused__)) __Request__exception_raise_state_identity_t **In1PP) -{ - - typedef __Request__exception_raise_state_identity_t __Request; - __Request *In1P; -#if __MigTypeCheck - unsigned int msgh_size; -#endif /* __MigTypeCheck */ - unsigned int msgh_size_delta; - -#if __MigTypeCheck - msgh_size = In0P->Head.msgh_size; - if (!(In0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) || - (In0P->msgh_body.msgh_descriptor_count != 2) || - (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 584)) || (msgh_size > (mach_msg_size_t)sizeof(__Request)) -) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if __MigTypeCheck - if (In0P->thread.type != MACH_MSG_PORT_DESCRIPTOR || - In0P->thread.disposition != 17) - return MIG_TYPE_ERROR; -#endif /* __MigTypeCheck */ - -#if __MigTypeCheck - if (In0P->task.type != MACH_MSG_PORT_DESCRIPTOR || - In0P->task.disposition != 17) - return MIG_TYPE_ERROR; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt(&In0P->codeCnt, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined */ - msgh_size_delta = (4 * In0P->codeCnt); -#if __MigTypeCheck - if (msgh_size < (mach_msg_size_t)(sizeof(__Request) - 584) + msgh_size_delta) - return MIG_BAD_ARGUMENTS; - msgh_size -= msgh_size_delta; -#endif /* __MigTypeCheck */ - - *In1PP = In1P = (__Request *) ((pointer_t) In0P + msgh_size_delta - 8); - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt(&In1P->old_stateCnt, In1P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined */ -#if __MigTypeCheck - if (msgh_size != (mach_msg_size_t)(sizeof(__Request) - 584) + ((4 * In1P->old_stateCnt))) - return MIG_BAD_ARGUMENTS; -#endif /* __MigTypeCheck */ - -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__codeCnt__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined) || \ - defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_stateCnt__defined) - if (In0P->NDR.int_rep != NDR_record.int_rep) { -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception(&In0P->exception, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__exception__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code(&In0P->code, In0P->NDR.int_rep, In0P->codeCnt); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__code__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor(&In1P->flavor, In0P->NDR.int_rep); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__flavor__defined */ -#if defined(__NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined) - __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state(&In1P->old_state, In0P->NDR.int_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__int_rep__Request__exception_raise_state_identity_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__int_rep...) */ - -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined) || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined) || \ - 0 || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined) || \ - defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined) || \ - 0 - if (In0P->NDR.char_rep != NDR_record.char_rep) { -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined) - __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception(&In0P->exception, In0P->NDR.char_rep); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__exception__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined) - __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code(&In0P->code, In0P->NDR.char_rep, In0P->codeCnt); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__code__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined) - __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor(&In1P->flavor, In0P->NDR.char_rep); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__flavor__defined */ -#if defined(__NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined) - __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state(&In1P->old_state, In0P->NDR.char_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__char_rep__Request__exception_raise_state_identity_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__char_rep...) */ - -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined) || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined) || \ - 0 || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined) || \ - defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined) || \ - 0 - if (In0P->NDR.float_rep != NDR_record.float_rep) { -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined) - __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception(&In0P->exception, In0P->NDR.float_rep); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__exception__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined) - __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code(&In0P->code, In0P->NDR.float_rep, In0P->codeCnt); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__code__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined) - __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor(&In1P->flavor, In0P->NDR.float_rep); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__flavor__defined */ -#if defined(__NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined) - __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state(&In1P->old_state, In0P->NDR.float_rep, In1P->old_stateCnt); -#endif /* __NDR_convert__float_rep__Request__exception_raise_state_identity_t__old_state__defined */ - } -#endif /* defined(__NDR_convert__float_rep...) */ - - return MACH_MSG_SUCCESS; -} -#endif /* !defined(__MIG_check__Request__exception_raise_state_identity_t__defined) */ -#endif /* __MIG_check__Request__exc_subsystem__ */ -#endif /* ( __MigTypeCheck || __NDR_convert__ ) */ - - -/* Routine exception_raise_state_identity */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t breakpad_exception_raise_state_identity -( - mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt -); - -/* Routine exception_raise_state_identity */ -mig_internal novalue _Xexception_raise_state_identity - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) -{ - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - mach_msg_trailer_t trailer; - } Request; -#ifdef __MigPackStructs -#pragma pack() -#endif - typedef __Request__exception_raise_state_identity_t __Request; - typedef __Reply__exception_raise_state_identity_t Reply; - - /* - * typedef struct { - * mach_msg_header_t Head; - * NDR_record_t NDR; - * kern_return_t RetCode; - * } mig_reply_error_t; - */ - - Request *In0P = (Request *) InHeadP; - Request *In1P; - Reply *OutP = (Reply *) OutHeadP; -#ifdef __MIG_check__Request__exception_raise_state_identity_t__defined - kern_return_t check_result; -#endif /* __MIG_check__Request__exception_raise_state_identity_t__defined */ - - __DeclareRcvRpc(2403, "exception_raise_state_identity") - __BeforeRcvRpc(2403, "exception_raise_state_identity") - -#if defined(__MIG_check__Request__exception_raise_state_identity_t__defined) - check_result = __MIG_check__Request__exception_raise_state_identity_t((__Request *)In0P, (__Request **)&In1P); - if (check_result != MACH_MSG_SUCCESS) - { MIG_RETURN_ERROR(OutP, check_result); } -#endif /* defined(__MIG_check__Request__exception_raise_state_identity_t__defined) */ - - OutP->new_stateCnt = 144; - - OutP->RetCode = breakpad_exception_raise_state_identity(In0P->Head.msgh_request_port, In0P->thread.name, In0P->task.name, In0P->exception, In0P->code, In0P->codeCnt, &In1P->flavor, In1P->old_state, In1P->old_stateCnt, OutP->new_state, &OutP->new_stateCnt); - if (OutP->RetCode != KERN_SUCCESS) { - MIG_RETURN_ERROR(OutP, OutP->RetCode); - } - - OutP->NDR = NDR_record; - - - OutP->flavor = In1P->flavor; - OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply) - 576) + (((4 * OutP->new_stateCnt))); - - __AfterRcvRpc(2403, "exception_raise_state_identity") -} - - -extern boolean_t exc_server( - mach_msg_header_t *InHeadP, - mach_msg_header_t *OutHeadP); - -extern mig_routine_t exc_server_routine( - mach_msg_header_t *InHeadP); - - -/* Description of this subsystem, for use in direct RPC */ -const struct breakpad_exc_subsystem { - mig_server_routine_t server; /* Server routine */ - mach_msg_id_t start; /* Min routine number */ - mach_msg_id_t end; /* Max routine number + 1 */ - unsigned int maxsize; /* Max msg size */ - vm_address_t reserved; /* Reserved */ - struct routine_descriptor /*Array of routine descriptors */ - routine[3]; -} breakpad_exc_subsystem = { - exc_server_routine, - 2401, - 2404, - (mach_msg_size_t)sizeof(union __ReplyUnion__breakpad_exc_subsystem), - (vm_address_t)0, - { - { (mig_impl_routine_t) 0, - (mig_stub_routine_t) _Xexception_raise, 6, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__exception_raise_t)}, - { (mig_impl_routine_t) 0, - (mig_stub_routine_t) _Xexception_raise_state, 9, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__exception_raise_state_t)}, - { (mig_impl_routine_t) 0, - (mig_stub_routine_t) _Xexception_raise_state_identity, 11, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__exception_raise_state_identity_t)}, - } -}; - -mig_external boolean_t exc_server - (mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP) -{ - /* - * typedef struct { - * mach_msg_header_t Head; - * NDR_record_t NDR; - * kern_return_t RetCode; - * } mig_reply_error_t; - */ - - register mig_routine_t routine; - - OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0); - OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port; - /* Minimal size: routine() will update it if different */ - OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); - OutHeadP->msgh_local_port = MACH_PORT_NULL; - OutHeadP->msgh_id = InHeadP->msgh_id + 100; - - if ((InHeadP->msgh_id > 2403) || (InHeadP->msgh_id < 2401) || - ((routine = breakpad_exc_subsystem.routine[InHeadP->msgh_id - 2401].stub_routine) == 0)) { - ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record; - ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID; - return FALSE; - } - (*routine) (InHeadP, OutHeadP); - return TRUE; -} - -mig_external mig_routine_t exc_server_routine - (mach_msg_header_t *InHeadP) -{ - register int msgh_id; - - msgh_id = InHeadP->msgh_id - 2401; - - if ((msgh_id > 2) || (msgh_id < 0)) - return 0; - - return breakpad_exc_subsystem.routine[msgh_id].stub_routine; -} diff --git a/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.h b/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.h deleted file mode 100644 index c05f25a8c..000000000 --- a/thirdparty/breakpad/client/mac/handler/breakpad_exc_server.h +++ /dev/null @@ -1,258 +0,0 @@ -#ifndef _exc_user_ -#define _exc_user_ - -/* Module exc */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef AUTOTEST -#ifndef FUNCTION_PTR_T -#define FUNCTION_PTR_T -typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t); -typedef struct { - char *name; - function_ptr_t function; -} function_table_entry; -typedef function_table_entry *function_table_t; -#endif /* FUNCTION_PTR_T */ -#endif /* AUTOTEST */ - -#ifndef exc_MSG_COUNT -#define exc_MSG_COUNT 3 -#endif /* exc_MSG_COUNT */ - -#include -#include -#include -#include - -#ifdef __BeforeMigUserHeader -__BeforeMigUserHeader -#endif /* __BeforeMigUserHeader */ - -#include -__BEGIN_DECLS - - -/* Routine exception_raise */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t exception_raise -( - mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt -); - -/* Routine exception_raise_state */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t exception_raise_state -( - mach_port_t exception_port, - exception_type_t exception, - const exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - const thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt -); - -/* Routine exception_raise_state_identity */ -#ifdef mig_external -mig_external -#else -extern -#endif /* mig_external */ -kern_return_t exception_raise_state_identity -( - mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt -); - -__END_DECLS - -/********************** Caution **************************/ -/* The following data types should be used to calculate */ -/* maximum message sizes only. The actual message may be */ -/* smaller, and the position of the arguments within the */ -/* message layout may vary from what is presented here. */ -/* For example, if any of the arguments are variable- */ -/* sized, and less than the maximum is sent, the data */ -/* will be packed tight in the actual message to reduce */ -/* the presence of holes. */ -/********************** Caution **************************/ - -/* typedefs for all requests */ - -#ifndef __Request__exc_subsystem__defined -#define __Request__exc_subsystem__defined - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - } __Request__exception_raise_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - } __Request__exception_raise_state_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - /* start of the kernel processed data */ - mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; - /* end of the kernel processed data */ - NDR_record_t NDR; - exception_type_t exception; - mach_msg_type_number_t codeCnt; - integer_t code[2]; - int flavor; - mach_msg_type_number_t old_stateCnt; - natural_t old_state[144]; - } __Request__exception_raise_state_identity_t; -#ifdef __MigPackStructs -#pragma pack() -#endif -#endif /* !__Request__exc_subsystem__defined */ - -/* union of all requests */ - -#ifndef __RequestUnion__exc_subsystem__defined -#define __RequestUnion__exc_subsystem__defined -union __RequestUnion__exc_subsystem { - __Request__exception_raise_t Request_exception_raise; - __Request__exception_raise_state_t Request_exception_raise_state; - __Request__exception_raise_state_identity_t Request_exception_raise_state_identity; -}; -#endif /* !__RequestUnion__exc_subsystem__defined */ -/* typedefs for all replies */ - -#ifndef __Reply__exc_subsystem__defined -#define __Reply__exc_subsystem__defined - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - } __Reply__exception_raise_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - int flavor; - mach_msg_type_number_t new_stateCnt; - natural_t new_state[144]; - } __Reply__exception_raise_state_t; -#ifdef __MigPackStructs -#pragma pack() -#endif - -#ifdef __MigPackStructs -#pragma pack(4) -#endif - typedef struct { - mach_msg_header_t Head; - NDR_record_t NDR; - kern_return_t RetCode; - int flavor; - mach_msg_type_number_t new_stateCnt; - natural_t new_state[144]; - } __Reply__exception_raise_state_identity_t; -#ifdef __MigPackStructs -#pragma pack() -#endif -#endif /* !__Reply__exc_subsystem__defined */ - -/* union of all replies */ - -#ifndef __ReplyUnion__exc_subsystem__defined -#define __ReplyUnion__exc_subsystem__defined -union __ReplyUnion__exc_subsystem { - __Reply__exception_raise_t Reply_exception_raise; - __Reply__exception_raise_state_t Reply_exception_raise_state; - __Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity; -}; -#endif /* !__RequestUnion__exc_subsystem__defined */ - -#ifndef subsystem_to_name_map_exc -#define subsystem_to_name_map_exc \ - { "exception_raise", 2401 },\ - { "exception_raise_state", 2402 },\ - { "exception_raise_state_identity", 2403 } -#endif - -#ifdef __AfterMigUserHeader -__AfterMigUserHeader -#endif /* __AfterMigUserHeader */ - -#endif /* _exc_user_ */ diff --git a/thirdparty/breakpad/client/mac/handler/breakpad_nlist_64.cc b/thirdparty/breakpad/client/mac/handler/breakpad_nlist_64.cc index 709e8546f..b50aa03af 100644 --- a/thirdparty/breakpad/client/mac/handler/breakpad_nlist_64.cc +++ b/thirdparty/breakpad/client/mac/handler/breakpad_nlist_64.cc @@ -67,6 +67,7 @@ #include "breakpad_nlist_64.h" +#include #include #include #include @@ -189,16 +190,16 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, struct exec buf; if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) || - (N_BADMAG(buf) && *((long *)&buf) != magic && - NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) && - /* The following is the big-endian ppc64 check */ - (*((long*)&buf)) != FAT_MAGIC) { + (N_BADMAG(buf) && *((uint32_t *)&buf) != magic && + CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC && + /* The following is the big-endian ppc64 check */ + (*((uint32_t*)&buf)) != FAT_MAGIC)) { return -1; } /* Deal with fat file if necessary */ unsigned arch_offset = 0; - if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC || + if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC || /* The following is the big-endian ppc64 check */ *((unsigned int *)&buf) == FAT_MAGIC) { /* Get host info */ @@ -222,7 +223,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, } /* Convert fat_narchs to host byte order */ - fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch); + fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch); /* Read in the fat archs */ struct fat_arch *fat_archs = @@ -232,7 +233,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, } if (read(fd, (char *)fat_archs, sizeof(struct fat_arch) * fh.nfat_arch) != - (ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) { + (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) { free(fat_archs); return -1; } @@ -243,15 +244,15 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, */ for (unsigned i = 0; i < fh.nfat_arch; i++) { fat_archs[i].cputype = - NXSwapBigIntToHost(fat_archs[i].cputype); + CFSwapInt32BigToHost(fat_archs[i].cputype); fat_archs[i].cpusubtype = - NXSwapBigIntToHost(fat_archs[i].cpusubtype); + CFSwapInt32BigToHost(fat_archs[i].cpusubtype); fat_archs[i].offset = - NXSwapBigIntToHost(fat_archs[i].offset); + CFSwapInt32BigToHost(fat_archs[i].offset); fat_archs[i].size = - NXSwapBigIntToHost(fat_archs[i].size); + CFSwapInt32BigToHost(fat_archs[i].size); fat_archs[i].align = - NXSwapBigIntToHost(fat_archs[i].align); + CFSwapInt32BigToHost(fat_archs[i].align); } struct fat_arch *fap = NULL; @@ -296,7 +297,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, return -1; } if (read(fd, (char *)load_commands, mh.sizeofcmds) != - mh.sizeofcmds) { + (ssize_t)mh.sizeofcmds) { free(load_commands); return -1; } @@ -304,7 +305,7 @@ int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, struct load_command *lcp = load_commands; // iterate through all load commands, looking for // LC_SYMTAB load command - for (long i = 0; i < mh.ncmds; i++) { + for (uint32_t i = 0; i < mh.ncmds; i++) { if (lcp->cmdsize % sizeof(word_type) != 0 || lcp->cmdsize <= 0 || (char *)lcp + lcp->cmdsize > diff --git a/thirdparty/breakpad/client/mac/handler/dynamic_images.cc b/thirdparty/breakpad/client/mac/handler/dynamic_images.cc index c6499e68c..ef5743ca2 100644 --- a/thirdparty/breakpad/client/mac/handler/dynamic_images.cc +++ b/thirdparty/breakpad/client/mac/handler/dynamic_images.cc @@ -35,19 +35,22 @@ extern "C" { // needed to compile on Leopard #include } -#include "breakpad_nlist_64.h" -#include #include -#include +#include #include -#include #include #include +#include #include #include #include +#include "breakpad_nlist_64.h" + +#if !TARGET_OS_IPHONE +#include + #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif @@ -67,6 +70,8 @@ typedef struct task_dyld_info *task_dyld_info_t; #endif +#endif // !TARGET_OS_IPHONE + namespace google_breakpad { using std::string; @@ -358,6 +363,11 @@ static uint64_t LookupSymbol(const char* symbol_name, return list.n_value; } +#if TARGET_OS_IPHONE +static bool HasTaskDyldInfo() { + return true; +} +#else static SInt32 GetOSVersionInternal() { SInt32 os_version = 0; Gestalt(gestaltSystemVersion, &os_version); @@ -369,21 +379,22 @@ static SInt32 GetOSVersion() { return os_version; } -static bool IsSnowLeopardOrLater() { +static bool HasTaskDyldInfo() { #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 return true; #else return GetOSVersion() >= 0x1060; #endif } +#endif // TARGET_OS_IPHONE uint64_t DynamicImages::GetDyldAllImageInfosPointer() { - if (IsSnowLeopardOrLater()) { + if (HasTaskDyldInfo()) { task_dyld_info_data_t task_dyld_info; mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) != KERN_SUCCESS) { - return NULL; + return 0; } return (uint64_t)task_dyld_info.all_image_info_addr; diff --git a/thirdparty/breakpad/client/mac/handler/dynamic_images.h b/thirdparty/breakpad/client/mac/handler/dynamic_images.h index 63816bf38..d039eda00 100644 --- a/thirdparty/breakpad/client/mac/handler/dynamic_images.h +++ b/thirdparty/breakpad/client/mac/handler/dynamic_images.h @@ -45,6 +45,8 @@ #include #include +#include "mach_vm_compat.h" + namespace google_breakpad { using std::string; @@ -281,6 +283,8 @@ class DynamicImages { return CPU_TYPE_POWERPC; #elif defined(__ppc64__) return CPU_TYPE_POWERPC64; +#elif defined(__arm__) + return CPU_TYPE_ARM; #else #error "GetNativeCPUType not implemented for this architecture" #endif diff --git a/thirdparty/breakpad/client/mac/handler/exception_handler.cc b/thirdparty/breakpad/client/mac/handler/exception_handler.cc index d4cd43d5f..40430194f 100644 --- a/thirdparty/breakpad/client/mac/handler/exception_handler.cc +++ b/thirdparty/breakpad/client/mac/handler/exception_handler.cc @@ -28,16 +28,25 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include #include +#include +#include #include "client/mac/handler/exception_handler.h" #include "client/mac/handler/minidump_generator.h" #include "common/mac/macho_utilities.h" #include "common/mac/scoped_task_suspend-inl.h" +#include "google_breakpad/common/minidump_exception_mac.h" #ifndef USE_PROTECTED_ALLOCATIONS +#if TARGET_OS_IPHONE +#define USE_PROTECTED_ALLOCATIONS 1 +#else #define USE_PROTECTED_ALLOCATIONS 0 #endif +#endif // If USE_PROTECTED_ALLOCATIONS is activated then the // gBreakpadAllocator needs to be setup in other code @@ -48,9 +57,15 @@ extern ProtectedMemoryAllocator *gBreakpadAllocator; #endif - namespace google_breakpad { +static union { +#if USE_PROTECTED_ALLOCATIONS + char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +#endif + google_breakpad::ExceptionHandler *handler; +} gProtectedData; + using std::map; // These structures and techniques are illustrated in @@ -87,6 +102,7 @@ struct ExceptionReplyMessage { exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; +#if !TARGET_OS_IPHONE extern "C" { // Forward declarations for functions that need "C" style compilation @@ -102,128 +118,98 @@ extern "C" exception_data_t code, mach_msg_type_number_t code_count) __attribute__((visibility("default"))); +} +#endif - kern_return_t ForwardException(mach_port_t task, - mach_port_t failed_thread, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count); +kern_return_t ForwardException(mach_port_t task, + mach_port_t failed_thread, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); - kern_return_t exception_raise(mach_port_t target_port, - mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t exception_code, - mach_msg_type_number_t exception_code_count); +#if TARGET_OS_IPHONE +// Implementation is based on the implementation generated by mig. +boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP) { + OutHeadP->msgh_bits = + MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0); + OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port; + /* Minimal size: routine() will update it if different */ + OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); + OutHeadP->msgh_local_port = MACH_PORT_NULL; + OutHeadP->msgh_id = InHeadP->msgh_id + 100; - kern_return_t - exception_raise_state(mach_port_t target_port, - mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t exception_code, - mach_msg_type_number_t code_count, - thread_state_flavor_t *target_flavor, - thread_state_t in_thread_state, - mach_msg_type_number_t in_thread_state_count, - thread_state_t out_thread_state, - mach_msg_type_number_t *out_thread_state_count); + if (InHeadP->msgh_id != 2401) { + ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record; + ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID; + return FALSE; + } - kern_return_t - exception_raise_state_identity(mach_port_t target_port, - mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t exception_code, - mach_msg_type_number_t exception_code_count, - thread_state_flavor_t *target_flavor, - thread_state_t in_thread_state, - mach_msg_type_number_t in_thread_state_count, - thread_state_t out_thread_state, - mach_msg_type_number_t *out_thread_state_count); +#ifdef __MigPackStructs +#pragma pack(4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + mach_msg_trailer_t trailer; + } Request; - kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, - exception_type_t exception, - const exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - const thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt - ); + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#ifdef __MigPackStructs +#pragma pack() +#endif - kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt - ); + Request *In0P = (Request *)InHeadP; + Reply *OutP = (Reply *)OutHeadP; - kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count); + if (In0P->task.name != mach_task_self()) { + return FALSE; + } + OutP->RetCode = ForwardException(In0P->task.name, + In0P->thread.name, + In0P->exception, + In0P->code, + In0P->codeCnt); + OutP->NDR = NDR_record; + return TRUE; +} +#else +boolean_t breakpad_exc_server(mach_msg_header_t *request, + mach_msg_header_t *reply) { + return exc_server(request, reply); } - - -kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, - exception_type_t exception, - const exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - const thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt - ) -{ - return KERN_SUCCESS; -} - -kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, - mach_port_t thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t codeCnt, - int *flavor, - thread_state_t old_state, - mach_msg_type_number_t old_stateCnt, - thread_state_t new_state, - mach_msg_type_number_t *new_stateCnt - ) -{ - return KERN_SUCCESS; -} - -kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count) { - +// Callback from exc_server() +kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { if (task != mach_task_self()) { return KERN_FAILURE; } return ForwardException(task, failed_thread, exception, code, code_count); } - +#endif ExceptionHandler::ExceptionHandler(const string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, bool install_handler, - const char *port_name) + const char *port_name) : dump_path_(), filter_(filter), callback_(callback), @@ -239,8 +225,10 @@ ExceptionHandler::ExceptionHandler(const string &dump_path, // This will update to the ID and C-string pointers set_dump_path(dump_path); MinidumpGenerator::GatherSystemInformation(); +#if !TARGET_OS_IPHONE if (port_name) crash_generation_client_.reset(new CrashGenerationClient(port_name)); +#endif Setup(install_handler); } @@ -322,6 +310,8 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, EXC_I386_BPT, #elif defined (__ppc__) || defined (__ppc64__) EXC_PPC_BREAKPOINT, +#elif defined (__arm__) + EXC_ARM_BREAKPOINT, #else #error architecture not supported #endif @@ -340,7 +330,8 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type, int exception_code, int exception_subcode, mach_port_t thread_name, - bool exit_after_write) { + bool exit_after_write, + bool report_current_thread) { bool result = false; if (directCallback_) { @@ -352,6 +343,7 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type, if (exit_after_write) _exit(exception_type); } +#if !TARGET_OS_IPHONE } else if (IsOutOfProcess()) { if (exception_type && exception_code) { // If this is a real exception, give the filter (if any) a chance to @@ -364,13 +356,16 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type, exception_subcode, thread_name); } +#endif } else { string minidump_id; // Putting the MinidumpGenerator in its own context will ensure that the // destructor is executed, closing the newly created minidump file. if (!dump_path_.empty()) { - MinidumpGenerator md; + MinidumpGenerator md(mach_task_self(), + report_current_thread ? MACH_PORT_NULL : + mach_thread_self()); if (exception_type && exception_code) { // If this is a real exception, give the filter (if any) a chance to // decide if this should be sent. @@ -411,13 +406,13 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, current.count = EXC_TYPES_COUNT; mach_port_t current_task = mach_task_self(); - kern_return_t result = task_get_exception_ports(current_task, - s_exception_mask, - current.masks, - ¤t.count, - current.ports, - current.behaviors, - current.flavors); + task_get_exception_ports(current_task, + s_exception_mask, + current.masks, + ¤t.count, + current.ports, + current.behaviors, + current.flavors); // Find the first exception handler that matches the exception unsigned int found; @@ -435,48 +430,16 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, mach_port_t target_port = current.ports[found]; exception_behavior_t target_behavior = current.behaviors[found]; - thread_state_flavor_t target_flavor = current.flavors[found]; - mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; - breakpad_thread_state_data_t thread_state; + kern_return_t result; switch (target_behavior) { case EXCEPTION_DEFAULT: result = exception_raise(target_port, failed_thread, task, exception, code, code_count); break; - case EXCEPTION_STATE: - result = thread_get_state(failed_thread, target_flavor, thread_state, - &thread_state_count); - if (result == KERN_SUCCESS) - result = exception_raise_state(target_port, failed_thread, task, - exception, code, - code_count, &target_flavor, - thread_state, thread_state_count, - thread_state, &thread_state_count); - if (result == KERN_SUCCESS) - result = thread_set_state(failed_thread, target_flavor, thread_state, - thread_state_count); - break; - - case EXCEPTION_STATE_IDENTITY: - result = thread_get_state(failed_thread, target_flavor, thread_state, - &thread_state_count); - if (result == KERN_SUCCESS) - result = exception_raise_state_identity(target_port, failed_thread, - task, exception, code, - code_count, &target_flavor, - thread_state, - thread_state_count, - thread_state, - &thread_state_count); - if (result == KERN_SUCCESS) - result = thread_set_state(failed_thread, target_flavor, thread_state, - thread_state_count); - break; - default: - fprintf(stderr, "** Unknown exception behavior\n"); + fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior); result = KERN_FAILURE; break; } @@ -484,18 +447,6 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, return result; } -// Callback from exc_server() -kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, - mach_port_t task, - exception_type_t exception, - exception_data_t code, - mach_msg_type_number_t code_count) { - if (task != mach_task_self()) { - return KERN_FAILURE; - } - return ForwardException(task, failed_thread, exception, code, code_count); -} - // static void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { ExceptionHandler *self = @@ -534,7 +485,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { self->SuspendThreads(); #if USE_PROTECTED_ALLOCATIONS - if(gBreakpadAllocator) + if (gBreakpadAllocator) gBreakpadAllocator->Unprotect(); #endif @@ -548,6 +499,8 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { exception_code = EXC_I386_BPT; #elif defined (__ppc__) || defined (__ppc64__) exception_code = EXC_PPC_BREAKPOINT; +#elif defined (__arm__) + exception_code = EXC_ARM_BREAKPOINT; #else #error architecture not supported #endif @@ -557,10 +510,10 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { self->last_minidump_write_result_ = self->WriteMinidumpWithException(exception_type, exception_code, 0, thread, - false); + false, false); #if USE_PROTECTED_ALLOCATIONS - if(gBreakpadAllocator) + if (gBreakpadAllocator) gBreakpadAllocator->Protect(); #endif @@ -575,13 +528,13 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { // exceptions that occur in the parent process are caught and // processed. If the exception was not caused by this task, we // still need to call into the exception server and have it return - // KERN_FAILURE (see breakpad_exception_raise) in order for the kernel + // KERN_FAILURE (see catch_exception_raise) in order for the kernel // to move onto the host exception handler for the child task if (receive.task.name == mach_task_self()) { self->SuspendThreads(); #if USE_PROTECTED_ALLOCATIONS - if(gBreakpadAllocator) + if (gBreakpadAllocator) gBreakpadAllocator->Unprotect(); #endif @@ -591,34 +544,35 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { // Generate the minidump with the exception data. self->WriteMinidumpWithException(receive.exception, receive.code[0], - subcode, receive.thread.name, true); + subcode, receive.thread.name, true, + false); #if USE_PROTECTED_ALLOCATIONS // This may have become protected again within // WriteMinidumpWithException, but it needs to be unprotected for // UninstallHandler. - if(gBreakpadAllocator) + if (gBreakpadAllocator) gBreakpadAllocator->Unprotect(); #endif self->UninstallHandler(true); #if USE_PROTECTED_ALLOCATIONS - if(gBreakpadAllocator) + if (gBreakpadAllocator) gBreakpadAllocator->Protect(); #endif } // Pass along the exception to the server, which will setup the - // message and call breakpad_exception_raise() and put the return + // message and call catch_exception_raise() and put the return // code into the reply. ExceptionReplyMessage reply; - if (!exc_server(&receive.header, &reply.header)) + if (!breakpad_exc_server(&receive.header, &reply.header)) exit(1); // Send a reply and exit - result = mach_msg(&(reply.header), MACH_SEND_MSG, - reply.header.msgh_size, 0, MACH_PORT_NULL, - MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + mach_msg(&(reply.header), MACH_SEND_MSG, + reply.header.msgh_size, 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); } } } @@ -626,7 +580,52 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { return NULL; } +//static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + gProtectedData.handler->WriteMinidumpWithException( + EXC_SOFTWARE, + MD_EXCEPTION_CODE_MAC_ABORT, + 0, + mach_thread_self(), + true, + true); +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif +} + bool ExceptionHandler::InstallHandler() { + // If a handler is already installed, something is really wrong. + if (gProtectedData.handler != NULL) { + return false; + } +#if TARGET_OS_IPHONE + if (!IsOutOfProcess()) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGABRT); + sa.sa_sigaction = ExceptionHandler::SignalHandler; + sa.sa_flags = SA_SIGINFO; + + scoped_ptr old(new struct sigaction); + if (sigaction(SIGABRT, &sa, old.get()) == -1) { + return false; + } + old_handler_.swap(old); + gProtectedData.handler = this; +#if USE_PROTECTED_ALLOCATIONS + assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); +#endif + } +#endif + try { #if USE_PROTECTED_ALLOCATIONS previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) @@ -665,6 +664,16 @@ bool ExceptionHandler::InstallHandler() { bool ExceptionHandler::UninstallHandler(bool in_exception) { kern_return_t result = KERN_SUCCESS; + if (old_handler_.get()) { + sigaction(SIGABRT, old_handler_.get(), NULL); +#if USE_PROTECTED_ALLOCATIONS + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, + PROT_READ | PROT_WRITE); +#endif + old_handler_.reset(); + gProtectedData.handler = NULL; + } + if (installed_exception_handler_) { mach_port_t current_task = mach_task_self(); @@ -744,7 +753,7 @@ bool ExceptionHandler::Teardown() { } handler_thread_ = NULL; - handler_port_ = NULL; + handler_port_ = MACH_PORT_NULL; pthread_mutex_destroy(&minidump_write_mutex_); return result == KERN_SUCCESS; diff --git a/thirdparty/breakpad/client/mac/handler/exception_handler.h b/thirdparty/breakpad/client/mac/handler/exception_handler.h index 172dc3583..ec091341f 100644 --- a/thirdparty/breakpad/client/mac/handler/exception_handler.h +++ b/thirdparty/breakpad/client/mac/handler/exception_handler.h @@ -37,12 +37,16 @@ #define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ #include +#include #include -#include "client/mac/crash_generation/crash_generation_client.h" #include "processor/scoped_ptr.h" +#if !TARGET_OS_IPHONE +#include "client/mac/crash_generation/crash_generation_client.h" +#endif + namespace google_breakpad { using std::string; @@ -152,7 +156,11 @@ class ExceptionHandler { // Returns whether out-of-process dump generation is used or not. bool IsOutOfProcess() const { +#if TARGET_OS_IPHONE + return false; +#else return crash_generation_client_.get() != NULL; +#endif } private: @@ -179,12 +187,16 @@ class ExceptionHandler { int exception_code, int exception_subcode, mach_port_t thread_name, - bool exit_after_write); + bool exit_after_write, + bool report_current_thread); // When installed, this static function will be call from a newly created // pthread with |this| as the argument static void *WaitForMessage(void *exception_handler_class); + // Signal handler for SIGABRT. + static void SignalHandler(int sig, siginfo_t* info, void* uc); + // disallow copy ctor and operator= explicit ExceptionHandler(const ExceptionHandler &); void operator=(const ExceptionHandler &); @@ -250,8 +262,14 @@ class ExceptionHandler { // True, if we're using the mutext to indicate when mindump writing occurs bool use_minidump_write_mutex_; + // Old signal handler for SIGABRT. Used to be able to restore it when + // uninstalling. + scoped_ptr old_handler_; + +#if !TARGET_OS_IPHONE // Client for out-of-process dump generation. scoped_ptr crash_generation_client_; +#endif }; } // namespace google_breakpad diff --git a/thirdparty/breakpad/client/mac/handler/mach_vm_compat.h b/thirdparty/breakpad/client/mac/handler/mach_vm_compat.h new file mode 100644 index 000000000..e0459be67 --- /dev/null +++ b/thirdparty/breakpad/client/mac/handler/mach_vm_compat.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ +#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ + +#include + +// On iOS 5, mach/mach_vm.h is not supported anymore. As the architecture is 32 +// bits, we can use the simple vm_ functions instead of the mach_vm_ ones. +#if TARGET_OS_IPHONE +#include +#define mach_vm_address_t vm_address_t +#define mach_vm_deallocate vm_deallocate +#define mach_vm_read vm_read +#define mach_vm_region vm_region +#define mach_vm_region_recurse vm_region_recurse +#define mach_vm_size_t vm_size_t +#else +#include +#endif // TARGET_OS_IPHONE + +#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ diff --git a/thirdparty/breakpad/client/mac/handler/minidump_generator.cc b/thirdparty/breakpad/client/mac/handler/minidump_generator.cc index a37e73c55..b1d429cfa 100644 --- a/thirdparty/breakpad/client/mac/handler/minidump_generator.cc +++ b/thirdparty/breakpad/client/mac/handler/minidump_generator.cc @@ -31,8 +31,6 @@ #include #include -#include -#include #include #include #include @@ -43,12 +41,19 @@ #include "client/mac/handler/minidump_generator.h" +#ifdef HAS_ARM_SUPPORT +#include +#endif #ifdef HAS_PPC_SUPPORT #include #endif +#ifdef HAS_X86_SUPPORT +#include +#endif #include "client/minidump_file_writer-inl.h" #include "common/mac/file_id.h" +#include "common/mac/macho_id.h" #include "common/mac/string_utilities.h" using MacStringUtils::ConvertToString; @@ -130,14 +135,19 @@ void MinidumpGenerator::GatherSystemInformation() { CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL, &error); - if (!data) + if (!data) { + CFRelease(sys_vers); return; + } CFDictionaryRef list = static_cast (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, NULL)); - if (!list) + if (!list) { + CFRelease(sys_vers); + CFRelease(data); return; + } CFStringRef build_version = static_cast (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); @@ -348,16 +358,22 @@ bool MinidumpGenerator::WriteStackFromStartAddress( bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location) { switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteStackARM(state, stack_location); +#endif #ifdef HAS_PPC_SUPPORT case CPU_TYPE_POWERPC: return WriteStackPPC(state, stack_location); case CPU_TYPE_POWERPC64: return WriteStackPPC64(state, stack_location); #endif +#ifdef HAS_X86_SUPPORT case CPU_TYPE_I386: return WriteStackX86(state, stack_location); case CPU_TYPE_X86_64: return WriteStackX86_64(state, stack_location); +#endif default: return false; } @@ -366,16 +382,22 @@ bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, MDLocationDescriptor *register_location) { switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteContextARM(state, register_location); +#endif #ifdef HAS_PPC_SUPPORT case CPU_TYPE_POWERPC: return WriteContextPPC(state, register_location); case CPU_TYPE_POWERPC64: return WriteContextPPC64(state, register_location); #endif +#ifdef HAS_X86_SUPPORT case CPU_TYPE_I386: return WriteContextX86(state, register_location); case CPU_TYPE_X86_64: return WriteContextX86_64(state, register_location); +#endif default: return false; } @@ -384,22 +406,86 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, u_int64_t MinidumpGenerator::CurrentPCForStack( breakpad_thread_state_data_t state) { switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return CurrentPCForStackARM(state); +#endif #ifdef HAS_PPC_SUPPORT case CPU_TYPE_POWERPC: return CurrentPCForStackPPC(state); case CPU_TYPE_POWERPC64: return CurrentPCForStackPPC64(state); #endif +#ifdef HAS_X86_SUPPORT case CPU_TYPE_I386: return CurrentPCForStackX86(state); case CPU_TYPE_X86_64: return CurrentPCForStackX86_64(state); +#endif default: assert("Unknown CPU type!"); return 0; } } +#ifdef HAS_ARM_SUPPORT +bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + arm_thread_state_t *machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) { + arm_thread_state_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, pc); +} + +bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) +{ + TypedMDRVA context(&writer_); + arm_thread_state_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextARM *context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_ARM_FULL; + +#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a]) + + context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp); + context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr); + context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc); + context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); +#undef AddReg +#undef AddGPR + + return true; +} +#endif + #ifdef HAS_PCC_SUPPORT bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location) { @@ -560,6 +646,7 @@ bool MinidumpGenerator::WriteContextPPC64( #endif +#ifdef HAS_X86_SUPPORT bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location) { i386_thread_state_t *machine_state = @@ -678,12 +765,18 @@ bool MinidumpGenerator::WriteContextX86_64( return true; } +#endif 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_ARM_SUPPORT + case CPU_TYPE_ARM: + flavor = ARM_THREAD_STATE; + break; +#endif #ifdef HAS_PPC_SUPPORT case CPU_TYPE_POWERPC: flavor = PPC_THREAD_STATE; @@ -692,12 +785,14 @@ bool MinidumpGenerator::GetThreadState(thread_act_t target_thread, flavor = PPC_THREAD_STATE64; break; #endif +#ifdef HAS_X86_SUPPORT case CPU_TYPE_I386: flavor = i386_THREAD_STATE; break; case CPU_TYPE_X86_64: flavor = x86_THREAD_STATE64; break; +#endif default: return false; } @@ -927,10 +1022,18 @@ bool MinidumpGenerator::WriteSystemInfoStream( MDRawSystemInfo *info_ptr = info.get(); switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM; + break; +#endif +#ifdef HAS_PPC_SUPPORT case CPU_TYPE_POWERPC: case CPU_TYPE_POWERPC64: info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; break; +#endif +#ifdef HAS_X86_SUPPORT case CPU_TYPE_I386: case CPU_TYPE_X86_64: if (cpu_type_ == CPU_TYPE_I386) @@ -994,13 +1097,18 @@ bool MinidumpGenerator::WriteSystemInfoStream( #endif // __i386__ || __x86_64_ break; +#endif // HAS_X86_SUPPORT default: info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; break; } info_ptr->number_of_processors = number_of_processors; +#if TARGET_OS_IPHONE + info_ptr->platform_id = MD_OS_IOS; +#else info_ptr->platform_id = MD_OS_MAC_OS_X; +#endif // TARGET_OS_IPHONE MDLocationDescriptor build_string_loc; @@ -1057,7 +1165,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index, module->version_info.file_version_lo |= (modVersion & 0xff); } - if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) { + if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) { return false; } } else { @@ -1103,7 +1211,11 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index, module->size_of_image = static_cast(seg->vmsize); module->module_name_rva = string_location.rva; - if (!WriteCVRecord(module, cpu_type, name)) + bool in_memory = false; +#if TARGET_OS_IPHONE + in_memory = true; +#endif + if (!WriteCVRecord(module, cpu_type, name, in_memory)) return false; return true; @@ -1141,7 +1253,7 @@ int MinidumpGenerator::FindExecutableModule() { } bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, - const char *module_path) { + const char *module_path, bool in_memory) { TypedMDRVA cv(&writer_); // Only return the last path component of the full module path @@ -1167,10 +1279,23 @@ bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, cv_ptr->age = 0; // Get the module identifier - FileID file_id(module_path); unsigned char identifier[16]; + bool result = false; + if (in_memory) { + MacFileUtilities::MachoID macho(module_path, + reinterpret_cast(module->base_of_image), + static_cast(module->size_of_image)); + result = macho.UUIDCommand(cpu_type, identifier); + if (!result) + result = macho.MD5(cpu_type, identifier); + } - if (file_id.MachoIdentifier(cpu_type, identifier)) { + if (!result) { + FileID file_id(module_path); + result = file_id.MachoIdentifier(cpu_type, identifier); + } + + if (result) { cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | (uint32_t)identifier[3]; @@ -1193,8 +1318,9 @@ bool MinidumpGenerator::WriteModuleListStream( MDRawDirectory *module_list_stream) { TypedMDRVA list(&writer_); - int image_count = dynamic_images_ ? - dynamic_images_->GetImageCount() : _dyld_image_count(); + size_t image_count = dynamic_images_ ? + static_cast(dynamic_images_->GetImageCount()) : + _dyld_image_count(); if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) return false; @@ -1205,7 +1331,7 @@ bool MinidumpGenerator::WriteModuleListStream( // Write out the executable module as the first one MDRawModule module; - int executableIndex = FindExecutableModule(); + size_t executableIndex = FindExecutableModule(); if (!WriteModuleStream(executableIndex, &module)) { return false; @@ -1214,7 +1340,7 @@ bool MinidumpGenerator::WriteModuleListStream( list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); int destinationIndex = 1; // Write all other modules after this one - for (int i = 0; i < image_count; ++i) { + for (size_t i = 0; i < image_count; ++i) { if (i != executableIndex) { if (!WriteModuleStream(i, &module)) { return false; @@ -1257,19 +1383,11 @@ bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, static_cast(info_ptr->process_id) }; u_int mibsize = static_cast(sizeof(mib) / sizeof(mib[0])); - size_t size; - if (!sysctl(mib, mibsize, NULL, &size, NULL, 0)) { - mach_vm_address_t addr; - if (mach_vm_allocate(mach_task_self(), - &addr, - size, - true) == KERN_SUCCESS) { - struct kinfo_proc *proc = (struct kinfo_proc *)addr; - if (!sysctl(mib, mibsize, proc, &size, NULL, 0)) - info_ptr->process_create_time = - static_cast(proc->kp_proc.p_starttime.tv_sec); - mach_vm_deallocate(mach_task_self(), addr, size); - } + struct kinfo_proc proc; + size_t size = sizeof(proc); + if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) { + info_ptr->process_create_time = + static_cast(proc.kp_proc.p_starttime.tv_sec); } // Speed diff --git a/thirdparty/breakpad/client/mac/handler/minidump_generator.h b/thirdparty/breakpad/client/mac/handler/minidump_generator.h index 6f9fe8f76..80bb116c4 100644 --- a/thirdparty/breakpad/client/mac/handler/minidump_generator.h +++ b/thirdparty/breakpad/client/mac/handler/minidump_generator.h @@ -33,6 +33,7 @@ #define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ #include +#include #include @@ -42,10 +43,16 @@ #include "google_breakpad/common/minidump_format.h" #include "dynamic_images.h" +#include "mach_vm_compat.h" -#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 +#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) #define HAS_PPC_SUPPORT #endif +#if defined(__arm__) + #define HAS_ARM_SUPPORT +#elif defined(__i386__) || defined(__x86_64__) + #define HAS_X86_SUPPORT +#endif namespace google_breakpad { @@ -53,7 +60,7 @@ using std::string; // Use the REGISTER_FROM_THREADSTATE to access a register name from the // breakpad_thread_state_t structure. -#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 +#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM // In The 10.5 SDK Headers Apple prepended __ to the variable names in the // i386_thread_state_t structure. There's no good way to tell what version of // the SDK we're compiling against so we just toggle on the same preprocessor @@ -75,7 +82,7 @@ class MinidumpGenerator { MinidumpGenerator(); MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread); - ~MinidumpGenerator(); + virtual ~MinidumpGenerator(); // Return /.dmp // Sets |unique_name| (if requested) to the unique name for the minidump @@ -99,13 +106,19 @@ class MinidumpGenerator { // the MinidumpGenerator class. static void GatherSystemInformation(); + protected: + // Overridable Stream writers + virtual bool WriteExceptionStream(MDRawDirectory *exception_stream); + + // Overridable Helper + virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread); + private: - typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *); + typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *); // Stream writers bool WriteThreadListStream(MDRawDirectory *thread_list_stream); bool WriteMemoryListStream(MDRawDirectory *memory_list_stream); - bool WriteExceptionStream(MDRawDirectory *exception_stream); bool WriteSystemInfoStream(MDRawDirectory *system_info_stream); bool WriteModuleListStream(MDRawDirectory *module_list_stream); bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream); @@ -121,14 +134,20 @@ class MinidumpGenerator { MDMemoryDescriptor *stack_location); bool WriteContext(breakpad_thread_state_data_t state, MDLocationDescriptor *register_location); - bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread); - bool WriteCVRecord(MDRawModule *module, int cpu_type, - const char *module_path); + bool WriteCVRecord(MDRawModule *module, int cpu_type, + const char *module_path, bool in_memory); bool WriteModuleStream(unsigned int index, MDRawModule *module); size_t CalculateStackSize(mach_vm_address_t start_addr); int FindExecutableModule(); // Per-CPU implementations of these methods +#ifdef HAS_ARM_SUPPORT + bool WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state); +#endif #ifdef HAS_PPC_SUPPORT bool WriteStackPPC(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location); @@ -141,6 +160,7 @@ class MinidumpGenerator { MDLocationDescriptor *register_location); u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state); #endif +#ifdef HAS_X86_SUPPORT bool WriteStackX86(breakpad_thread_state_data_t state, MDMemoryDescriptor *stack_location); bool WriteContextX86(breakpad_thread_state_data_t state, @@ -151,14 +171,17 @@ class MinidumpGenerator { bool WriteContextX86_64(breakpad_thread_state_data_t state, MDLocationDescriptor *register_location); u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state); +#endif // disallow copy ctor and operator= explicit MinidumpGenerator(const MinidumpGenerator &); void operator=(const MinidumpGenerator &); + protected: // Use this writer to put the data to disk MinidumpFileWriter writer_; + private: // Exception information int exception_type_; int exception_code_; @@ -183,6 +206,7 @@ class MinidumpGenerator { // directly from the system, even while handling an exception. mutable PageAllocator allocator_; + protected: // Blocks of memory written to the dump. These are all currently // written while writing the thread list stream, but saved here // so a memory list stream can be written afterwards. diff --git a/thirdparty/breakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj b/thirdparty/breakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj index b140a71a2..2a597d502 100644 --- a/thirdparty/breakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj +++ b/thirdparty/breakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj @@ -72,7 +72,7 @@ F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; }; F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; }; F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; }; - F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.c */; }; + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; }; F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; }; F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; }; F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; }; @@ -157,7 +157,7 @@ F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; - F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; }; + F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; }; F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = ""; }; F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = ""; }; F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = ""; }; @@ -236,7 +236,7 @@ 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */, 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */, 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */, - F9721FA80E8B0E4800D7E813 /* md5.c */, + F9721FA80E8B0E4800D7E813 /* md5.cc */, F9721F760E8B0DC700D7E813 /* bytereader.cc */, F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */, F9721F780E8B0DC700D7E813 /* functioninfo.cc */, @@ -451,7 +451,14 @@ isa = PBXProject; buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */; compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */; projectDirPath = ""; projectRoot = ""; @@ -594,7 +601,7 @@ F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */, F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */, F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */, - F93A888B0E8B4C9A0026AF89 /* md5.c in Sources */, + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */, F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */, F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */, F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */, diff --git a/thirdparty/breakpad/client/mac/handler/testcases/DynamicImagesTests.cc b/thirdparty/breakpad/client/mac/handler/testcases/DynamicImagesTests.cc index cb76eb10e..0fc7825b2 100644 --- a/thirdparty/breakpad/client/mac/handler/testcases/DynamicImagesTests.cc +++ b/thirdparty/breakpad/client/mac/handler/testcases/DynamicImagesTests.cc @@ -55,21 +55,17 @@ void DynamicImagesTests::ReadTaskMemoryTest() { // pick test2 as a symbol we know to be valid to read // anything will work, really void *addr = reinterpret_cast(&test2); - void *buf; + std::vector buf(getpagesize()); fprintf(stderr, "reading 0x%p\n", addr); - buf = google_breakpad::ReadTaskMemory(mach_task_self(), - addr, - getpagesize(), - &kr); + kr = google_breakpad::ReadTaskMemory(mach_task_self(), + (uint64_t)addr, + getpagesize(), + buf); CPTAssert(kr == KERN_SUCCESS); - CPTAssert(buf != NULL); - - CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize())); - - free(buf); + CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize())); } void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() { @@ -79,7 +75,5 @@ void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() { fprintf(stderr,"Local task image count: %d\n", d->GetImageCount()); - d->TestPrint(); - CPTAssert(d->GetImageCount() > 0); } diff --git a/thirdparty/breakpad/client/mac/sender/crash_report_sender.h b/thirdparty/breakpad/client/mac/sender/crash_report_sender.h index c0728ca8c..6a29d48a1 100644 --- a/thirdparty/breakpad/client/mac/sender/crash_report_sender.h +++ b/thirdparty/breakpad/client/mac/sender/crash_report_sender.h @@ -32,17 +32,11 @@ // It will perform throttling based on the parameters passed to it and will // prompt the user to send the minidump. -#include +#import -#include "client/mac/Framework/Breakpad.h" +#include "client/mac/sender/uploader.h" #import "GTMDefines.h" -#define kClientIdPreferenceKey @"clientid" - -extern NSString *const kGoogleServerType; -extern NSString *const kSocorroServerType; -extern NSString *const kDefaultServerType; - // We're sublcassing NSTextField in order to override a particular // method (see the implementation) that lets us reject changes if they // are longer than a particular length. Bindings would normally solve @@ -87,29 +81,12 @@ extern NSString *const kDefaultServerType; NSString *countdownMessage_; // Message indicating time // left for input. @private - int configFile_; // File descriptor for config file - NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) - NSData *minidumpContents_; // The data in the minidump (STRONG) - NSData *logFileData_; // An NSdata for the tar, - // bz2'd log file. NSTimeInterval remainingDialogTime_; // Keeps track of how long // we have until we cancel // the dialog NSTimer *messageTimer_; // Timer we use to update // the dialog - NSMutableDictionary *serverDictionary_; // The dictionary mapping a - // server type name to a - // dictionary of server - // parameter names. - NSMutableDictionary *socorroDictionary_; // The dictionary for - // Socorro. - NSMutableDictionary *googleDictionary_; // The dictionary for - // Google. - NSMutableDictionary *extraServerVars_; // A dictionary containing - // extra key/value pairs - // that are uploaded to the - // crash server with the - // minidump. + Uploader* uploader_; // Uploader we use to send the data. } // Stops the modal panel with an NSAlertDefaultReturn value. This is the action diff --git a/thirdparty/breakpad/client/mac/sender/crash_report_sender.m b/thirdparty/breakpad/client/mac/sender/crash_report_sender.m index e38897610..c591f0ca3 100644 --- a/thirdparty/breakpad/client/mac/sender/crash_report_sender.m +++ b/thirdparty/breakpad/client/mac/sender/crash_report_sender.m @@ -27,31 +27,26 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#import -#import -#import +#import "client/mac/sender/crash_report_sender.h" #import +#import +#import #import +#import -#import "common/mac/HTTPMultipartUpload.h" - -#import "crash_report_sender.h" +#import "client/apple/Framework/BreakpadDefines.h" #import "common/mac/GTMLogger.h" +#import "common/mac/HTTPMultipartUpload.h" #define kLastSubmission @"LastSubmission" -const int kMinidumpFileLengthLimit = 800000; const int kUserCommentsMaxLength = 1500; const int kEmailMaxLength = 64; #define kApplePrefsSyncExcludeAllKey \ @"com.apple.PreferenceSync.ExcludeAllSyncKeys" -NSString *const kGoogleServerType = @"google"; -NSString *const kSocorroServerType = @"socorro"; -NSString *const kDefaultServerType = @"google"; - #pragma mark - @interface NSView (ResizabilityExtentions) @@ -160,18 +155,8 @@ NSString *const kDefaultServerType = @"google"; #pragma mark - - @interface Reporter(PrivateMethods) -+ (uid_t)consoleUID; - -- (id)initWithConfigurationFD:(int)fd; - -- (NSString *)readString; -- (NSData *)readData:(ssize_t)length; - -- (BOOL)readConfigurationData; -- (BOOL)readMinidumpData; -- (BOOL)readLogFileData; +- (id)initWithConfigFile:(const char *)configFile; // Returns YES if it has been long enough since the last report that we should // submit a report for this crash. @@ -221,30 +206,6 @@ NSString *const kDefaultServerType = @"google"; - (NSInteger)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; -// Returns a unique client id (user-specific), creating a persistent -// one in the user defaults, if necessary. -- (NSString*)clientID; - -// Returns a dictionary that can be used to map Breakpad parameter names to -// URL parameter names. -- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType; - -// Helper method to set HTTP parameters based on server type. This is -// called right before the upload - crashParameters will contain, on exit, -// URL parameters that should be sent with the minidump. -- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters; - -// Initialization helper to create dictionaries mapping Breakpad -// parameters to URL parameters -- (void)createServerParameterDictionaries; - -// Accessor method for the URL parameter dictionary -- (NSMutableDictionary *)urlParameterDictionary; - -// This method adds a key/value pair to the dictionary that -// will be uploaded to the crash server. -- (void)addServerParameter:(id)value forKey:(NSString *)key; - // This method is used to periodically update the UI with how many // seconds are left in the dialog display. - (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; @@ -255,291 +216,24 @@ NSString *const kDefaultServerType = @"google"; // in their comments/email. - (void)controlTextDidBeginEditing:(NSNotification *)aNotification; -// Records the uploaded crash ID to the log file. -- (void)logUploadWithID:(const char *)uploadID; +- (void)report; @end @implementation Reporter //============================================================================= -+ (uid_t)consoleUID { - SCDynamicStoreRef store = - SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL); - uid_t uid = -2; // Default to "nobody" - if (store) { - CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL); - - if (user) - CFRelease(user); - else - uid = -2; - - CFRelease(store); - } - - return uid; -} - -//============================================================================= -- (id)initWithConfigurationFD:(int)fd { +- (id)initWithConfigFile:(const char *)configFile { if ((self = [super init])) { - configFile_ = fd; remainingDialogTime_ = 0; + uploader_ = [[Uploader alloc] initWithConfigFile:configFile]; + if (!uploader_) { + [self release]; + return nil; + } } - - // Because the reporter is embedded in the framework (and many copies - // of the framework may exist) its not completely certain that the OS - // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our - // Info.plist. To make sure, also set the key directly if needed. - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { - [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; - } - - [self createServerParameterDictionaries]; - return self; } -//============================================================================= -- (NSString *)readString { - NSMutableString *str = [NSMutableString stringWithCapacity:32]; - char ch[2] = { 0 }; - - while (read(configFile_, &ch[0], 1) == 1) { - if (ch[0] == '\n') { - // Break if this is the first newline after reading some other string - // data. - if ([str length]) - break; - } else { - [str appendString:[NSString stringWithUTF8String:ch]]; - } - } - - return str; -} - -//============================================================================= -- (NSData *)readData:(ssize_t)length { - NSMutableData *data = [NSMutableData dataWithLength:length]; - char *bytes = (char *)[data bytes]; - - if (read(configFile_, bytes, length) != length) - return nil; - - return data; -} - -//============================================================================= -- (BOOL)readConfigurationData { - parameters_ = [[NSMutableDictionary alloc] init]; - - while (1) { - NSString *key = [self readString]; - - if (![key length]) - break; - - // Read the data. Try to convert to a UTF-8 string, or just save - // the data - NSString *lenStr = [self readString]; - ssize_t len = [lenStr intValue]; - NSData *data = [self readData:len]; - id value = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - - // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX - // that indicates that it should be uploaded to the server along - // with the minidump, so we treat it specially. - if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) { - NSString *urlParameterKey = - [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]]; - if ([urlParameterKey length]) { - if (value) { - [self addServerParameter:value - forKey:urlParameterKey]; - } else { - [self addServerParameter:data - forKey:urlParameterKey]; - } - } - } else { - [parameters_ setObject:(value ? value : data) forKey:key]; - } - [value release]; - } - - // generate a unique client ID based on this host's MAC address - // then add a key/value pair for it - NSString *clientID = [self clientID]; - [parameters_ setObject:clientID forKey:@"guid"]; - - close(configFile_); - configFile_ = -1; - - return YES; -} - -// Per user per machine -- (NSString *)clientID { - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; - if (crashClientID) { - return crashClientID; - } - - // Otherwise, if we have no client id, generate one! - srandom((int)[[NSDate date] timeIntervalSince1970]); - long clientId1 = random(); - long clientId2 = random(); - long clientId3 = random(); - crashClientID = [NSString stringWithFormat:@"%x%x%x", - clientId1, clientId2, clientId3]; - - [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; - [ud synchronize]; - return crashClientID; -} - -//============================================================================= -- (BOOL)readLogFileData { - unsigned int logFileCounter = 0; - - NSString *logPath; - size_t logFileTailSize = - [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue]; - - NSMutableArray *logFilenames; // An array of NSString, one per log file - logFilenames = [[NSMutableArray alloc] init]; - - char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; - char *tmpDir = mkdtemp(tmpDirTemplate); - - // Construct key names for the keys we expect to contain log file paths - for(logFileCounter = 0;; logFileCounter++) { - NSString *logFileKey = [NSString stringWithFormat:@"%@%d", - @BREAKPAD_LOGFILE_KEY_PREFIX, - logFileCounter]; - - logPath = [parameters_ objectForKey:logFileKey]; - - // They should all be consecutive, so if we don't find one, assume - // we're done - - if (!logPath) { - break; - } - - NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; - - if (entireLogFile == nil) { - continue; - } - - NSRange fileRange; - - // Truncate the log file, only if necessary - - if ([entireLogFile length] <= logFileTailSize) { - fileRange = NSMakeRange(0, [entireLogFile length]); - } else { - fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, - logFileTailSize); - } - - char tmpFilenameTemplate[100]; - - // Generate a template based on the log filename - sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, - [[logPath lastPathComponent] fileSystemRepresentation]); - - char *tmpFile = mktemp(tmpFilenameTemplate); - - NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; - NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; - [logSubdata writeToFile:tmpFileString atomically:NO]; - - [logFilenames addObject:[tmpFileString lastPathComponent]]; - [entireLogFile release]; - } - - if ([logFilenames count] == 0) { - [logFilenames release]; - logFileData_ = nil; - return NO; - } - - // now, bzip all files into one - NSTask *tarTask = [[NSTask alloc] init]; - - [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; - [tarTask setLaunchPath:@"/usr/bin/tar"]; - - NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", - @"log.tar.bz2",nil]; - [bzipArgs addObjectsFromArray:logFilenames]; - - [logFilenames release]; - - [tarTask setArguments:bzipArgs]; - [tarTask launch]; - [tarTask waitUntilExit]; - [tarTask release]; - - NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; - logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; - if (logFileData_ == nil) { - GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile); - return NO; - } - return YES; - -} - -//============================================================================= -- (BOOL)readMinidumpData { - NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; - NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; - - if (![minidumpID length]) - return NO; - - NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; - path = [path stringByAppendingPathExtension:@"dmp"]; - - // check the size of the minidump and limit it to a reasonable size - // before attempting to load into memory and upload - const char *fileName = [path fileSystemRepresentation]; - struct stat fileStatus; - - BOOL success = YES; - - if (!stat(fileName, &fileStatus)) { - if (fileStatus.st_size > kMinidumpFileLengthLimit) { - fprintf(stderr, "Breakpad Reporter: minidump file too large " \ - "to upload : %d\n", (int)fileStatus.st_size); - success = NO; - } - } else { - fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \ - "file length\n"); - success = NO; - } - - if (success) { - minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; - success = ([minidumpContents_ length] ? YES : NO); - } - - if (!success) { - // something wrong with the minidump file -- delete it - unlink(fileName); - } - - return success; -} - //============================================================================= - (BOOL)askUserPermissionToSend { // Initialize Cocoa, needed to display the alert @@ -560,12 +254,14 @@ NSString *const kDefaultServerType = @"google"; buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout]; - // Extract info from the user into the parameters_ dictionary + // Extract info from the user into the uploader_. if ([self commentsValue]) { - [parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS]; + [[uploader_ parameters] setObject:[self commentsValue] + forKey:@BREAKPAD_COMMENTS]; } if ([self emailValue]) { - [parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL]; + [[uploader_ parameters] setObject:[self emailValue] + forKey:@BREAKPAD_EMAIL]; } } else { // Create an alert panel to tell the user something happened @@ -804,9 +500,9 @@ doCommandBySelector:(SEL)commandSelector { #pragma mark - //============================================================================= - (BOOL)reportIntervalElapsed { - float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL] - floatValue]; - NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + float interval = [[[uploader_ parameters] + objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue]; + NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; NSMutableDictionary *programDict = [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]]; @@ -831,29 +527,30 @@ doCommandBySelector:(SEL)commandSelector { } - (BOOL)isOnDemand { - return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND] + return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND] isEqualToString:@"YES"]; } - (BOOL)shouldSubmitSilently { - return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] + return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM] isEqualToString:@"YES"]; } - (BOOL)shouldRequestComments { - return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS] + return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS] isEqualToString:@"YES"]; } - (BOOL)shouldRequestEmail { - return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL] + return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL] isEqualToString:@"YES"]; } - (NSString*)shortDialogMessage { - NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString *displayName = + [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; if (![displayName length]) - displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; if ([self isOnDemand]) { return [NSString @@ -867,11 +564,12 @@ doCommandBySelector:(SEL)commandSelector { } - (NSString*)explanatoryDialogText { - NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString *displayName = + [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; if (![displayName length]) - displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; - NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; + NSString *vendor = [[uploader_ parameters] objectForKey:@BREAKPAD_VENDOR]; if (![vendor length]) vendor = @"unknown vendor"; @@ -888,8 +586,8 @@ doCommandBySelector:(SEL)commandSelector { - (NSTimeInterval)messageTimeout { // Get the timeout value for the notification. - NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] - floatValue]; + NSTimeInterval timeout = [[[uploader_ parameters] + objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue]; // Require a timeout of at least a minute (except 0, which means no timeout). if (timeout > 0.001 && timeout < 60.0) { timeout = 60.0; @@ -897,194 +595,13 @@ doCommandBySelector:(SEL)commandSelector { return timeout; } -- (void)createServerParameterDictionaries { - serverDictionary_ = [[NSMutableDictionary alloc] init]; - socorroDictionary_ = [[NSMutableDictionary alloc] init]; - googleDictionary_ = [[NSMutableDictionary alloc] init]; - extraServerVars_ = [[NSMutableDictionary alloc] init]; - - [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType]; - [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType]; - - [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME]; - [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL]; - [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS]; - [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT]; - [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION]; - - [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS]; - [socorroDictionary_ setObject:@"CrashTime" - forKey:@BREAKPAD_PROCESS_CRASH_TIME]; - [socorroDictionary_ setObject:@"StartupTime" - forKey:@BREAKPAD_PROCESS_START_TIME]; - [socorroDictionary_ setObject:@"Version" - forKey:@BREAKPAD_VERSION]; - [socorroDictionary_ setObject:@"ProductName" - forKey:@BREAKPAD_PRODUCT]; - [socorroDictionary_ setObject:@"Email" - forKey:@BREAKPAD_EMAIL]; -} - -- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType { - if (serverType == nil || [serverType length] == 0) { - return [serverDictionary_ objectForKey:kDefaultServerType]; - } - return [serverDictionary_ objectForKey:serverType]; -} - -- (NSMutableDictionary *)urlParameterDictionary { - NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; - return [self dictionaryForServerType:serverType]; - -} - -- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters { - NSDictionary *urlParameterNames = [self urlParameterDictionary]; - - id key; - NSEnumerator *enumerator = [parameters_ keyEnumerator]; - - while ((key = [enumerator nextObject])) { - // The key from parameters_ corresponds to a key in - // urlParameterNames. The value in parameters_ gets stored in - // crashParameters with a key that is the value in - // urlParameterNames. - - // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and - // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP - // URL parameter becomes [pname => "FOOBAR"]. - NSString *breakpadParameterName = (NSString *)key; - NSString *urlParameter = [urlParameterNames - objectForKey:breakpadParameterName]; - if (urlParameter) { - [crashParameters setObject:[parameters_ objectForKey:key] - forKey:urlParameter]; - } - } - - // Now, add the parameters that were added by the application. - enumerator = [extraServerVars_ keyEnumerator]; - - while ((key = [enumerator nextObject])) { - NSString *urlParameterName = (NSString *)key; - NSString *urlParameterValue = - [extraServerVars_ objectForKey:urlParameterName]; - [crashParameters setObject:urlParameterValue - forKey:urlParameterName]; - } - return YES; -} - -- (void)addServerParameter:(id)value forKey:(NSString *)key { - [extraServerVars_ setObject:value forKey:key]; -} - -//============================================================================= - (void)report { - NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; - HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; - NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; - - if (![self populateServerDictionary:uploadParameters]) { - return; - } - - [upload setParameters:uploadParameters]; - - // Add minidump file - if (minidumpContents_) { - [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; - - // Send it - NSError *error = nil; - NSData *data = [upload send:&error]; - NSString *result = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - const char *reportID = "ERR"; - - if (error) { - fprintf(stderr, "Breakpad Reporter: Send Error: %s\n", - [[error description] UTF8String]); - } else { - NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; - reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; - [self logUploadWithID:reportID]; - } - - // rename the minidump file according to the id returned from the server - NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; - NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; - - NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", - minidumpDir, minidumpID]; - NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", - minidumpDir, reportID]; - - const char *src = [srcString fileSystemRepresentation]; - const char *dest = [destString fileSystemRepresentation]; - - if (rename(src, dest) == 0) { - GTMLoggerInfo(@"Breakpad Reporter: Renamed %s to %s after successful " \ - "upload",src, dest); - } - else { - // can't rename - don't worry - it's not important for users - GTMLoggerDebug(@"Breakpad Reporter: successful upload report ID = %s\n", - reportID ); - } - [result release]; - } - - if (logFileData_) { - HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url]; - - [uploadParameters setObject:@"log" forKey:@"type"]; - [logUpload setParameters:uploadParameters]; - [logUpload addFileContents:logFileData_ name:@"log"]; - - NSError *error = nil; - NSData *data = [logUpload send:&error]; - NSString *result = [[NSString alloc] initWithData:data - encoding:NSUTF8StringEncoding]; - [result release]; - [logUpload release]; - } - - [upload release]; -} - -- (void)logUploadWithID:(const char *)uploadID { - NSString *minidumpDir = - [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; - NSString *logFilePath = [NSString stringWithFormat:@"%@/%s", - minidumpDir, kReporterLogFilename]; - NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n", - [[NSDate date] timeIntervalSince1970], uploadID]; - NSData *logData = [logLine dataUsingEncoding:kCFStringEncodingUTF8]; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:logFilePath]) { - NSFileHandle *logFileHandle = - [NSFileHandle fileHandleForWritingAtPath:logFilePath]; - [logFileHandle seekToEndOfFile]; - [logFileHandle writeData:logData]; - [logFileHandle closeFile]; - } else { - [fileManager createFileAtPath:logFilePath - contents:logData - attributes:nil]; - } + [uploader_ report]; } //============================================================================= - (void)dealloc { - [parameters_ release]; - [minidumpContents_ release]; - [logFileData_ release]; - [googleDictionary_ release]; - [socorroDictionary_ release]; - [serverDictionary_ release]; - [extraServerVars_ release]; + [uploader_ release]; [super dealloc]; } @@ -1175,40 +692,12 @@ int main(int argc, const char *argv[]) { exit(1); } - // Open the file before (potentially) switching to console user - int configFile = open(argv[1], O_RDONLY, 0600); - - if (configFile == -1) { - GTMLoggerDebug(@"Couldn't open config file %s - %s", - argv[1], - strerror(errno)); - } - - // we want to avoid a build-up of old config files even if they - // have been incorrectly written by the framework - unlink(argv[1]); - - if (configFile == -1) { - GTMLoggerDebug(@"Couldn't unlink config file %s - %s", - argv[1], - strerror(errno)); + Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]]; + if (!reporter) { + GTMLoggerDebug(@"reporter initialization failed"); exit(1); } - Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile]; - - // Gather the configuration data - if (![reporter readConfigurationData]) { - GTMLoggerDebug(@"reporter readConfigurationData failed"); - exit(1); - } - - // Read the minidump into memory before we (potentially) switch from the - // root user - [reporter readMinidumpData]; - - [reporter readLogFileData]; - // only submit a report if we have not recently crashed in the past BOOL shouldSubmitReport = [reporter reportIntervalElapsed]; BOOL okayToSend = NO; diff --git a/thirdparty/breakpad/client/mac/sender/uploader.h b/thirdparty/breakpad/client/mac/sender/uploader.h new file mode 100644 index 000000000..318165c98 --- /dev/null +++ b/thirdparty/breakpad/client/mac/sender/uploader.h @@ -0,0 +1,81 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This component uses the HTTPMultipartUpload of the breakpad project to send +// the minidump and associated data to the crash reporting servers. +// It will perform throttling based on the parameters passed to it and will +// prompt the user to send the minidump. + +#include + +#import "common/mac/GTMDefines.h" + +#define kClientIdPreferenceKey @"clientid" + +extern NSString *const kGoogleServerType; +extern NSString *const kSocorroServerType; +extern NSString *const kDefaultServerType; + +@interface Uploader : NSObject { + @private + NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) + NSData *minidumpContents_; // The data in the minidump (STRONG) + NSData *logFileData_; // An NSdata for the tar, + // bz2'd log file. + NSMutableDictionary *serverDictionary_; // The dictionary mapping a + // server type name to a + // dictionary of server + // parameter names. + NSMutableDictionary *socorroDictionary_; // The dictionary for + // Socorro. + NSMutableDictionary *googleDictionary_; // The dictionary for + // Google. + NSMutableDictionary *extraServerVars_; // A dictionary containing + // extra key/value pairs + // that are uploaded to the + // crash server with the + // minidump. +} + +- (id)initWithConfigFile:(const char *)configFile; + +- (id)initWithConfig:(NSDictionary *)config; + +- (NSMutableDictionary *)parameters; + +- (void)report; + +// Upload the given data to the crash server. +- (void)uploadData:(NSData *)data name:(NSString *)name; + +// This method adds a key/value pair to the dictionary that +// will be uploaded to the crash server. +- (void)addServerParameter:(id)value forKey:(NSString *)key; + +@end diff --git a/thirdparty/breakpad/client/mac/sender/uploader.mm b/thirdparty/breakpad/client/mac/sender/uploader.mm new file mode 100644 index 000000000..03b4051b3 --- /dev/null +++ b/thirdparty/breakpad/client/mac/sender/uploader.mm @@ -0,0 +1,619 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import +#import +#include +#import + +#import + +#import "common/mac/HTTPMultipartUpload.h" + +#import "client/apple/Framework/BreakpadDefines.h" +#import "client/mac/sender/uploader.h" +#import "common/mac/GTMLogger.h" + +const int kMinidumpFileLengthLimit = 2 * 1024 * 1024; // 2MB + +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" + +NSString *const kGoogleServerType = @"google"; +NSString *const kSocorroServerType = @"socorro"; +NSString *const kDefaultServerType = @"google"; + +#pragma mark - + +namespace { +// Read one line from the configuration file. +NSString *readString(int fileId) { + NSMutableString *str = [NSMutableString stringWithCapacity:32]; + char ch[2] = { 0 }; + + while (read(fileId, &ch[0], 1) == 1) { + if (ch[0] == '\n') { + // Break if this is the first newline after reading some other string + // data. + if ([str length]) + break; + } else { + [str appendString:[NSString stringWithUTF8String:ch]]; + } + } + + return str; +} + +//============================================================================= +// Read |length| of binary data from the configuration file. This method will +// returns |nil| in case of error. +NSData *readData(int fileId, ssize_t length) { + NSMutableData *data = [NSMutableData dataWithLength:length]; + char *bytes = (char *)[data bytes]; + + if (read(fileId, bytes, length) != length) + return nil; + + return data; +} + +//============================================================================= +// Read the configuration from the config file. +NSDictionary *readConfigurationData(const char *configFile) { + int fileId = open(configFile, O_RDONLY, 0600); + if (fileId == -1) { + GTMLoggerDebug(@"Couldn't open config file %s - %s", + configFile, + strerror(errno)); + } + + // we want to avoid a build-up of old config files even if they + // have been incorrectly written by the framework + if (unlink(configFile)) { + GTMLoggerDebug(@"Couldn't unlink config file %s - %s", + configFile, + strerror(errno)); + } + + if (fileId == -1) { + return nil; + } + + NSMutableDictionary *config = [NSMutableDictionary dictionary]; + + while (1) { + NSString *key = readString(fileId); + + if (![key length]) + break; + + // Read the data. Try to convert to a UTF-8 string, or just save + // the data + NSString *lenStr = readString(fileId); + ssize_t len = [lenStr intValue]; + NSData *data = readData(fileId, len); + id value = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + [config setObject:(value ? value : data) forKey:key]; + [value release]; + } + + close(fileId); + return config; +} +} // namespace + +#pragma mark - + +@interface Uploader(PrivateMethods) + +// Update |parameters_| as well as the server parameters using |config|. +- (void)translateConfigurationData:(NSDictionary *)config; + +// Read the minidump referenced in |parameters_| and update |minidumpContents_| +// with its content. +- (BOOL)readMinidumpData; + +// Read the log files referenced in |parameters_| and update |logFileData_| +// with their content. +- (BOOL)readLogFileData; + +// Returns a unique client id (user-specific), creating a persistent +// one in the user defaults, if necessary. +- (NSString*)clientID; + +// Returns a dictionary that can be used to map Breakpad parameter names to +// URL parameter names. +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType; + +// Helper method to set HTTP parameters based on server type. This is +// called right before the upload - crashParameters will contain, on exit, +// URL parameters that should be sent with the minidump. +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters; + +// Initialization helper to create dictionaries mapping Breakpad +// parameters to URL parameters +- (void)createServerParameterDictionaries; + +// Accessor method for the URL parameter dictionary +- (NSMutableDictionary *)urlParameterDictionary; + +// Records the uploaded crash ID to the log file. +- (void)logUploadWithID:(const char *)uploadID; +@end + +@implementation Uploader + +//============================================================================= +- (id)initWithConfigFile:(const char *)configFile { + NSDictionary *config = readConfigurationData(configFile); + if (!config) + return nil; + + return [self initWithConfig:config]; +} + +//============================================================================= +- (id)initWithConfig:(NSDictionary *)config { + if ((self = [super init])) { + // Because the reporter is embedded in the framework (and many copies + // of the framework may exist) its not completely certain that the OS + // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our + // Info.plist. To make sure, also set the key directly if needed. + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { + [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; + } + + [self createServerParameterDictionaries]; + + [self translateConfigurationData:config]; + + // Read the minidump into memory. + [self readMinidumpData]; + [self readLogFileData]; + } + return self; +} + +//============================================================================= +- (void)translateConfigurationData:(NSDictionary *)config { + parameters_ = [[NSMutableDictionary alloc] init]; + + NSEnumerator *it = [config keyEnumerator]; + while (NSString *key = [it nextObject]) { + // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX + // that indicates that it should be uploaded to the server along + // with the minidump, so we treat it specially. + if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) { + NSString *urlParameterKey = + [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]]; + if ([urlParameterKey length]) { + id value = [config objectForKey:key]; + if ([value isKindOfClass:[NSString class]]) { + [self addServerParameter:(NSString *)value + forKey:urlParameterKey]; + } else { + [self addServerParameter:(NSData *)value + forKey:urlParameterKey]; + } + } + } else { + [parameters_ setObject:[config objectForKey:key] forKey:key]; + } + } + + // generate a unique client ID based on this host's MAC address + // then add a key/value pair for it + NSString *clientID = [self clientID]; + [parameters_ setObject:clientID forKey:@"guid"]; +} + +// Per user per machine +- (NSString *)clientID { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; + if (crashClientID) { + return crashClientID; + } + + // Otherwise, if we have no client id, generate one! + srandom((int)[[NSDate date] timeIntervalSince1970]); + long clientId1 = random(); + long clientId2 = random(); + long clientId3 = random(); + crashClientID = [NSString stringWithFormat:@"%lx%lx%lx", + clientId1, clientId2, clientId3]; + + [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; + [ud synchronize]; + return crashClientID; +} + +//============================================================================= +- (BOOL)readLogFileData { +#if TARGET_OS_IPHONE + return NO; +#else + unsigned int logFileCounter = 0; + + NSString *logPath; + size_t logFileTailSize = + [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue]; + + NSMutableArray *logFilenames; // An array of NSString, one per log file + logFilenames = [[NSMutableArray alloc] init]; + + char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; + char *tmpDir = mkdtemp(tmpDirTemplate); + + // Construct key names for the keys we expect to contain log file paths + for(logFileCounter = 0;; logFileCounter++) { + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + logPath = [parameters_ objectForKey:logFileKey]; + + // They should all be consecutive, so if we don't find one, assume + // we're done + + if (!logPath) { + break; + } + + NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; + + if (entireLogFile == nil) { + continue; + } + + NSRange fileRange; + + // Truncate the log file, only if necessary + + if ([entireLogFile length] <= logFileTailSize) { + fileRange = NSMakeRange(0, [entireLogFile length]); + } else { + fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, + logFileTailSize); + } + + char tmpFilenameTemplate[100]; + + // Generate a template based on the log filename + sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, + [[logPath lastPathComponent] fileSystemRepresentation]); + + char *tmpFile = mktemp(tmpFilenameTemplate); + + NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; + NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; + [logSubdata writeToFile:tmpFileString atomically:NO]; + + [logFilenames addObject:[tmpFileString lastPathComponent]]; + [entireLogFile release]; + } + + if ([logFilenames count] == 0) { + [logFilenames release]; + logFileData_ = nil; + return NO; + } + + // now, bzip all files into one + NSTask *tarTask = [[NSTask alloc] init]; + + [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; + [tarTask setLaunchPath:@"/usr/bin/tar"]; + + NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", + @"log.tar.bz2",nil]; + [bzipArgs addObjectsFromArray:logFilenames]; + + [logFilenames release]; + + [tarTask setArguments:bzipArgs]; + [tarTask launch]; + [tarTask waitUntilExit]; + [tarTask release]; + + NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; + logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; + if (logFileData_ == nil) { + GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile); + return NO; + } + return YES; +#endif // TARGET_OS_IPHONE +} + +//============================================================================= +- (BOOL)readMinidumpData { + NSString *minidumpDir = + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + if (![minidumpID length]) + return NO; + + NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; + path = [path stringByAppendingPathExtension:@"dmp"]; + + // check the size of the minidump and limit it to a reasonable size + // before attempting to load into memory and upload + const char *fileName = [path fileSystemRepresentation]; + struct stat fileStatus; + + BOOL success = YES; + + if (!stat(fileName, &fileStatus)) { + if (fileStatus.st_size > kMinidumpFileLengthLimit) { + fprintf(stderr, "Breakpad Uploader: minidump file too large " \ + "to upload : %d\n", (int)fileStatus.st_size); + success = NO; + } + } else { + fprintf(stderr, "Breakpad Uploader: unable to determine minidump " \ + "file length\n"); + success = NO; + } + + if (success) { + minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; + success = ([minidumpContents_ length] ? YES : NO); + } + + if (!success) { + // something wrong with the minidump file -- delete it + unlink(fileName); + } + + return success; +} + +#pragma mark - +//============================================================================= + +- (void)createServerParameterDictionaries { + serverDictionary_ = [[NSMutableDictionary alloc] init]; + socorroDictionary_ = [[NSMutableDictionary alloc] init]; + googleDictionary_ = [[NSMutableDictionary alloc] init]; + extraServerVars_ = [[NSMutableDictionary alloc] init]; + + [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType]; + [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType]; + + [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME]; + [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL]; + [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS]; + [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT]; + [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION]; + [googleDictionary_ setObject:@"guid" forKey:@"guid"]; + + [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS]; + [socorroDictionary_ setObject:@"CrashTime" + forKey:@BREAKPAD_PROCESS_CRASH_TIME]; + [socorroDictionary_ setObject:@"StartupTime" + forKey:@BREAKPAD_PROCESS_START_TIME]; + [socorroDictionary_ setObject:@"Version" + forKey:@BREAKPAD_VERSION]; + [socorroDictionary_ setObject:@"ProductName" + forKey:@BREAKPAD_PRODUCT]; + [socorroDictionary_ setObject:@"Email" + forKey:@BREAKPAD_EMAIL]; +} + +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType { + if (serverType == nil || [serverType length] == 0) { + return [serverDictionary_ objectForKey:kDefaultServerType]; + } + return [serverDictionary_ objectForKey:serverType]; +} + +- (NSMutableDictionary *)urlParameterDictionary { + NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; + return [self dictionaryForServerType:serverType]; + +} + +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters { + NSDictionary *urlParameterNames = [self urlParameterDictionary]; + + id key; + NSEnumerator *enumerator = [parameters_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + // The key from parameters_ corresponds to a key in + // urlParameterNames. The value in parameters_ gets stored in + // crashParameters with a key that is the value in + // urlParameterNames. + + // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and + // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP + // URL parameter becomes [pname => "FOOBAR"]. + NSString *breakpadParameterName = (NSString *)key; + NSString *urlParameter = [urlParameterNames + objectForKey:breakpadParameterName]; + if (urlParameter) { + [crashParameters setObject:[parameters_ objectForKey:key] + forKey:urlParameter]; + } + } + + // Now, add the parameters that were added by the application. + enumerator = [extraServerVars_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + NSString *urlParameterName = (NSString *)key; + NSString *urlParameterValue = + [extraServerVars_ objectForKey:urlParameterName]; + [crashParameters setObject:urlParameterValue + forKey:urlParameterName]; + } + return YES; +} + +- (void)addServerParameter:(id)value forKey:(NSString *)key { + [extraServerVars_ setObject:value forKey:key]; +} + +//============================================================================= +- (void)report { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + if (![self populateServerDictionary:uploadParameters]) { + [upload release]; + return; + } + + [upload setParameters:uploadParameters]; + + // Add minidump file + if (minidumpContents_) { + [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; + + // If there is a log file, upload it together with the minidump. + if (logFileData_) { + [upload addFileContents:logFileData_ name:@"log"]; + } + + // Send it + NSError *error = nil; + NSData *data = [upload send:&error]; + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + const char *reportID = "ERR"; + + if (error) { + fprintf(stderr, "Breakpad Uploader: Send Error: %s\n", + [[error description] UTF8String]); + } else { + NSCharacterSet *trimSet = + [NSCharacterSet whitespaceAndNewlineCharacterSet]; + reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; + [self logUploadWithID:reportID]; + } + + // rename the minidump file according to the id returned from the server + NSString *minidumpDir = + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", + minidumpDir, minidumpID]; + NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", + minidumpDir, reportID]; + + const char *src = [srcString fileSystemRepresentation]; + const char *dest = [destString fileSystemRepresentation]; + + if (rename(src, dest) == 0) { + GTMLoggerInfo(@"Breakpad Uploader: Renamed %s to %s after successful " \ + "upload",src, dest); + } + else { + // can't rename - don't worry - it's not important for users + GTMLoggerDebug(@"Breakpad Uploader: successful upload report ID = %s\n", + reportID ); + } + [result release]; + } else { + // Minidump is missing -- upload just the log file. + if (logFileData_) { + [self uploadData:logFileData_ name:@"log"]; + } + } + [upload release]; +} + +- (void)uploadData:(NSData *)data name:(NSString *)name { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + if (![self populateServerDictionary:uploadParameters]) + return; + + HTTPMultipartUpload *upload = + [[HTTPMultipartUpload alloc] initWithURL:url]; + + [uploadParameters setObject:name forKey:@"type"]; + [upload setParameters:uploadParameters]; + [upload addFileContents:data name:name]; + + [upload send:nil]; + [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:NSUTF8StringEncoding]; + + 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]; + } +} + +//============================================================================= +- (NSMutableDictionary *)parameters { + return parameters_; +} + +//============================================================================= +- (void)dealloc { + [parameters_ release]; + [minidumpContents_ release]; + [logFileData_ release]; + [googleDictionary_ release]; + [socorroDictionary_ release]; + [serverDictionary_ release]; + [extraServerVars_ release]; + [super dealloc]; +} + +@end diff --git a/thirdparty/breakpad/client/mac/tests/crash_generation_server_test.cc b/thirdparty/breakpad/client/mac/tests/crash_generation_server_test.cc index fdb9cdd1b..f71624dff 100644 --- a/thirdparty/breakpad/client/mac/tests/crash_generation_server_test.cc +++ b/thirdparty/breakpad/client/mac/tests/crash_generation_server_test.cc @@ -43,8 +43,8 @@ #include "client/mac/crash_generation/crash_generation_client.h" #include "client/mac/crash_generation/crash_generation_server.h" #include "client/mac/handler/exception_handler.h" -#include "client/mac/tests/auto_tempdir.h" #include "client/mac/tests/spawn_child_process.h" +#include "common/tests/auto_tempdir.h" #include "google_breakpad/processor/minidump.h" namespace google_breakpad { @@ -111,12 +111,12 @@ TEST_F(CrashGenerationServerTest, testStartStopServer) { // Test without actually dumping TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) { CrashGenerationServer server(mach_port_name, - NULL, // dump callback - NULL, // dump context - NULL, // exit callback - NULL, // exit context - false, // don't generate dumps - temp_dir.path); // dump path + NULL, // dump callback + NULL, // dump context + NULL, // exit callback + NULL, // exit context + false, // don't generate dumps + temp_dir.path()); // dump path ASSERT_TRUE(server.Start()); pid_t pid = fork(); @@ -133,7 +133,7 @@ TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) { EXPECT_EQ(0, WEXITSTATUS(ret)); EXPECT_TRUE(server.Stop()); // check that no minidump was written - string pattern = temp_dir.path + "/*"; + string pattern = temp_dir.path() + "/*"; glob_t dirContents; ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents); EXPECT_EQ(GLOB_NOMATCH, ret); @@ -161,12 +161,12 @@ void *RequestDump(void *context) { // Test that actually writing a minidump works TEST_F(CrashGenerationServerTest, testRequestDump) { CrashGenerationServer server(mach_port_name, - dumpCallback, // dump callback - this, // dump context - NULL, // exit callback - NULL, // exit context - true, // generate dumps - temp_dir.path); // dump path + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path ASSERT_TRUE(server.Start()); pid_t pid = fork(); @@ -209,12 +209,12 @@ static void Crasher() { // the parent. TEST_F(CrashGenerationServerTest, testChildProcessCrash) { CrashGenerationServer server(mach_port_name, - dumpCallback, // dump callback - this, // dump context - NULL, // exit callback - NULL, // exit context - true, // generate dumps - temp_dir.path); // dump path + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path ASSERT_TRUE(server.Start()); pid_t pid = fork(); @@ -270,12 +270,12 @@ TEST_F(CrashGenerationServerTest, testChildProcessCrash) { // produces a valid minidump. TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) { CrashGenerationServer server(mach_port_name, - dumpCallback, // dump callback - this, // dump context - NULL, // exit callback - NULL, // exit context - true, // generate dumps - temp_dir.path); // dump path + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path ASSERT_TRUE(server.Start()); // Spawn a child process diff --git a/thirdparty/breakpad/client/mac/tests/exception_handler_test.cc b/thirdparty/breakpad/client/mac/tests/exception_handler_test.cc index b1b74ef1e..ee3ffa5b3 100644 --- a/thirdparty/breakpad/client/mac/tests/exception_handler_test.cc +++ b/thirdparty/breakpad/client/mac/tests/exception_handler_test.cc @@ -36,8 +36,8 @@ #include "breakpad_googletest_includes.h" #include "client/mac/handler/exception_handler.h" -#include "client/mac/tests/auto_tempdir.h" #include "common/mac/MachIPC.h" +#include "common/tests/auto_tempdir.h" #include "google_breakpad/processor/minidump.h" namespace google_breakpad { @@ -64,6 +64,7 @@ using testing::Test; class ExceptionHandlerTest : public Test { public: + void InProcessCrash(bool aborting); AutoTempDir tempDir; string lastDumpName; }; @@ -75,8 +76,13 @@ static void Crasher() { fprintf(stdout, "A = %d", *a); } -static void SoonToCrash() { - Crasher(); +static void AbortCrasher() { + fprintf(stdout, "Going to crash...\n"); + abort(); +} + +static void SoonToCrash(void(*crasher)()) { + crasher(); } static bool MDCallback(const char *dump_dir, const char *file_name, @@ -94,7 +100,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name, return true; } -TEST_F(ExceptionHandlerTest, InProcess) { +void ExceptionHandlerTest::InProcessCrash(bool aborting) { // Give the child process a pipe to report back on. int fds[2]; ASSERT_EQ(0, pipe(fds)); @@ -103,9 +109,9 @@ TEST_F(ExceptionHandlerTest, InProcess) { if (pid == 0) { // In the child process. close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // crash - SoonToCrash(); + SoonToCrash(aborting ? &AbortCrasher : &Crasher); // not reached exit(1); } @@ -128,6 +134,16 @@ TEST_F(ExceptionHandlerTest, InProcess) { EXPECT_EQ(0, WEXITSTATUS(ret)); } +TEST_F(ExceptionHandlerTest, InProcess) { + InProcessCrash(false); +} + +#if TARGET_OS_IPHONE +TEST_F(ExceptionHandlerTest, InProcessAbort) { + InProcessCrash(true); +} +#endif + static bool DumpNameMDCallback(const char *dump_dir, const char *file_name, void *context, bool success) { ExceptionHandlerTest *self = reinterpret_cast(context); @@ -141,7 +157,8 @@ static bool DumpNameMDCallback(const char *dump_dir, const char *file_name, } TEST_F(ExceptionHandlerTest, WriteMinidump) { - ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, + NULL); ASSERT_TRUE(eh.WriteMinidump()); // Ensure that minidump file exists and is > 0 bytes. @@ -159,7 +176,8 @@ TEST_F(ExceptionHandlerTest, WriteMinidump) { } TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) { - ExceptionHandler eh(tempDir.path, NULL, DumpNameMDCallback, this, true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, + NULL); ASSERT_TRUE(eh.WriteMinidump(true)); // Ensure that minidump file exists and is > 0 bytes. @@ -227,10 +245,10 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) { // Write a minidump of the child process. bool result = ExceptionHandler::WriteMinidumpForChild(child_task, - child_thread, - tempDir.path, - DumpNameMDCallback, - this); + child_thread, + tempDir.path(), + DumpNameMDCallback, + this); ASSERT_EQ(true, result); // Ensure that minidump file exists and is > 0 bytes. @@ -267,7 +285,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemory) { pid_t pid = fork(); if (pid == 0) { close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -379,7 +397,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { pid_t pid = fork(); if (pid == 0) { close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -491,7 +509,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { pid_t pid = fork(); if (pid == 0) { close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // Get some executable memory. char* memory = reinterpret_cast(mmap(NULL, @@ -594,7 +612,7 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { pid_t pid = fork(); if (pid == 0) { close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // Try calling a NULL pointer. typedef void (*void_function)(void); void_function memory_function = @@ -651,7 +669,7 @@ TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) { pid_t pid = fork(); if (pid == 0) { close(fds[0]); - ExceptionHandler eh(tempDir.path, NULL, MDCallback, &fds[1], true, NULL); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); // Run an extra thread so >2 memory regions will be written. pthread_t junk_thread; diff --git a/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc b/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc index f9cf75aff..b3f8f0a1e 100644 --- a/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc +++ b/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc @@ -41,9 +41,9 @@ #include "breakpad_googletest_includes.h" #include "client/mac/handler/minidump_generator.h" -#include "client/mac/tests/auto_tempdir.h" #include "client/mac/tests/spawn_child_process.h" #include "common/mac/MachIPC.h" +#include "common/tests/auto_tempdir.h" #include "google_breakpad/processor/minidump.h" namespace google_breakpad { @@ -88,8 +88,8 @@ static void *Junk(void* data) { TEST_F(MinidumpGeneratorTest, InProcess) { MinidumpGenerator generator; - string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path, - NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); // Run an extra thread since MinidumpGenerator assumes there // are 2 or more threads. @@ -179,8 +179,8 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) { // Write a minidump of the child process. MinidumpGenerator generator(child_task, MACH_PORT_NULL); - string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path, - NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); ASSERT_TRUE(generator.Write(dump_filename.c_str())); // Ensure that minidump file exists and is > 0 bytes. @@ -258,8 +258,8 @@ TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) { // Write a minidump of the child process. MinidumpGenerator generator(child_task, MACH_PORT_NULL); - string dump_filename = MinidumpGenerator::UniqueNameInDirectory(tempDir.path, - NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); ASSERT_TRUE(generator.Write(dump_filename.c_str())); // Ensure that minidump file exists and is > 0 bytes. diff --git a/thirdparty/breakpad/client/windows/breakpad_client.gyp b/thirdparty/breakpad/client/windows/breakpad_client.gyp old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/build/common.gypi b/thirdparty/breakpad/client/windows/build/common.gypi old mode 100755 new mode 100644 index fb4e6d89b..2860224a0 --- a/thirdparty/breakpad/client/windows/build/common.gypi +++ b/thirdparty/breakpad/client/windows/build/common.gypi @@ -191,10 +191,6 @@ # Currently ignored on Windows. 'coverage%': 0, - # Overridable specification for potential use of alternative - # JavaScript engines. - 'javascript_engine%': 'v8', - # Although base/allocator lets you select a heap library via an # environment variable, the libcmt shim it uses sometimes gets in # the way. To disable it entirely, and switch to normal msvcrt, do e.g. @@ -272,7 +268,7 @@ # Enable new NPDevice API. 'enable_new_npdevice_api%': 0, - + 'conditions': [ ['OS=="linux" or OS=="freebsd" or OS=="openbsd"', { # This will set gcc_version to XY if you are running gcc X.Y.*. diff --git a/thirdparty/breakpad/client/windows/build/external_code.gypi b/thirdparty/breakpad/client/windows/build/external_code.gypi old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/build/internal/release_defaults.gypi b/thirdparty/breakpad/client/windows/build/internal/release_defaults.gypi old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/build/internal/release_impl.gypi b/thirdparty/breakpad/client/windows/build/internal/release_impl.gypi old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/build/internal/release_impl_official.gypi b/thirdparty/breakpad/client/windows/build/internal/release_impl_official.gypi old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/build/release.gypi b/thirdparty/breakpad/client/windows/build/release.gypi old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/common/auto_critical_section.h b/thirdparty/breakpad/client/windows/common/auto_critical_section.h index 82c7b7f14..40287427f 100644 --- a/thirdparty/breakpad/client/windows/common/auto_critical_section.h +++ b/thirdparty/breakpad/client/windows/common/auto_critical_section.h @@ -40,13 +40,30 @@ class AutoCriticalSection { public: // Creates a new instance with the given critical section object // and enters the critical section immediately. - explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs) { + explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs), taken_(false) { assert(cs_); - EnterCriticalSection(cs_); + Acquire(); } // Destructor: leaves the critical section. ~AutoCriticalSection() { + if (taken_) { + Release(); + } + } + + // Enters the critical section. Recursive Acquire() calls are not allowed. + void Acquire() { + assert(!taken_); + EnterCriticalSection(cs_); + taken_ = true; + } + + // Leaves the critical section. The caller should not call Release() unless + // the critical seciton has been entered already. + void Release() { + assert(taken_); + taken_ = false; LeaveCriticalSection(cs_); } @@ -56,6 +73,7 @@ class AutoCriticalSection { AutoCriticalSection& operator=(const AutoCriticalSection&); CRITICAL_SECTION* cs_; + bool taken_; }; } // namespace google_breakpad diff --git a/thirdparty/breakpad/client/windows/common/ipc_protocol.h b/thirdparty/breakpad/client/windows/common/ipc_protocol.h index f58e1381b..b03c032b7 100644 --- a/thirdparty/breakpad/client/windows/common/ipc_protocol.h +++ b/thirdparty/breakpad/client/windows/common/ipc_protocol.h @@ -30,8 +30,8 @@ #ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ #define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ -#include -#include +#include +#include #include #include #include "common/windows/string_utils-inl.h" @@ -90,7 +90,8 @@ enum MessageTag { MESSAGE_TAG_NONE = 0, MESSAGE_TAG_REGISTRATION_REQUEST = 1, MESSAGE_TAG_REGISTRATION_RESPONSE = 2, - MESSAGE_TAG_REGISTRATION_ACK = 3 + MESSAGE_TAG_REGISTRATION_ACK = 3, + MESSAGE_TAG_UPLOAD_REQUEST = 4 }; struct CustomClientInfo { @@ -102,7 +103,7 @@ struct CustomClientInfo { struct ProtocolMessage { ProtocolMessage() : tag(MESSAGE_TAG_NONE), - pid(0), + id(0), dump_type(MiniDumpNormal), thread_id(0), exception_pointers(NULL), @@ -115,7 +116,7 @@ struct ProtocolMessage { // Creates an instance with the given parameters. ProtocolMessage(MessageTag arg_tag, - DWORD arg_pid, + DWORD arg_id, MINIDUMP_TYPE arg_dump_type, DWORD* arg_thread_id, EXCEPTION_POINTERS** arg_exception_pointers, @@ -125,7 +126,7 @@ struct ProtocolMessage { HANDLE arg_dump_generated_handle, HANDLE arg_server_alive) : tag(arg_tag), - pid(arg_pid), + id(arg_id), dump_type(arg_dump_type), thread_id(arg_thread_id), exception_pointers(arg_exception_pointers), @@ -139,8 +140,9 @@ struct ProtocolMessage { // Tag in the message. MessageTag tag; - // Process id. - DWORD pid; + // The id for this message. This may be either a process id or a crash id + // depending on the type of message. + DWORD id; // Dump type requested. MINIDUMP_TYPE dump_type; diff --git a/thirdparty/breakpad/client/windows/crash_generation/client_info.cc b/thirdparty/breakpad/client/windows/crash_generation/client_info.cc index 94f9c3cdc..60bbac828 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/client_info.cc +++ b/thirdparty/breakpad/client/windows/crash_generation/client_info.cc @@ -31,6 +31,7 @@ #include "client/windows/common/ipc_protocol.h" static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime"; +static const size_t kMaxCustomInfoEntries = 4096; namespace google_breakpad { @@ -52,7 +53,8 @@ ClientInfo::ClientInfo(CrashGenerationServer* crash_server, dump_requested_handle_(NULL), dump_generated_handle_(NULL), dump_request_wait_handle_(NULL), - process_exit_wait_handle_(NULL) { + process_exit_wait_handle_(NULL), + crash_id_(NULL) { GetSystemTimeAsFileTime(&start_time_); } @@ -62,6 +64,12 @@ bool ClientInfo::Initialize() { return false; } + // The crash_id will be the low order word of the process creation time. + FILETIME creation_time, exit_time, kernel_time, user_time; + if (GetProcessTimes(process_handle_, &creation_time, &exit_time, + &kernel_time, &user_time)) + crash_id_ = creation_time.dwLowDateTime; + dump_requested_handle_ = CreateEvent(NULL, // Security attributes. TRUE, // Manual reset. FALSE, // Initial state. @@ -160,6 +168,9 @@ void ClientInfo::SetProcessUptime() { } bool ClientInfo::PopulateCustomInfo() { + if (custom_client_info_.count > kMaxCustomInfoEntries) + return false; + SIZE_T bytes_count = 0; SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count; diff --git a/thirdparty/breakpad/client/windows/crash_generation/client_info.h b/thirdparty/breakpad/client/windows/crash_generation/client_info.h index 47a5d21fe..999e66784 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/client_info.h +++ b/thirdparty/breakpad/client/windows/crash_generation/client_info.h @@ -65,6 +65,7 @@ class ClientInfo { HANDLE process_handle() const { return process_handle_; } HANDLE dump_requested_handle() const { return dump_requested_handle_; } HANDLE dump_generated_handle() const { return dump_generated_handle_; } + DWORD crash_id() const { return crash_id_; } HANDLE dump_request_wait_handle() const { return dump_request_wait_handle_; @@ -160,6 +161,11 @@ class ClientInfo { // for the client process when it signals a crash. FILETIME start_time_; + // The crash id which can be used to request an upload. This will be the + // value of the low order dword of the process creation time for the process + // being dumped. + DWORD crash_id_; + // Disallow copy ctor and operator=. ClientInfo(const ClientInfo& client_info); ClientInfo& operator=(const ClientInfo& client_info); diff --git a/thirdparty/breakpad/client/windows/crash_generation/crash_generation.gyp b/thirdparty/breakpad/client/windows/crash_generation/crash_generation.gyp old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc index 5e4e3cb9e..ffbcaf209 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc +++ b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.cc @@ -167,6 +167,23 @@ bool CrashGenerationClient::Register() { return success; } +bool CrashGenerationClient::RequestUpload(DWORD crash_id) { + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + CustomClientInfo custom_info = {NULL, 0}; + ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id, + static_cast(NULL), NULL, NULL, NULL, + custom_info, NULL, NULL, NULL); + DWORD bytes_count = 0; + bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0; + + CloseHandle(pipe); + return success; +} + HANDLE CrashGenerationClient::ConnectToServer() { HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), kPipeDesiredAccess, @@ -223,7 +240,7 @@ bool CrashGenerationClient::RegisterClient(HANDLE pipe) { crash_event_ = reply.dump_request_handle; crash_generated_ = reply.dump_generated_handle; server_alive_ = reply.server_alive_handle; - server_process_id_ = reply.pid; + server_process_id_ = reply.id; return true; } @@ -261,7 +278,7 @@ HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, bool CrashGenerationClient::ValidateResponse( const ProtocolMessage& msg) const { return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && - (msg.pid != 0) && + (msg.id != 0) && (msg.dump_request_handle != NULL) && (msg.dump_generated_handle != NULL) && (msg.server_alive_handle != NULL); diff --git a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h index 01d13ddec..85a0456cd 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h +++ b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_client.h @@ -73,6 +73,10 @@ class CrashGenerationClient { // Returns true if the registration is successful; false otherwise. bool Register(); + // Requests the crash server to upload a previous dump with the + // given crash id. + bool RequestUpload(DWORD crash_id); + bool RequestDump(EXCEPTION_POINTERS* ex_info, MDRawAssertionInfo* assert_info); diff --git a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc index 61af1b2dd..477973c1f 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc +++ b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.cc @@ -84,11 +84,12 @@ static const int kShutdownDelayMs = 10000; static const int kShutdownSleepIntervalMs = 5; static bool IsClientRequestValid(const ProtocolMessage& msg) { - return msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && - msg.pid != 0 && - msg.thread_id != NULL && - msg.exception_pointers != NULL && - msg.assert_info != NULL; + return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST || + (msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && + msg.id != 0 && + msg.thread_id != NULL && + msg.exception_pointers != NULL && + msg.assert_info != NULL); } CrashGenerationServer::CrashGenerationServer( @@ -100,6 +101,8 @@ CrashGenerationServer::CrashGenerationServer( void* dump_context, OnClientExitedCallback exit_callback, void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, bool generate_dumps, const std::wstring* dump_path) : pipe_name_(pipe_name), @@ -113,6 +116,8 @@ CrashGenerationServer::CrashGenerationServer( dump_context_(dump_context), exit_callback_(exit_callback), exit_context_(exit_context), + upload_request_callback_(upload_request_callback), + upload_context_(upload_context), generate_dumps_(generate_dumps), dump_generator_(NULL), server_state_(IPC_SERVER_STATE_UNINITIALIZED), @@ -120,7 +125,7 @@ CrashGenerationServer::CrashGenerationServer( overlapped_(), client_info_(NULL), cleanup_item_count_(0) { - InitializeCriticalSection(&clients_sync_); + InitializeCriticalSection(&sync_); if (dump_path) { dump_generator_.reset(new MinidumpGenerator(*dump_path)); @@ -128,38 +133,41 @@ CrashGenerationServer::CrashGenerationServer( } CrashGenerationServer::~CrashGenerationServer() { - // Indicate to existing threads that server is shutting down. - shutting_down_ = true; - - // Even if there are no current worker threads running, it is possible that - // an I/O request is pending on the pipe right now but not yet done. In fact, - // it's very likely this is the case unless we are in an ERROR state. If we - // don't wait for the pending I/O to be done, then when the I/O completes, - // it may write to invalid memory. AppVerifier will flag this problem too. - // So we disconnect from the pipe and then wait for the server to get into - // error state so that the pending I/O will fail and get cleared. - DisconnectNamedPipe(pipe_); - int num_tries = 100; - while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { - Sleep(10); - } - - // Unregister wait on the pipe. - if (pipe_wait_handle_) { - // Wait for already executing callbacks to finish. - UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE); - } - - // Close the pipe to avoid further client connections. - if (pipe_) { - CloseHandle(pipe_); - } - - // Request all ClientInfo objects to unregister all waits. - // New scope to hold the lock for the shortest time. + // New scope to release the lock automatically. { - AutoCriticalSection lock(&clients_sync_); + AutoCriticalSection lock(&sync_); + // Indicate to existing threads that server is shutting down. + shutting_down_ = true; + + // Even if there are no current worker threads running, it is possible that + // an I/O request is pending on the pipe right now but not yet done. + // In fact, it's very likely this is the case unless we are in an ERROR + // state. If we don't wait for the pending I/O to be done, then when the I/O + // completes, it may write to invalid memory. AppVerifier will flag this + // problem too. So we disconnect from the pipe and then wait for the server + // to get into error state so that the pending I/O will fail and get + // cleared. + DisconnectNamedPipe(pipe_); + int num_tries = 100; + while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { + lock.Release(); + Sleep(10); + lock.Acquire(); + } + + // Unregister wait on the pipe. + if (pipe_wait_handle_) { + // Wait for already executing callbacks to finish. + UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE); + } + + // Close the pipe to avoid further client connections. + if (pipe_) { + CloseHandle(pipe_); + } + + // Request all ClientInfo objects to unregister all waits. std::list::iterator iter; for (iter = clients_.begin(); iter != clients_.end(); ++iter) { ClientInfo* client_info = *iter; @@ -180,33 +188,35 @@ CrashGenerationServer::~CrashGenerationServer() { } } - // Clean up all the ClientInfo objects. // New scope to hold the lock for the shortest time. { - AutoCriticalSection lock(&clients_sync_); + AutoCriticalSection lock(&sync_); + // Clean up all the ClientInfo objects. std::list::iterator iter; for (iter = clients_.begin(); iter != clients_.end(); ++iter) { ClientInfo* client_info = *iter; delete client_info; } + + if (server_alive_handle_) { + // Release the mutex before closing the handle so that clients requesting + // dumps wait for a long time for the server to generate a dump. + ReleaseMutex(server_alive_handle_); + CloseHandle(server_alive_handle_); + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + } } - if (server_alive_handle_) { - // Release the mutex before closing the handle so that clients requesting - // dumps wait for a long time for the server to generate a dump. - ReleaseMutex(server_alive_handle_); - CloseHandle(server_alive_handle_); - } - - if (overlapped_.hEvent) { - CloseHandle(overlapped_.hEvent); - } - - DeleteCriticalSection(&clients_sync_); + DeleteCriticalSection(&sync_); } bool CrashGenerationServer::Start() { + AutoCriticalSection lock(&sync_); + if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) { return false; } @@ -416,9 +426,16 @@ void CrashGenerationServer::HandleReadDoneState() { return; } + if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) { + if (upload_request_callback_) + upload_request_callback_(upload_context_, msg_.id); + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + scoped_ptr client_info( new ClientInfo(this, - msg_.pid, + msg_.id, msg_.dump_type, msg_.thread_id, msg_.exception_pointers, @@ -582,7 +599,7 @@ void CrashGenerationServer::EnterStateImmediately(IPCServerState state) { bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info, ProtocolMessage* reply) const { reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE; - reply->pid = GetCurrentProcessId(); + reply->id = GetCurrentProcessId(); if (CreateClientHandles(client_info, reply)) { return true; @@ -670,6 +687,8 @@ bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) { // implements the state machine described in ReadMe.txt along with the // helper methods HandleXXXState. void CrashGenerationServer::HandleConnectionRequest() { + AutoCriticalSection lock(&sync_); + // If we are shutting doen then get into ERROR state, reset the event so more // workers don't run and return immediately. if (shutting_down_) { @@ -756,7 +775,7 @@ bool CrashGenerationServer::AddClient(ClientInfo* client_info) { // New scope to hold the lock for the shortest time. { - AutoCriticalSection lock(&clients_sync_); + AutoCriticalSection lock(&sync_); clients_.push_back(client_info); } @@ -793,6 +812,7 @@ void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) { CrashGenerationServer* crash_server = client_info->crash_server(); assert(crash_server); + client_info->UnregisterWaits(); InterlockedIncrement(&crash_server->cleanup_item_count_); if (!QueueUserWorkItem(CleanupClient, context, WT_EXECUTEDEFAULT)) { @@ -823,7 +843,7 @@ void CrashGenerationServer::DoCleanup(ClientInfo* client_info) { // Start a new scope to release lock automatically. { - AutoCriticalSection lock(&clients_sync_); + AutoCriticalSection lock(&sync_); clients_.remove(client_info); } diff --git a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h index 31a353bf3..ea3776fb7 100644 --- a/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h +++ b/thirdparty/breakpad/client/windows/crash_generation/crash_generation_server.h @@ -59,6 +59,9 @@ class CrashGenerationServer { typedef void (*OnClientExitedCallback)(void* context, const ClientInfo* client_info); + typedef void (*OnClientUploadRequestCallback)(void* context, + const DWORD crash_id); + // Creates an instance with the given parameters. // // Parameter pipe_name: Name of the Windows named pipe @@ -86,6 +89,8 @@ class CrashGenerationServer { void* dump_context, OnClientExitedCallback exit_callback, void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, bool generate_dumps, const std::wstring* dump_path); @@ -211,8 +216,9 @@ class CrashGenerationServer { // asynchronous IO operation. void EnterStateWhenSignaled(IPCServerState state); - // Sync object for thread-safe access to the shared list of clients. - CRITICAL_SECTION clients_sync_; + // Sync object for thread-safe access to the shared list of clients and + // the server's state. + CRITICAL_SECTION sync_; // List of clients. std::list clients_; @@ -250,6 +256,12 @@ class CrashGenerationServer { // Context for client process exit callback. void* exit_context_; + // Callback for upload request. + OnClientUploadRequestCallback upload_request_callback_; + + // Context for upload request callback. + void* upload_context_; + // Whether to generate dumps. bool generate_dumps_; @@ -260,10 +272,10 @@ class CrashGenerationServer { // Note that since we restrict the pipe to one instance, we // only need to keep one state of the server. Otherwise, server // would have one state per client it is talking to. - volatile IPCServerState server_state_; + IPCServerState server_state_; // Whether the server is shutting down. - volatile bool shutting_down_; + bool shutting_down_; // Overlapped instance for async I/O on the pipe. OVERLAPPED overlapped_; diff --git a/thirdparty/breakpad/client/windows/handler/exception_handler.cc b/thirdparty/breakpad/client/windows/handler/exception_handler.cc index 423bc11f2..08c5fb5f9 100644 --- a/thirdparty/breakpad/client/windows/handler/exception_handler.cc +++ b/thirdparty/breakpad/client/windows/handler/exception_handler.cc @@ -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. -#include +#include #include #include @@ -39,8 +39,6 @@ #include "client/windows/handler/exception_handler.h" #include "common/windows/guid_string.h" -typedef VOID (WINAPI *RtlCaptureContextPtr) (PCONTEXT pContextRecord); - namespace google_breakpad { static const int kWaitForHandlerThreadMs = 60000; @@ -223,8 +221,8 @@ void ExceptionHandler::Initialize(const wstring& dump_path, previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); #endif // _MSC_VER >= 1400 -// if (handler_types & HANDLER_PURECALL) -// previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); + if (handler_types & HANDLER_PURECALL) + previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); LeaveCriticalSection(&handler_stack_critical_section_); } @@ -250,8 +248,8 @@ ExceptionHandler::~ExceptionHandler() { _set_invalid_parameter_handler(previous_iph_); #endif // _MSC_VER >= 1400 -// if (handler_types_ & HANDLER_PURECALL) -// _set_purecall_handler(previous_pch_); + if (handler_types_ & HANDLER_PURECALL) + _set_purecall_handler(previous_pch_); if (handler_stack_->back() == this) { handler_stack_->pop_back(); @@ -314,6 +312,10 @@ ExceptionHandler::~ExceptionHandler() { } } +bool ExceptionHandler::RequestUpload(DWORD crash_id) { + return crash_generation_client_->RequestUpload(crash_id); +} + // static DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { ExceptionHandler* self = reinterpret_cast(lpParameter); @@ -380,7 +382,7 @@ class AutoExceptionHandler { #if _MSC_VER >= 1400 // MSVC 2005/8 _set_invalid_parameter_handler(handler_->previous_iph_); #endif // _MSC_VER >= 1400 -// _set_purecall_handler(handler_->previous_pch_); + _set_purecall_handler(handler_->previous_pch_); } ~AutoExceptionHandler() { @@ -389,7 +391,7 @@ class AutoExceptionHandler { #if _MSC_VER >= 1400 // MSVC 2005/8 _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter); #endif // _MSC_VER >= 1400 -// _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); + _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); --ExceptionHandler::handler_stack_index_; LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); @@ -498,27 +500,19 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, CONTEXT exception_context = {}; EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; - EXCEPTION_POINTERS* exinfo = NULL; + ::RtlCaptureContext(&exception_context); - RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr) - GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext"); - if (fnRtlCaptureContext) { - fnRtlCaptureContext(&exception_context); + exception_record.ExceptionCode = STATUS_INVALID_PARAMETER; - exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; - - // We store pointers to the the expression and function strings, - // and the line as exception parameters to make them easy to - // access by the developer on the far side. - exception_record.NumberParameters = 3; - exception_record.ExceptionInformation[0] = - reinterpret_cast(&assertion.expression); - exception_record.ExceptionInformation[1] = - reinterpret_cast(&assertion.file); - exception_record.ExceptionInformation[2] = assertion.line; - - exinfo = &exception_ptrs; - } + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; bool success = false; // In case of out-of-process dump generation, directly call @@ -526,10 +520,10 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, if (current_handler->IsOutOfProcess()) { success = current_handler->WriteMinidumpWithException( GetCurrentThreadId(), - exinfo, + &exception_ptrs, &assertion); } else { - success = current_handler->WriteMinidumpOnHandlerThread(exinfo, + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, &assertion); } @@ -586,27 +580,19 @@ void ExceptionHandler::HandlePureVirtualCall() { CONTEXT exception_context = {}; EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; - EXCEPTION_POINTERS* exinfo = NULL; + ::RtlCaptureContext(&exception_context); - RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr) - GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext"); - if (fnRtlCaptureContext) { - fnRtlCaptureContext(&exception_context); + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; - exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; - - // We store pointers to the the expression and function strings, - // and the line as exception parameters to make them easy to - // access by the developer on the far side. - exception_record.NumberParameters = 3; - exception_record.ExceptionInformation[0] = - reinterpret_cast(&assertion.expression); - exception_record.ExceptionInformation[1] = - reinterpret_cast(&assertion.file); - exception_record.ExceptionInformation[2] = assertion.line; - - exinfo = &exception_ptrs; - } + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; bool success = false; // In case of out-of-process dump generation, directly call @@ -615,10 +601,10 @@ void ExceptionHandler::HandlePureVirtualCall() { if (current_handler->IsOutOfProcess()) { success = current_handler->WriteMinidumpWithException( GetCurrentThreadId(), - exinfo, + &exception_ptrs, &assertion); } else { - success = current_handler->WriteMinidumpOnHandlerThread(exinfo, + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, &assertion); } @@ -678,7 +664,18 @@ bool ExceptionHandler::WriteMinidumpOnHandlerThread( } bool ExceptionHandler::WriteMinidump() { - return WriteMinidumpForException(NULL); + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + + return WriteMinidumpForException(&exception_ptrs); } bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { @@ -775,7 +772,7 @@ bool ExceptionHandler::WriteMinidumpWithException( if (exinfo) { // Find a memory region of 256 bytes centered on the // faulting instruction pointer. - const ULONG64 instruction_pointer = + const ULONG64 instruction_pointer = #if defined(_M_IX86) exinfo->ContextRecord->Eip; #elif defined(_M_AMD64) @@ -854,7 +851,7 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( callback_context->finished = true; return TRUE; } - + // Include all modules. case IncludeModuleCallback: case ModuleCallback: @@ -866,11 +863,10 @@ BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( return TRUE; // Stop receiving cancel callbacks. -//FIXME: CancelCallback is missing in our mingw headers currently, but it's present in trunk, so we need to comment this in as soon as mingw is updated in opensuse (domme) -// case CancelCallback: -// callback_output->CheckCancel = FALSE; -// callback_output->Cancel = FALSE; -// return TRUE; + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; } // Ignore other callback types. return FALSE; diff --git a/thirdparty/breakpad/client/windows/handler/exception_handler.gyp b/thirdparty/breakpad/client/windows/handler/exception_handler.gyp old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/handler/exception_handler.h b/thirdparty/breakpad/client/windows/handler/exception_handler.h index a00329be8..6c5ee76ac 100644 --- a/thirdparty/breakpad/client/windows/handler/exception_handler.h +++ b/thirdparty/breakpad/client/windows/handler/exception_handler.h @@ -57,8 +57,8 @@ #define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ #include -#include -#include +#include +#include #include #pragma warning( push ) @@ -88,9 +88,9 @@ class ExceptionHandler { // if any. // // If a FilterCallback returns true, Breakpad will continue processing, - // attempting to write a minidump. If a FilterCallback returns false, Breakpad - // will immediately report the exception as unhandled without writing a - // minidump, allowing another handler the opportunity to handle it. + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. typedef bool (*FilterCallback)(void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion); @@ -177,6 +177,9 @@ class ExceptionHandler { UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. } + // Requests that a previously reported crash be uploaded. + bool RequestUpload(DWORD crash_id); + // Writes a minidump immediately. This can be used to capture the // execution state independently of a crash. Returns true on success. bool WriteMinidump(); diff --git a/thirdparty/breakpad/client/windows/sender/crash_report_sender.gyp b/thirdparty/breakpad/client/windows/sender/crash_report_sender.gyp old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.cc b/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.cc index 1b4bd3c98..e36c1d1e1 100644 --- a/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.cc +++ b/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.cc @@ -291,6 +291,8 @@ void CrashServerStart() { NULL, ShowClientExited, NULL, + NULL, + NULL, true, &dump_path); diff --git a/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.gyp b/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.gyp index aabf9c017..b5dabc54f 100644 --- a/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.gyp +++ b/thirdparty/breakpad/client/windows/tests/crash_generation_app/crash_generation_app.gyp @@ -1,61 +1,61 @@ -# 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. - -{ - 'includes': [ - '../../build/common.gypi', - ], - 'targets': [ - { - 'target_name': 'crash_generation_app', - 'type': 'executable', - 'sources': [ - 'abstract_class.cc', - 'abstract_class.h', - 'crash_generation_app.cc', - 'crash_generation_app.h', - 'crash_generation_app.ico', - 'crash_generation_app.rc', - 'resource.h', - 'small.ico', - ], - 'dependencies': [ - '../../breakpad_client.gyp:common', - '../../crash_generation/crash_generation.gyp:crash_generation_server', - '../../crash_generation/crash_generation.gyp:crash_generation_client', - '../../handler/exception_handler.gyp:exception_handler', - ], - 'msvs_settings': { - 'VCLinkerTool': { - 'SubSystem': '2', # Windows Subsystem as opposed to a console app - }, - }, - } - ] -} +# 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. + +{ + 'includes': [ + '../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'crash_generation_app', + 'type': 'executable', + 'sources': [ + 'abstract_class.cc', + 'abstract_class.h', + 'crash_generation_app.cc', + 'crash_generation_app.h', + 'crash_generation_app.ico', + 'crash_generation_app.rc', + 'resource.h', + 'small.ico', + ], + 'dependencies': [ + '../../breakpad_client.gyp:common', + '../../crash_generation/crash_generation.gyp:crash_generation_server', + '../../crash_generation/crash_generation.gyp:crash_generation_client', + '../../handler/exception_handler.gyp:exception_handler', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '2', # Windows Subsystem as opposed to a console app + }, + }, + } + ] +} diff --git a/thirdparty/breakpad/client/windows/unittests/client_tests.gyp b/thirdparty/breakpad/client/windows/unittests/client_tests.gyp old mode 100755 new mode 100644 diff --git a/thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc b/thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc index 75d0be97b..cf95d43f0 100644 --- a/thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc +++ b/thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc @@ -1,298 +1,306 @@ -// Copyright 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 "testing/gtest/include/gtest/gtest.h" -#include "testing/include/gmock/gmock.h" - -#include "client/windows/crash_generation/crash_generation_server.h" -#include "client/windows/common/ipc_protocol.h" - -using testing::_; - -namespace { - -const wchar_t kPipeName[] = - L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer"; - -const DWORD kPipeDesiredAccess = FILE_READ_DATA | - FILE_WRITE_DATA | - FILE_WRITE_ATTRIBUTES; - -const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | - SECURITY_SQOS_PRESENT; - -const DWORD kPipeMode = PIPE_READMODE_MESSAGE; - -int kCustomInfoCount = 2; - -google_breakpad::CustomInfoEntry kCustomInfoEntries[] = { - google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"), - google_breakpad::CustomInfoEntry(L"ver", L"1.0"), -}; - -class CrashGenerationServerTest : public ::testing::Test { - public: - CrashGenerationServerTest() - : crash_generation_server_(kPipeName, - NULL, - CallOnClientConnected, &mock_callbacks_, - CallOnClientDumpRequested, &mock_callbacks_, - CallOnClientExited, &mock_callbacks_, - false, - NULL), - thread_id_(0), - exception_pointers_(NULL) { - memset(&assert_info_, 0, sizeof(assert_info_)); - } - - protected: - class MockCrashGenerationServerCallbacks { - public: - MOCK_METHOD1(OnClientConnected, - void(const google_breakpad::ClientInfo* client_info)); - MOCK_METHOD2(OnClientDumpRequested, - void(const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path)); - MOCK_METHOD1(OnClientExited, - void(const google_breakpad::ClientInfo* client_info)); - }; - - enum ClientFault { - NO_FAULT, - CLOSE_AFTER_CONNECT, - SEND_INVALID_REGISTRATION, - TRUNCATE_REGISTRATION, - CLOSE_AFTER_REGISTRATION, - RESPONSE_BUFFER_TOO_SMALL, - CLOSE_AFTER_RESPONSE, - SEND_INVALID_ACK - }; - - void SetUp() { - ASSERT_TRUE(crash_generation_server_.Start()); - } - - void FaultyClient(ClientFault fault_type) { - HANDLE pipe = CreateFile(kPipeName, - kPipeDesiredAccess, - 0, - NULL, - OPEN_EXISTING, - kPipeFlagsAndAttributes, - NULL); - - if (pipe == INVALID_HANDLE_VALUE) { - ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError()); - - // Cannot continue retrying if wait on pipe fails. - ASSERT_TRUE(WaitNamedPipe(kPipeName, 500)); - - pipe = CreateFile(kPipeName, - kPipeDesiredAccess, - 0, - NULL, - OPEN_EXISTING, - kPipeFlagsAndAttributes, - NULL); - } - - ASSERT_NE(pipe, INVALID_HANDLE_VALUE); - - DWORD mode = kPipeMode; - ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL)); - - DoFaultyClient(fault_type, pipe); - - CloseHandle(pipe); - } - - void DoTestFault(ClientFault fault) { - EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0); - ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); - ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); - ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); - - EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); - - ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); - - // Slight hack. The OnClientConnected is only invoked after the ack is - // received by the server. At that point, the FaultyClient call has already - // returned. The best way to wait until the server is done handling that is - // to send one more ping, whose processing will be blocked by delivery of - // the OnClientConnected message. - ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); - } - - MockCrashGenerationServerCallbacks mock_callbacks_; - - private: - // Depends on the caller to successfully open the pipe before invocation and - // to close it immediately afterwards. - void DoFaultyClient(ClientFault fault_type, HANDLE pipe) { - if (fault_type == CLOSE_AFTER_CONNECT) { - return; - } - - google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries, - kCustomInfoCount}; - - google_breakpad::ProtocolMessage msg( - fault_type == SEND_INVALID_REGISTRATION ? - google_breakpad::MESSAGE_TAG_NONE : - google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST, - GetCurrentProcessId(), - MiniDumpNormal, - &thread_id_, - &exception_pointers_, - &assert_info_, - custom_info, - NULL, - NULL, - NULL); - - DWORD bytes_count = 0; - - ASSERT_TRUE(WriteFile(pipe, - &msg, - fault_type == TRUNCATE_REGISTRATION ? - sizeof(msg) / 2 : sizeof(msg), - &bytes_count, - NULL)); - - if (fault_type == CLOSE_AFTER_REGISTRATION) { - return; - } - - google_breakpad::ProtocolMessage reply; - - if (!ReadFile(pipe, - &reply, - fault_type == RESPONSE_BUFFER_TOO_SMALL ? - sizeof(google_breakpad::ProtocolMessage) / 2 : - sizeof(google_breakpad::ProtocolMessage), - &bytes_count, - NULL)) { - switch (fault_type) { - case TRUNCATE_REGISTRATION: - case RESPONSE_BUFFER_TOO_SMALL: - case SEND_INVALID_REGISTRATION: - return; - - default: - FAIL() << "Unexpectedly failed to register."; - } - } - - if (fault_type == CLOSE_AFTER_RESPONSE) { - return; - } - - google_breakpad::ProtocolMessage ack_msg; - ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK; - - ASSERT_TRUE(WriteFile(pipe, - &ack_msg, - SEND_INVALID_ACK ? - sizeof(ack_msg) : sizeof(ack_msg) / 2, - &bytes_count, - NULL)); - - return; - } - - static void CallOnClientConnected( - void* context, const google_breakpad::ClientInfo* client_info) { - static_cast(context)-> - OnClientConnected(client_info); - } - - static void CallOnClientDumpRequested( - void* context, - const google_breakpad::ClientInfo* client_info, - const std::wstring* file_path) { - static_cast(context)-> - OnClientDumpRequested(client_info, file_path); - } - - static void CallOnClientExited( - void* context, const google_breakpad::ClientInfo* client_info) { - static_cast(context)-> - OnClientExited(client_info); - } - - DWORD thread_id_; - EXCEPTION_POINTERS* exception_pointers_; - MDRawAssertionInfo assert_info_; - - google_breakpad::CrashGenerationServer crash_generation_server_; -}; - -TEST_F(CrashGenerationServerTest, PingServerTest) { - DoTestFault(CLOSE_AFTER_CONNECT); -} - -TEST_F(CrashGenerationServerTest, InvalidRegistration) { - DoTestFault(SEND_INVALID_REGISTRATION); -} - -TEST_F(CrashGenerationServerTest, TruncateRegistration) { - DoTestFault(TRUNCATE_REGISTRATION); -} - -TEST_F(CrashGenerationServerTest, CloseAfterRegistration) { - DoTestFault(CLOSE_AFTER_REGISTRATION); -} - -TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) { - DoTestFault(RESPONSE_BUFFER_TOO_SMALL); -} - -TEST_F(CrashGenerationServerTest, CloseAfterResponse) { - DoTestFault(CLOSE_AFTER_RESPONSE); -} - -// It turns out that, as long as you send one byte, the ACK is accepted and -// registration succeeds. -TEST_F(CrashGenerationServerTest, SendInvalidAck) { - EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); - ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK)); - - // See DoTestFault for an explanation of this line - ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); - - EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); - ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); - - // See DoTestFault for an explanation of this line - ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); -} - -} // anonymous namespace +// Copyright 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 "testing/gtest/include/gtest/gtest.h" +#include "testing/include/gmock/gmock.h" + +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/common/ipc_protocol.h" + +using testing::_; + +namespace { + +const wchar_t kPipeName[] = + L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer"; + +const DWORD kPipeDesiredAccess = FILE_READ_DATA | + FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES; + +const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | + SECURITY_SQOS_PRESENT; + +const DWORD kPipeMode = PIPE_READMODE_MESSAGE; + +int kCustomInfoCount = 2; + +google_breakpad::CustomInfoEntry kCustomInfoEntries[] = { + google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"), + google_breakpad::CustomInfoEntry(L"ver", L"1.0"), +}; + +class CrashGenerationServerTest : public ::testing::Test { + public: + CrashGenerationServerTest() + : crash_generation_server_(kPipeName, + NULL, + CallOnClientConnected, &mock_callbacks_, + CallOnClientDumpRequested, &mock_callbacks_, + CallOnClientExited, &mock_callbacks_, + CallOnClientUploadRequested, &mock_callbacks_, + false, + NULL), + thread_id_(0), + exception_pointers_(NULL) { + memset(&assert_info_, 0, sizeof(assert_info_)); + } + + protected: + class MockCrashGenerationServerCallbacks { + public: + MOCK_METHOD1(OnClientConnected, + void(const google_breakpad::ClientInfo* client_info)); + MOCK_METHOD2(OnClientDumpRequested, + void(const google_breakpad::ClientInfo* client_info, + const std::wstring* file_path)); + MOCK_METHOD1(OnClientExited, + void(const google_breakpad::ClientInfo* client_info)); + MOCK_METHOD1(OnClientUploadRequested, + void(const DWORD crash_id)); + }; + + enum ClientFault { + NO_FAULT, + CLOSE_AFTER_CONNECT, + SEND_INVALID_REGISTRATION, + TRUNCATE_REGISTRATION, + CLOSE_AFTER_REGISTRATION, + RESPONSE_BUFFER_TOO_SMALL, + CLOSE_AFTER_RESPONSE, + SEND_INVALID_ACK + }; + + void SetUp() { + ASSERT_TRUE(crash_generation_server_.Start()); + } + + void FaultyClient(ClientFault fault_type) { + HANDLE pipe = CreateFile(kPipeName, + kPipeDesiredAccess, + 0, + NULL, + OPEN_EXISTING, + kPipeFlagsAndAttributes, + NULL); + + if (pipe == INVALID_HANDLE_VALUE) { + ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError()); + + // Cannot continue retrying if wait on pipe fails. + ASSERT_TRUE(WaitNamedPipe(kPipeName, 500)); + + pipe = CreateFile(kPipeName, + kPipeDesiredAccess, + 0, + NULL, + OPEN_EXISTING, + kPipeFlagsAndAttributes, + NULL); + } + + ASSERT_NE(pipe, INVALID_HANDLE_VALUE); + + DWORD mode = kPipeMode; + ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL)); + + DoFaultyClient(fault_type, pipe); + + CloseHandle(pipe); + } + + void DoTestFault(ClientFault fault) { + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0); + ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(fault)); + + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); + + ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); + + // Slight hack. The OnClientConnected is only invoked after the ack is + // received by the server. At that point, the FaultyClient call has already + // returned. The best way to wait until the server is done handling that is + // to send one more ping, whose processing will be blocked by delivery of + // the OnClientConnected message. + ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); + } + + MockCrashGenerationServerCallbacks mock_callbacks_; + + private: + // Depends on the caller to successfully open the pipe before invocation and + // to close it immediately afterwards. + void DoFaultyClient(ClientFault fault_type, HANDLE pipe) { + if (fault_type == CLOSE_AFTER_CONNECT) { + return; + } + + google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries, + kCustomInfoCount}; + + google_breakpad::ProtocolMessage msg( + fault_type == SEND_INVALID_REGISTRATION ? + google_breakpad::MESSAGE_TAG_NONE : + google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST, + GetCurrentProcessId(), + MiniDumpNormal, + &thread_id_, + &exception_pointers_, + &assert_info_, + custom_info, + NULL, + NULL, + NULL); + + DWORD bytes_count = 0; + + ASSERT_TRUE(WriteFile(pipe, + &msg, + fault_type == TRUNCATE_REGISTRATION ? + sizeof(msg) / 2 : sizeof(msg), + &bytes_count, + NULL)); + + if (fault_type == CLOSE_AFTER_REGISTRATION) { + return; + } + + google_breakpad::ProtocolMessage reply; + + if (!ReadFile(pipe, + &reply, + fault_type == RESPONSE_BUFFER_TOO_SMALL ? + sizeof(google_breakpad::ProtocolMessage) / 2 : + sizeof(google_breakpad::ProtocolMessage), + &bytes_count, + NULL)) { + switch (fault_type) { + case TRUNCATE_REGISTRATION: + case RESPONSE_BUFFER_TOO_SMALL: + case SEND_INVALID_REGISTRATION: + return; + + default: + FAIL() << "Unexpectedly failed to register."; + } + } + + if (fault_type == CLOSE_AFTER_RESPONSE) { + return; + } + + google_breakpad::ProtocolMessage ack_msg; + ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK; + + ASSERT_TRUE(WriteFile(pipe, + &ack_msg, + SEND_INVALID_ACK ? + sizeof(ack_msg) : sizeof(ack_msg) / 2, + &bytes_count, + NULL)); + + return; + } + + static void CallOnClientConnected( + void* context, const google_breakpad::ClientInfo* client_info) { + static_cast(context)-> + OnClientConnected(client_info); + } + + static void CallOnClientDumpRequested( + void* context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* file_path) { + static_cast(context)-> + OnClientDumpRequested(client_info, file_path); + } + + static void CallOnClientExited( + void* context, const google_breakpad::ClientInfo* client_info) { + static_cast(context)-> + OnClientExited(client_info); + } + + static void CallOnClientUploadRequested(void* context, const DWORD crash_id) { + static_cast(context)-> + OnClientUploadRequested(crash_id); + } + + DWORD thread_id_; + EXCEPTION_POINTERS* exception_pointers_; + MDRawAssertionInfo assert_info_; + + google_breakpad::CrashGenerationServer crash_generation_server_; +}; + +TEST_F(CrashGenerationServerTest, PingServerTest) { + DoTestFault(CLOSE_AFTER_CONNECT); +} + +TEST_F(CrashGenerationServerTest, InvalidRegistration) { + DoTestFault(SEND_INVALID_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, TruncateRegistration) { + DoTestFault(TRUNCATE_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, CloseAfterRegistration) { + DoTestFault(CLOSE_AFTER_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) { + DoTestFault(RESPONSE_BUFFER_TOO_SMALL); +} + +TEST_F(CrashGenerationServerTest, CloseAfterResponse) { + DoTestFault(CLOSE_AFTER_RESPONSE); +} + +// It turns out that, as long as you send one byte, the ACK is accepted and +// registration succeeds. +TEST_F(CrashGenerationServerTest, SendInvalidAck) { + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK)); + + // See DoTestFault for an explanation of this line + ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); + + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); + + // See DoTestFault for an explanation of this line + ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); +} + +} // anonymous namespace diff --git a/thirdparty/breakpad/client/windows/unittests/dump_analysis.cc b/thirdparty/breakpad/client/windows/unittests/dump_analysis.cc old mode 100755 new mode 100644 index 936b27fde..b57b03da6 --- a/thirdparty/breakpad/client/windows/unittests/dump_analysis.cc +++ b/thirdparty/breakpad/client/windows/unittests/dump_analysis.cc @@ -1,184 +1,184 @@ -// 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 -#include -#include - -#include "dump_analysis.h" // NOLINT -#include "gtest/gtest.h" - -DumpAnalysis::~DumpAnalysis() { - if (dump_file_view_ != NULL) { - EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_)); - ::CloseHandle(dump_file_mapping_); - dump_file_mapping_ = NULL; - } - - if (dump_file_handle_ != NULL) { - ::CloseHandle(dump_file_handle_); - dump_file_handle_ = NULL; - } -} - -void DumpAnalysis::EnsureDumpMapped() { - if (dump_file_view_ == NULL) { - dump_file_handle_ = ::CreateFile(dump_file_.c_str(), - GENERIC_READ, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL); - ASSERT_TRUE(dump_file_handle_ != NULL); - ASSERT_TRUE(dump_file_mapping_ == NULL); - - dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_, - NULL, - PAGE_READONLY, - 0, - 0, - NULL); - ASSERT_TRUE(dump_file_mapping_ != NULL); - - dump_file_view_ = ::MapViewOfFile(dump_file_mapping_, - FILE_MAP_READ, - 0, - 0, - 0); - ASSERT_TRUE(dump_file_view_ != NULL); - } -} - -bool DumpAnalysis::HasTebs() const { - MINIDUMP_THREAD_LIST* thread_list = NULL; - size_t thread_list_size = GetStream(ThreadListStream, &thread_list); - - if (thread_list_size > 0 && thread_list != NULL) { - for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) { - if (!HasMemory(thread_list->Threads[i].Teb)) - return false; - } - - return true; - } - - // No thread list, no TEB info. - return false; -} - -bool DumpAnalysis::HasPeb() const { - MINIDUMP_THREAD_LIST* thread_list = NULL; - size_t thread_list_size = GetStream(ThreadListStream, &thread_list); - - if (thread_list_size > 0 && thread_list != NULL && - thread_list->NumberOfThreads > 0) { - FakeTEB* teb = NULL; - if (!HasMemory(thread_list->Threads[0].Teb, &teb)) - return false; - - return HasMemory(teb->peb); - } - - return false; -} - -bool DumpAnalysis::HasStream(ULONG stream_number) const { - void* stream = NULL; - size_t stream_size = GetStreamImpl(stream_number, &stream); - return stream_size > 0 && stream != NULL; -} - -size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const { - MINIDUMP_DIRECTORY* directory = NULL; - ULONG memory_list_size = 0; - BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_, - stream_number, - &directory, - stream, - &memory_list_size); - - return ret ? memory_list_size : 0; -} - -bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize, - void **structure) const { - uintptr_t address = reinterpret_cast(addr_in); - MINIDUMP_MEMORY_LIST* memory_list = NULL; - size_t memory_list_size = GetStream(MemoryListStream, &memory_list); - if (memory_list_size > 0 && memory_list != NULL) { - for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) { - MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i]; - const uintptr_t range_start = - static_cast(descr.StartOfMemoryRange); - uintptr_t range_end = range_start + descr.Memory.DataSize; - - if (address >= range_start && - address + structuresize < range_end) { - // The start address falls in the range, and the end address is - // in bounds, return a pointer to the structure if requested. - if (structure != NULL) - *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva); - - return true; - } - } - } - - // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this - // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the - // end of the dump file. - MINIDUMP_MEMORY64_LIST* memory64_list = NULL; - memory_list_size = GetStream(Memory64ListStream, &memory64_list); - if (memory_list_size > 0 && memory64_list != NULL) { - // Keep track of where the current descriptor maps to. - RVA64 curr_rva = memory64_list->BaseRva; - for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) { - MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i]; - uintptr_t range_start = - static_cast(descr.StartOfMemoryRange); - uintptr_t range_end = range_start + static_cast(descr.DataSize); - - if (address >= range_start && - address + structuresize < range_end) { - // The start address falls in the range, and the end address is - // in bounds, return a pointer to the structure if requested. - if (structure != NULL) - *structure = RVA_TO_ADDR(dump_file_view_, curr_rva); - - return true; - } - - // Advance the current RVA. - curr_rva += descr.DataSize; - } - } - - return false; -} +// 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 +#include +#include + +#include "dump_analysis.h" // NOLINT +#include "gtest/gtest.h" + +DumpAnalysis::~DumpAnalysis() { + if (dump_file_view_ != NULL) { + EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_)); + ::CloseHandle(dump_file_mapping_); + dump_file_mapping_ = NULL; + } + + if (dump_file_handle_ != NULL) { + ::CloseHandle(dump_file_handle_); + dump_file_handle_ = NULL; + } +} + +void DumpAnalysis::EnsureDumpMapped() { + if (dump_file_view_ == NULL) { + dump_file_handle_ = ::CreateFile(dump_file_.c_str(), + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + ASSERT_TRUE(dump_file_handle_ != NULL); + ASSERT_TRUE(dump_file_mapping_ == NULL); + + dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_, + NULL, + PAGE_READONLY, + 0, + 0, + NULL); + ASSERT_TRUE(dump_file_mapping_ != NULL); + + dump_file_view_ = ::MapViewOfFile(dump_file_mapping_, + FILE_MAP_READ, + 0, + 0, + 0); + ASSERT_TRUE(dump_file_view_ != NULL); + } +} + +bool DumpAnalysis::HasTebs() const { + MINIDUMP_THREAD_LIST* thread_list = NULL; + size_t thread_list_size = GetStream(ThreadListStream, &thread_list); + + if (thread_list_size > 0 && thread_list != NULL) { + for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) { + if (!HasMemory(thread_list->Threads[i].Teb)) + return false; + } + + return true; + } + + // No thread list, no TEB info. + return false; +} + +bool DumpAnalysis::HasPeb() const { + MINIDUMP_THREAD_LIST* thread_list = NULL; + size_t thread_list_size = GetStream(ThreadListStream, &thread_list); + + if (thread_list_size > 0 && thread_list != NULL && + thread_list->NumberOfThreads > 0) { + FakeTEB* teb = NULL; + if (!HasMemory(thread_list->Threads[0].Teb, &teb)) + return false; + + return HasMemory(teb->peb); + } + + return false; +} + +bool DumpAnalysis::HasStream(ULONG stream_number) const { + void* stream = NULL; + size_t stream_size = GetStreamImpl(stream_number, &stream); + return stream_size > 0 && stream != NULL; +} + +size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const { + MINIDUMP_DIRECTORY* directory = NULL; + ULONG memory_list_size = 0; + BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_, + stream_number, + &directory, + stream, + &memory_list_size); + + return ret ? memory_list_size : 0; +} + +bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize, + void **structure) const { + uintptr_t address = reinterpret_cast(addr_in); + MINIDUMP_MEMORY_LIST* memory_list = NULL; + size_t memory_list_size = GetStream(MemoryListStream, &memory_list); + if (memory_list_size > 0 && memory_list != NULL) { + for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) { + MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i]; + const uintptr_t range_start = + static_cast(descr.StartOfMemoryRange); + uintptr_t range_end = range_start + descr.Memory.DataSize; + + if (address >= range_start && + address + structuresize < range_end) { + // The start address falls in the range, and the end address is + // in bounds, return a pointer to the structure if requested. + if (structure != NULL) + *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva); + + return true; + } + } + } + + // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this + // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the + // end of the dump file. + MINIDUMP_MEMORY64_LIST* memory64_list = NULL; + memory_list_size = GetStream(Memory64ListStream, &memory64_list); + if (memory_list_size > 0 && memory64_list != NULL) { + // Keep track of where the current descriptor maps to. + RVA64 curr_rva = memory64_list->BaseRva; + for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) { + MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i]; + uintptr_t range_start = + static_cast(descr.StartOfMemoryRange); + uintptr_t range_end = range_start + static_cast(descr.DataSize); + + if (address >= range_start && + address + structuresize < range_end) { + // The start address falls in the range, and the end address is + // in bounds, return a pointer to the structure if requested. + if (structure != NULL) + *structure = RVA_TO_ADDR(dump_file_view_, curr_rva); + + return true; + } + + // Advance the current RVA. + curr_rva += descr.DataSize; + } + } + + return false; +} diff --git a/thirdparty/breakpad/client/windows/unittests/dump_analysis.h b/thirdparty/breakpad/client/windows/unittests/dump_analysis.h old mode 100755 new mode 100644 index 0392f5d98..b3155691b --- a/thirdparty/breakpad/client/windows/unittests/dump_analysis.h +++ b/thirdparty/breakpad/client/windows/unittests/dump_analysis.h @@ -1,102 +1,102 @@ -// 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_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ -#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ - -#include "../crash_generation/minidump_generator.h" - -// Convenience to get to the PEB pointer in a TEB. -struct FakeTEB { - char dummy[0x30]; - void* peb; -}; - -class DumpAnalysis { - public: - explicit DumpAnalysis(const std::wstring& file_path) - : dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL), - dump_file_handle_(NULL) { - EnsureDumpMapped(); - } - ~DumpAnalysis(); - - bool HasStream(ULONG stream_number) const; - - // This is template to keep type safety in the front, but we end up casting - // to void** inside the implementation to pass the pointer to Win32. So - // casting here is considered safe. - template - size_t GetStream(ULONG stream_number, StreamType** stream) const { - return GetStreamImpl(stream_number, reinterpret_cast(stream)); - } - - bool HasTebs() const; - bool HasPeb() const; - bool HasMemory(ULONG64 address) const { - return HasMemory(address, NULL); - } - - bool HasMemory(const void* address) const { - return HasMemory(address, NULL); - } - - template - bool HasMemory(ULONG64 address, StructureType** structure = NULL) const { - // We can't cope with 64 bit addresses for now. - if (address > 0xFFFFFFFFUL) - return false; - - return HasMemory(reinterpret_cast(address), structure); - } - - template - bool HasMemory(const void* addr_in, StructureType** structure = NULL) const { - return HasMemoryImpl(addr_in, sizeof(StructureType), - reinterpret_cast(structure)); - } - - protected: - void EnsureDumpMapped(); - - HANDLE dump_file_mapping_; - HANDLE dump_file_handle_; - void* dump_file_view_; - std::wstring dump_file_; - - private: - // This is the implementation of GetStream<>. - size_t GetStreamImpl(ULONG stream_number, void** stream) const; - - // This is the implementation of HasMemory<>. - bool HasMemoryImpl(const void* addr_in, size_t pointersize, - void** structure) const; -}; - -#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ +// 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_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ +#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ + +#include "../crash_generation/minidump_generator.h" + +// Convenience to get to the PEB pointer in a TEB. +struct FakeTEB { + char dummy[0x30]; + void* peb; +}; + +class DumpAnalysis { + public: + explicit DumpAnalysis(const std::wstring& file_path) + : dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL), + dump_file_handle_(NULL) { + EnsureDumpMapped(); + } + ~DumpAnalysis(); + + bool HasStream(ULONG stream_number) const; + + // This is template to keep type safety in the front, but we end up casting + // to void** inside the implementation to pass the pointer to Win32. So + // casting here is considered safe. + template + size_t GetStream(ULONG stream_number, StreamType** stream) const { + return GetStreamImpl(stream_number, reinterpret_cast(stream)); + } + + bool HasTebs() const; + bool HasPeb() const; + bool HasMemory(ULONG64 address) const { + return HasMemory(address, NULL); + } + + bool HasMemory(const void* address) const { + return HasMemory(address, NULL); + } + + template + bool HasMemory(ULONG64 address, StructureType** structure = NULL) const { + // We can't cope with 64 bit addresses for now. + if (address > 0xFFFFFFFFUL) + return false; + + return HasMemory(reinterpret_cast(address), structure); + } + + template + bool HasMemory(const void* addr_in, StructureType** structure = NULL) const { + return HasMemoryImpl(addr_in, sizeof(StructureType), + reinterpret_cast(structure)); + } + + protected: + void EnsureDumpMapped(); + + HANDLE dump_file_mapping_; + HANDLE dump_file_handle_; + void* dump_file_view_; + std::wstring dump_file_; + + private: + // This is the implementation of GetStream<>. + size_t GetStreamImpl(ULONG stream_number, void** stream) const; + + // This is the implementation of HasMemory<>. + bool HasMemoryImpl(const void* addr_in, size_t pointersize, + void** structure) const; +}; + +#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ diff --git a/thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc b/thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc old mode 100755 new mode 100644 index adea044fa..514ee72aa --- a/thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc +++ b/thirdparty/breakpad/client/windows/unittests/exception_handler_death_test.cc @@ -161,8 +161,8 @@ TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { ASSERT_TRUE(DoesPathExist(temp_path_)); std::wstring dump_path(temp_path_); google_breakpad::CrashGenerationServer server( - kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true, - &dump_path); + kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); // This HAS to be EXPECT_, because when this test case is executed in the // child process, the server registration will fail due to the named pipe diff --git a/thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc b/thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc old mode 100755 new mode 100644 index f03418800..74d9a9bef --- a/thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc +++ b/thirdparty/breakpad/client/windows/unittests/exception_handler_test.cc @@ -220,8 +220,8 @@ TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) { ASSERT_TRUE(DoesPathExist(temp_path_)); wstring dump_path(temp_path_); google_breakpad::CrashGenerationServer server( - kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true, - &dump_path); + kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); ASSERT_TRUE(dump_file.empty() && full_dump_file.empty()); @@ -291,8 +291,8 @@ TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) { ASSERT_TRUE(DoesPathExist(temp_path_)); wstring dump_path(temp_path_); google_breakpad::CrashGenerationServer server( - kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true, - &dump_path); + kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); ASSERT_TRUE(dump_file.empty() && full_dump_file.empty()); diff --git a/thirdparty/breakpad/client/windows/unittests/minidump_test.cc b/thirdparty/breakpad/client/windows/unittests/minidump_test.cc old mode 100755 new mode 100644 index 5b2960b06..ab7ae3b74 --- a/thirdparty/breakpad/client/windows/unittests/minidump_test.cc +++ b/thirdparty/breakpad/client/windows/unittests/minidump_test.cc @@ -1,332 +1,332 @@ -// 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 -#include -#include - -#include "../crash_generation/minidump_generator.h" -#include "dump_analysis.h" // NOLINT - -#include "gtest/gtest.h" - -namespace { - -// Minidump with stacks, PEB, TEB, and unloaded module list. -const MINIDUMP_TYPE kSmallDumpType = static_cast( - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithUnloadedModules); // Get unloaded modules when available. - -// Minidump with all of the above, plus memory referenced from stack. -const MINIDUMP_TYPE kLargerDumpType = static_cast( - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithUnloadedModules | // Get unloaded modules when available. - MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. - -// Large dump with all process memory. -const MINIDUMP_TYPE kFullDumpType = static_cast( - MiniDumpWithFullMemory | // Full memory from process. - MiniDumpWithProcessThreadData | // Get PEB and TEB. - MiniDumpWithHandleData | // Get all handle information. - MiniDumpWithUnloadedModules); // Get unloaded modules when available. - -class MinidumpTest: public testing::Test { - public: - MinidumpTest() { - wchar_t temp_dir_path[ MAX_PATH ] = {0}; - ::GetTempPath(MAX_PATH, temp_dir_path); - dump_path_ = temp_dir_path; - } - - virtual void SetUp() { - // Make sure URLMon isn't loaded into our process. - ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll")); - - // Then load and unload it to ensure we have something to - // stock the unloaded module list with. - HMODULE urlmon = ::LoadLibrary(L"urlmon.dll"); - ASSERT_TRUE(urlmon != NULL); - ASSERT_TRUE(::FreeLibrary(urlmon)); - } - - virtual void TearDown() { - if (!dump_file_.empty()) { - ::DeleteFile(dump_file_.c_str()); - dump_file_ = L""; - } - if (!full_dump_file_.empty()) { - ::DeleteFile(full_dump_file_.c_str()); - full_dump_file_ = L""; - } - } - - bool WriteDump(ULONG flags) { - using google_breakpad::MinidumpGenerator; - - // Fake exception is access violation on write to this. - EXCEPTION_RECORD ex_record = { - STATUS_ACCESS_VIOLATION, // ExceptionCode - 0, // ExceptionFlags - NULL, // ExceptionRecord; - reinterpret_cast(0xCAFEBABE), // ExceptionAddress; - 2, // NumberParameters; - { EXCEPTION_WRITE_FAULT, reinterpret_cast(this) } - }; - CONTEXT ctx_record = {}; - EXCEPTION_POINTERS ex_ptrs = { - &ex_record, - &ctx_record, - }; - - MinidumpGenerator generator(dump_path_); - - // And write a dump - bool result = generator.WriteMinidump(::GetCurrentProcess(), - ::GetCurrentProcessId(), - ::GetCurrentThreadId(), - ::GetCurrentThreadId(), - &ex_ptrs, - NULL, - static_cast(flags), - TRUE, - &dump_file_, - &full_dump_file_); - return result == TRUE; - } - - protected: - std::wstring dump_file_; - std::wstring full_dump_file_; - - std::wstring dump_path_; -}; - -// We need to be able to get file information from Windows -bool HasFileInfo(const std::wstring& file_path) { - DWORD dummy; - const wchar_t* path = file_path.c_str(); - DWORD length = ::GetFileVersionInfoSize(path, &dummy); - if (length == 0) - return NULL; - - void* data = calloc(length, 1); - if (!data) - return false; - - if (!::GetFileVersionInfo(path, dummy, length, data)) { - free(data); - return false; - } - - void* translate = NULL; - UINT page_count; - BOOL query_result = VerQueryValue( - data, - L"\\VarFileInfo\\Translation", - static_cast(&translate), - &page_count); - - free(data); - if (query_result && translate) { - return true; - } else { - return false; - } -} - -TEST_F(MinidumpTest, Version) { - API_VERSION* version = ::ImagehlpApiVersion(); - - HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll"); - ASSERT_TRUE(dbg_help != NULL); - - wchar_t dbg_help_file[1024] = {}; - ASSERT_TRUE(::GetModuleFileName(dbg_help, - dbg_help_file, - sizeof(dbg_help_file) / - sizeof(*dbg_help_file))); - ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL); - -// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version(); -} - -TEST_F(MinidumpTest, Normal) { - EXPECT_TRUE(WriteDump(MiniDumpNormal)); - DumpAnalysis mini(dump_file_); - - // We expect threads, modules and some memory. - EXPECT_TRUE(mini.HasStream(ThreadListStream)); - EXPECT_TRUE(mini.HasStream(ModuleListStream)); - EXPECT_TRUE(mini.HasStream(MemoryListStream)); - EXPECT_TRUE(mini.HasStream(ExceptionStream)); - EXPECT_TRUE(mini.HasStream(SystemInfoStream)); - EXPECT_TRUE(mini.HasStream(MiscInfoStream)); - - EXPECT_FALSE(mini.HasStream(ThreadExListStream)); - EXPECT_FALSE(mini.HasStream(Memory64ListStream)); - EXPECT_FALSE(mini.HasStream(CommentStreamA)); - EXPECT_FALSE(mini.HasStream(CommentStreamW)); - EXPECT_FALSE(mini.HasStream(HandleDataStream)); - EXPECT_FALSE(mini.HasStream(FunctionTableStream)); - EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream)); - EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); - EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); - EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); - EXPECT_FALSE(mini.HasStream(TokenStream)); - - // We expect no PEB nor TEBs in this dump. - EXPECT_FALSE(mini.HasTebs()); - EXPECT_FALSE(mini.HasPeb()); - - // We expect no off-stack memory in this dump. - EXPECT_FALSE(mini.HasMemory(this)); -} - -TEST_F(MinidumpTest, SmallDump) { - ASSERT_TRUE(WriteDump(kSmallDumpType)); - DumpAnalysis mini(dump_file_); - - EXPECT_TRUE(mini.HasStream(ThreadListStream)); - EXPECT_TRUE(mini.HasStream(ModuleListStream)); - EXPECT_TRUE(mini.HasStream(MemoryListStream)); - EXPECT_TRUE(mini.HasStream(ExceptionStream)); - EXPECT_TRUE(mini.HasStream(SystemInfoStream)); - EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); - EXPECT_TRUE(mini.HasStream(MiscInfoStream)); - - // We expect PEB and TEBs in this dump. - EXPECT_TRUE(mini.HasTebs()); - EXPECT_TRUE(mini.HasPeb()); - - EXPECT_FALSE(mini.HasStream(ThreadExListStream)); - EXPECT_FALSE(mini.HasStream(Memory64ListStream)); - EXPECT_FALSE(mini.HasStream(CommentStreamA)); - EXPECT_FALSE(mini.HasStream(CommentStreamW)); - EXPECT_FALSE(mini.HasStream(HandleDataStream)); - EXPECT_FALSE(mini.HasStream(FunctionTableStream)); - EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); - EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); - EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); - EXPECT_FALSE(mini.HasStream(TokenStream)); - - // We expect no off-stack memory in this dump. - EXPECT_FALSE(mini.HasMemory(this)); -} - -TEST_F(MinidumpTest, LargerDump) { - ASSERT_TRUE(WriteDump(kLargerDumpType)); - DumpAnalysis mini(dump_file_); - - // The dump should have all of these streams. - EXPECT_TRUE(mini.HasStream(ThreadListStream)); - EXPECT_TRUE(mini.HasStream(ModuleListStream)); - EXPECT_TRUE(mini.HasStream(MemoryListStream)); - EXPECT_TRUE(mini.HasStream(ExceptionStream)); - EXPECT_TRUE(mini.HasStream(SystemInfoStream)); - EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); - EXPECT_TRUE(mini.HasStream(MiscInfoStream)); - - // We expect memory referenced by stack in this dump. - EXPECT_TRUE(mini.HasMemory(this)); - - // We expect PEB and TEBs in this dump. - EXPECT_TRUE(mini.HasTebs()); - EXPECT_TRUE(mini.HasPeb()); - - EXPECT_FALSE(mini.HasStream(ThreadExListStream)); - EXPECT_FALSE(mini.HasStream(Memory64ListStream)); - EXPECT_FALSE(mini.HasStream(CommentStreamA)); - EXPECT_FALSE(mini.HasStream(CommentStreamW)); - EXPECT_FALSE(mini.HasStream(HandleDataStream)); - EXPECT_FALSE(mini.HasStream(FunctionTableStream)); - EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); - EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); - EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); - EXPECT_FALSE(mini.HasStream(TokenStream)); -} - -TEST_F(MinidumpTest, FullDump) { - ASSERT_TRUE(WriteDump(kFullDumpType)); - ASSERT_TRUE(dump_file_ != L""); - ASSERT_TRUE(full_dump_file_ != L""); - DumpAnalysis mini(dump_file_); - DumpAnalysis full(full_dump_file_); - - // Either dumps can contain part of the information. - - // The dump should have all of these streams. - EXPECT_TRUE(mini.HasStream(ThreadListStream)); - EXPECT_TRUE(full.HasStream(ThreadListStream)); - EXPECT_TRUE(mini.HasStream(ModuleListStream)); - EXPECT_TRUE(full.HasStream(ModuleListStream)); - EXPECT_TRUE(mini.HasStream(ExceptionStream)); - EXPECT_TRUE(full.HasStream(ExceptionStream)); - EXPECT_TRUE(mini.HasStream(SystemInfoStream)); - EXPECT_TRUE(full.HasStream(SystemInfoStream)); - EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); - EXPECT_TRUE(full.HasStream(UnloadedModuleListStream)); - EXPECT_TRUE(mini.HasStream(MiscInfoStream)); - EXPECT_TRUE(full.HasStream(MiscInfoStream)); - EXPECT_TRUE(mini.HasStream(HandleDataStream)); - EXPECT_TRUE(full.HasStream(HandleDataStream)); - - // We expect memory referenced by stack in this dump. - EXPECT_FALSE(mini.HasMemory(this)); - EXPECT_TRUE(full.HasMemory(this)); - - // We expect PEB and TEBs in this dump. - EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); - EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); - - EXPECT_TRUE(mini.HasStream(MemoryListStream)); - EXPECT_TRUE(full.HasStream(Memory64ListStream)); - EXPECT_FALSE(mini.HasStream(Memory64ListStream)); - EXPECT_FALSE(full.HasStream(MemoryListStream)); - - // This is the only place we don't use OR because we want both not - // to have the streams. - EXPECT_FALSE(mini.HasStream(ThreadExListStream)); - EXPECT_FALSE(full.HasStream(ThreadExListStream)); - EXPECT_FALSE(mini.HasStream(CommentStreamA)); - EXPECT_FALSE(full.HasStream(CommentStreamA)); - EXPECT_FALSE(mini.HasStream(CommentStreamW)); - EXPECT_FALSE(full.HasStream(CommentStreamW)); - EXPECT_FALSE(mini.HasStream(FunctionTableStream)); - EXPECT_FALSE(full.HasStream(FunctionTableStream)); - EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); - EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); - EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); - EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); - EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); - EXPECT_FALSE(full.HasStream(HandleOperationListStream)); - EXPECT_FALSE(mini.HasStream(TokenStream)); - EXPECT_FALSE(full.HasStream(TokenStream)); -} - -} // namespace +// 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 +#include +#include + +#include "../crash_generation/minidump_generator.h" +#include "dump_analysis.h" // NOLINT + +#include "gtest/gtest.h" + +namespace { + +// Minidump with stacks, PEB, TEB, and unloaded module list. +const MINIDUMP_TYPE kSmallDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +// Minidump with all of the above, plus memory referenced from stack. +const MINIDUMP_TYPE kLargerDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. + +// Large dump with all process memory. +const MINIDUMP_TYPE kFullDumpType = static_cast( + MiniDumpWithFullMemory | // Full memory from process. + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithHandleData | // Get all handle information. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +class MinidumpTest: public testing::Test { + public: + MinidumpTest() { + wchar_t temp_dir_path[ MAX_PATH ] = {0}; + ::GetTempPath(MAX_PATH, temp_dir_path); + dump_path_ = temp_dir_path; + } + + virtual void SetUp() { + // Make sure URLMon isn't loaded into our process. + ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll")); + + // Then load and unload it to ensure we have something to + // stock the unloaded module list with. + HMODULE urlmon = ::LoadLibrary(L"urlmon.dll"); + ASSERT_TRUE(urlmon != NULL); + ASSERT_TRUE(::FreeLibrary(urlmon)); + } + + virtual void TearDown() { + if (!dump_file_.empty()) { + ::DeleteFile(dump_file_.c_str()); + dump_file_ = L""; + } + if (!full_dump_file_.empty()) { + ::DeleteFile(full_dump_file_.c_str()); + full_dump_file_ = L""; + } + } + + bool WriteDump(ULONG flags) { + using google_breakpad::MinidumpGenerator; + + // Fake exception is access violation on write to this. + EXCEPTION_RECORD ex_record = { + STATUS_ACCESS_VIOLATION, // ExceptionCode + 0, // ExceptionFlags + NULL, // ExceptionRecord; + reinterpret_cast(0xCAFEBABE), // ExceptionAddress; + 2, // NumberParameters; + { EXCEPTION_WRITE_FAULT, reinterpret_cast(this) } + }; + CONTEXT ctx_record = {}; + EXCEPTION_POINTERS ex_ptrs = { + &ex_record, + &ctx_record, + }; + + MinidumpGenerator generator(dump_path_); + + // And write a dump + bool result = generator.WriteMinidump(::GetCurrentProcess(), + ::GetCurrentProcessId(), + ::GetCurrentThreadId(), + ::GetCurrentThreadId(), + &ex_ptrs, + NULL, + static_cast(flags), + TRUE, + &dump_file_, + &full_dump_file_); + return result == TRUE; + } + + protected: + std::wstring dump_file_; + std::wstring full_dump_file_; + + std::wstring dump_path_; +}; + +// We need to be able to get file information from Windows +bool HasFileInfo(const std::wstring& file_path) { + DWORD dummy; + const wchar_t* path = file_path.c_str(); + DWORD length = ::GetFileVersionInfoSize(path, &dummy); + if (length == 0) + return NULL; + + void* data = calloc(length, 1); + if (!data) + return false; + + if (!::GetFileVersionInfo(path, dummy, length, data)) { + free(data); + return false; + } + + void* translate = NULL; + UINT page_count; + BOOL query_result = VerQueryValue( + data, + L"\\VarFileInfo\\Translation", + static_cast(&translate), + &page_count); + + free(data); + if (query_result && translate) { + return true; + } else { + return false; + } +} + +TEST_F(MinidumpTest, Version) { + API_VERSION* version = ::ImagehlpApiVersion(); + + HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll"); + ASSERT_TRUE(dbg_help != NULL); + + wchar_t dbg_help_file[1024] = {}; + ASSERT_TRUE(::GetModuleFileName(dbg_help, + dbg_help_file, + sizeof(dbg_help_file) / + sizeof(*dbg_help_file))); + ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL); + +// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version(); +} + +TEST_F(MinidumpTest, Normal) { + EXPECT_TRUE(WriteDump(MiniDumpNormal)); + DumpAnalysis mini(dump_file_); + + // We expect threads, modules and some memory. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + + // We expect no PEB nor TEBs in this dump. + EXPECT_FALSE(mini.HasTebs()); + EXPECT_FALSE(mini.HasPeb()); + + // We expect no off-stack memory in this dump. + EXPECT_FALSE(mini.HasMemory(this)); +} + +TEST_F(MinidumpTest, SmallDump) { + ASSERT_TRUE(WriteDump(kSmallDumpType)); + DumpAnalysis mini(dump_file_); + + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs()); + EXPECT_TRUE(mini.HasPeb()); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + + // We expect no off-stack memory in this dump. + EXPECT_FALSE(mini.HasMemory(this)); +} + +TEST_F(MinidumpTest, LargerDump) { + ASSERT_TRUE(WriteDump(kLargerDumpType)); + DumpAnalysis mini(dump_file_); + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + // We expect memory referenced by stack in this dump. + EXPECT_TRUE(mini.HasMemory(this)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs()); + EXPECT_TRUE(mini.HasPeb()); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); +} + +TEST_F(MinidumpTest, FullDump) { + ASSERT_TRUE(WriteDump(kFullDumpType)); + ASSERT_TRUE(dump_file_ != L""); + ASSERT_TRUE(full_dump_file_ != L""); + DumpAnalysis mini(dump_file_); + DumpAnalysis full(full_dump_file_); + + // Either dumps can contain part of the information. + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(full.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(full.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(full.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(full.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(full.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + EXPECT_TRUE(full.HasStream(MiscInfoStream)); + EXPECT_TRUE(mini.HasStream(HandleDataStream)); + EXPECT_TRUE(full.HasStream(HandleDataStream)); + + // We expect memory referenced by stack in this dump. + EXPECT_FALSE(mini.HasMemory(this)); + EXPECT_TRUE(full.HasMemory(this)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); + EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); + + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(full.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(full.HasStream(MemoryListStream)); + + // This is the only place we don't use OR because we want both not + // to have the streams. + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(full.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(full.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(full.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(full.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(full.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + EXPECT_FALSE(full.HasStream(TokenStream)); +} + +} // namespace diff --git a/thirdparty/breakpad/common/basictypes.h b/thirdparty/breakpad/common/basictypes.h new file mode 100644 index 000000000..694f7022f --- /dev/null +++ b/thirdparty/breakpad/common/basictypes.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef COMMON_BASICTYPES_H_ +#define COMMON_BASICTYPES_H_ + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#endif // COMMON_BASICTYPES_H_ diff --git a/thirdparty/breakpad/common/dwarf/dwarf2diehandler.cc b/thirdparty/breakpad/common/dwarf/dwarf2diehandler.cc index 5d0192935..163995475 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2diehandler.cc +++ b/thirdparty/breakpad/common/dwarf/dwarf2diehandler.cc @@ -176,11 +176,21 @@ void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, void DIEDispatcher::ProcessAttributeString(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, - const string& data) { + const std::string& data) { HandlerStack ¤t = die_handlers_.top(); // This had better be an attribute of the DIE we were meant to handle. assert(offset == current.offset_); current.handler_->ProcessAttributeString(attr, form, data); } +void DIEDispatcher::ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSignature(attr, form, signature); +} + } // namespace dwarf2reader diff --git a/thirdparty/breakpad/common/dwarf/dwarf2diehandler.h b/thirdparty/breakpad/common/dwarf/dwarf2diehandler.h index 4c9d7536a..5d899bf89 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2diehandler.h +++ b/thirdparty/breakpad/common/dwarf/dwarf2diehandler.h @@ -208,7 +208,10 @@ class DIEHandler { uint64 len) { } virtual void ProcessAttributeString(enum DwarfAttribute attr, enum DwarfForm form, - const string& data) { } + const std::string& data) { } + virtual void ProcessAttributeSignature(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signture) { } // Once we have reported all the DIE's attributes' values, we call // this member function. If it returns false, we skip all the DIE's @@ -313,7 +316,11 @@ class DIEDispatcher: public Dwarf2Handler { void ProcessAttributeString(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, - const string &data); + const std::string &data); + void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature); void EndDIE(uint64 offset); private: @@ -347,7 +354,7 @@ class DIEDispatcher: public Dwarf2Handler { // - When we decide to ignore a subtree, we only push an entry on // the stack for the root of the tree being ignored, rather than // pushing lots of stack entries with handler_ set to NULL. - stack die_handlers_; + std::stack die_handlers_; // The root handler. We don't push it on die_handlers_ until we // actually get the StartDIE call for the root. diff --git a/thirdparty/breakpad/common/dwarf/dwarf2diehandler_unittest.cc b/thirdparty/breakpad/common/dwarf/dwarf2diehandler_unittest.cc index 67ccb95f8..186b951ca 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2diehandler_unittest.cc +++ b/thirdparty/breakpad/common/dwarf/dwarf2diehandler_unittest.cc @@ -32,10 +32,16 @@ // dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. +#include +#include + #include "breakpad_googletest_includes.h" #include "common/dwarf/dwarf2diehandler.h" +using std::make_pair; +using std::string; + using ::testing::_; using ::testing::ContainerEq; using ::testing::ElementsAreArray; @@ -65,6 +71,8 @@ class MockDIEHandler: public DIEHandler { void(DwarfAttribute, DwarfForm, const char *, uint64)); MOCK_METHOD3(ProcessAttributeString, void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); MOCK_METHOD0(EndAttributes, bool()); MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, const AttributeList &)); @@ -83,6 +91,8 @@ class MockRootDIEHandler: public RootDIEHandler { void(DwarfAttribute, DwarfForm, const char *, uint64)); MOCK_METHOD3(ProcessAttributeString, void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); MOCK_METHOD0(EndAttributes, bool()); MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, const AttributeList &)); @@ -238,6 +248,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) { (DwarfForm) 0x15762fec, StrEq(str))) .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSignature((DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL)) + .WillOnce(Return()); EXPECT_CALL(mock_root_handler, EndAttributes()) .WillOnce(Return(true)); EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _)) @@ -279,6 +294,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) { (DwarfAttribute) 0x310ed065, (DwarfForm) 0x15762fec, str); + die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL); // Finish the root DIE (and thus the CU). die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); diff --git a/thirdparty/breakpad/common/dwarf/dwarf2enums.h b/thirdparty/breakpad/common/dwarf/dwarf2enums.h index 832a17ca2..5565d66e1 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2enums.h +++ b/thirdparty/breakpad/common/dwarf/dwarf2enums.h @@ -143,7 +143,13 @@ enum DwarfForm { DW_FORM_ref4 = 0x13, DW_FORM_ref8 = 0x14, DW_FORM_ref_udata = 0x15, - DW_FORM_indirect = 0x16 + DW_FORM_indirect = 0x16, + + // Added in DWARF 4: + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20 }; // Attribute names and codes diff --git a/thirdparty/breakpad/common/dwarf/dwarf2reader.cc b/thirdparty/breakpad/common/dwarf/dwarf2reader.cc index 63d1ffb79..435664679 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2reader.cc +++ b/thirdparty/breakpad/common/dwarf/dwarf2reader.cc @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -74,7 +75,7 @@ void CompilationUnit::ReadAbbrevs() { iter = sections_.find("__debug_abbrev"); assert(iter != sections_.end()); - abbrevs_ = new vector; + abbrevs_ = new std::vector; abbrevs_->resize(1); // The only way to check whether we are reading over the end of the @@ -121,7 +122,7 @@ void CompilationUnit::ReadAbbrevs() { const enum DwarfAttribute name = static_cast(nametemp); const enum DwarfForm form = static_cast(formtemp); - abbrev.attributes.push_back(make_pair(name, form)); + abbrev.attributes.push_back(std::make_pair(name, form)); } assert(abbrev.number == abbrevs_->size()); abbrevs_->push_back(abbrev); @@ -150,41 +151,35 @@ const char* CompilationUnit::SkipAttribute(const char* start, &len)); start += len; return SkipAttribute(start, form); - break; + case DW_FORM_flag_present: + return start; case DW_FORM_data1: case DW_FORM_flag: case DW_FORM_ref1: return start + 1; - break; case DW_FORM_ref2: case DW_FORM_data2: return start + 2; - break; case DW_FORM_ref4: case DW_FORM_data4: return start + 4; - break; case DW_FORM_ref8: case DW_FORM_data8: + case DW_FORM_ref_sig8: return start + 8; - break; case DW_FORM_string: return start + strlen(start) + 1; - break; case DW_FORM_udata: case DW_FORM_ref_udata: reader_->ReadUnsignedLEB128(start, &len); return start + len; - break; case DW_FORM_sdata: reader_->ReadSignedLEB128(start, &len); return start + len; - break; case DW_FORM_addr: return start + reader_->AddressSize(); - break; case DW_FORM_ref_addr: // DWARF2 and 3 differ on whether ref_addr is address size or // offset size. @@ -194,27 +189,21 @@ const char* CompilationUnit::SkipAttribute(const char* start, } else if (header_.version == 3) { return start + reader_->OffsetSize(); } - break; case DW_FORM_block1: return start + 1 + reader_->ReadOneByte(start); - break; case DW_FORM_block2: return start + 2 + reader_->ReadTwoBytes(start); - break; case DW_FORM_block4: return start + 4 + reader_->ReadFourBytes(start); - break; - case DW_FORM_block: { + case DW_FORM_block: + case DW_FORM_exprloc: { uint64 size = reader_->ReadUnsignedLEB128(start, &len); return start + size + len; } - break; case DW_FORM_strp: - return start + reader_->OffsetSize(); - break; - default: - fprintf(stderr,"Unhandled form type"); + case DW_FORM_sec_offset: + return start + reader_->OffsetSize(); } fprintf(stderr,"Unhandled form type"); return NULL; @@ -326,85 +315,78 @@ const char* CompilationUnit::ProcessAttribute( &len)); start += len; return ProcessAttribute(dieoffset, start, attr, form); - break; + case DW_FORM_flag_present: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1); + return start; case DW_FORM_data1: case DW_FORM_flag: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadOneByte(start)); return start + 1; - break; case DW_FORM_data2: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadTwoBytes(start)); return start + 2; - break; case DW_FORM_data4: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadFourBytes(start)); return start + 4; - break; case DW_FORM_data8: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadEightBytes(start)); return start + 8; - break; case DW_FORM_string: { const char* str = start; handler_->ProcessAttributeString(dieoffset, attr, form, str); return start + strlen(str) + 1; } - break; case DW_FORM_udata: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len)); return start + len; - break; case DW_FORM_sdata: handler_->ProcessAttributeSigned(dieoffset, attr, form, reader_->ReadSignedLEB128(start, &len)); return start + len; - break; case DW_FORM_addr: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadAddress(start)); return start + reader_->AddressSize(); - break; + case DW_FORM_sec_offset: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); case DW_FORM_ref1: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadOneByte(start) + offset_from_section_start_); return start + 1; - break; case DW_FORM_ref2: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadTwoBytes(start) + offset_from_section_start_); return start + 2; - break; case DW_FORM_ref4: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadFourBytes(start) + offset_from_section_start_); return start + 4; - break; case DW_FORM_ref8: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadEightBytes(start) + offset_from_section_start_); return start + 8; - break; case DW_FORM_ref_udata: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len) + offset_from_section_start_); return start + len; - break; case DW_FORM_ref_addr: // DWARF2 and 3 differ on whether ref_addr is address size or // offset size. @@ -419,35 +401,36 @@ const char* CompilationUnit::ProcessAttribute( return start + reader_->OffsetSize(); } break; + case DW_FORM_ref_sig8: + handler_->ProcessAttributeSignature(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; case DW_FORM_block1: { uint64 datalen = reader_->ReadOneByte(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, - datalen); + datalen); return start + 1 + datalen; } - break; case DW_FORM_block2: { uint64 datalen = reader_->ReadTwoBytes(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, - datalen); + datalen); return start + 2 + datalen; } - break; case DW_FORM_block4: { uint64 datalen = reader_->ReadFourBytes(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, - datalen); + datalen); return start + 4 + datalen; } - break; - case DW_FORM_block: { + case DW_FORM_block: + case DW_FORM_exprloc: { uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, - datalen); + datalen); return start + datalen + len; } - break; case DW_FORM_strp: { assert(string_buffer_ != NULL); @@ -459,11 +442,8 @@ const char* CompilationUnit::ProcessAttribute( str); return start + reader_->OffsetSize(); } - break; - default: - fprintf(stderr, "Unhandled form type"); } - fprintf(stderr, "Unhandled form type"); + fprintf(stderr, "Unhandled form type\n"); return NULL; } @@ -493,7 +473,7 @@ void CompilationUnit::ProcessDIEs() { else lengthstart += 4; - stack die_stack; + std::stack die_stack; while (dieptr < (lengthstart + header_.length)) { // We give the user the absolute offset from the beginning of @@ -583,7 +563,7 @@ void LineInfo::ReadHeader() { header_.opcode_base = reader_->ReadOneByte(lineptr); lineptr += 1; - header_.std_opcode_lengths = new vector; + header_.std_opcode_lengths = new std::vector; header_.std_opcode_lengths->resize(header_.opcode_base + 1); (*header_.std_opcode_lengths)[0] = 0; for (int i = 1; i < header_.opcode_base; i++) { @@ -1024,7 +1004,7 @@ class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule { // Rule: EXPRESSION evaluates to the address at which the register is saved. class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { public: - explicit ExpressionRule(const string &expression) + explicit ExpressionRule(const std::string &expression) : expression_(expression) { } ~ExpressionRule() { } bool Handle(Handler *handler, uint64 address, int reg) const { @@ -1038,13 +1018,13 @@ class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { } Rule *Copy() const { return new ExpressionRule(*this); } private: - string expression_; + std::string expression_; }; // Rule: EXPRESSION evaluates to the address at which the register is saved. class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { public: - explicit ValExpressionRule(const string &expression) + explicit ValExpressionRule(const std::string &expression) : expression_(expression) { } ~ValExpressionRule() { } bool Handle(Handler *handler, uint64 address, int reg) const { @@ -1059,7 +1039,7 @@ class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { } Rule *Copy() const { return new ValExpressionRule(*this); } private: - string expression_; + std::string expression_; }; // A map from register numbers to rules. @@ -1096,7 +1076,7 @@ class CallFrameInfo::RuleMap { private: // A map from register numbers to Rules. - typedef map RuleByNumber; + typedef std::map RuleByNumber; // Remove all register rules and clear cfa_rule_. void Clear(); @@ -1240,7 +1220,7 @@ class CallFrameInfo::State { unsigned register_number; // A register number. uint64 offset; // An offset or address. long signed_offset; // A signed offset. - string expression; // A DWARF expression. + std::string expression; // A DWARF expression. }; // Parse CFI instruction operands from STATE's instruction stream as @@ -1341,7 +1321,7 @@ class CallFrameInfo::State { // A stack of saved states, for DW_CFA_remember_state and // DW_CFA_restore_state. - stack saved_rules_; + std::stack saved_rules_; }; bool CallFrameInfo::State::InterpretCIE(const CIE &cie) { @@ -1427,7 +1407,7 @@ bool CallFrameInfo::State::ParseOperands(const char *format, if (len > bytes_left || expression_length > bytes_left - len) return ReportIncomplete(); cursor_ += len; - operands->expression = string(cursor_, expression_length); + operands->expression = std::string(cursor_, expression_length); cursor_ += expression_length; break; } @@ -1877,20 +1857,14 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) { cie->version = reader_->ReadOneByte(cursor); cursor++; - // If we don't recognize the version, we can't parse any more fields - // of the CIE. For DWARF CFI, we handle versions 1 through 3 (there - // was never a version 2 of CFI data). For .eh_frame, we handle only - // version 1. - if (eh_frame_) { - if (cie->version != 1) { - reporter_->UnrecognizedVersion(cie->offset, cie->version); - return false; - } - } else { - if (cie->version < 1 || cie->version > 3) { - reporter_->UnrecognizedVersion(cie->offset, cie->version); - return false; - } + // If we don't recognize the version, we can't parse any more fields of the + // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a + // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well; + // the difference between those versions seems to be the same as for + // .debug_frame. + if (cie->version < 1 || cie->version > 3) { + reporter_->UnrecognizedVersion(cie->offset, cie->version); + return false; } const char *augmentation_start = cursor; @@ -1898,7 +1872,8 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) { memchr(augmentation_start, '\0', cie->end - augmentation_start); if (! augmentation_end) return ReportIncomplete(cie); cursor = static_cast(augmentation_end); - cie->augmentation = string(augmentation_start, cursor - augmentation_start); + cie->augmentation = std::string(augmentation_start, + cursor - augmentation_start); // Skip the terminating '\0'. cursor++; @@ -2285,7 +2260,7 @@ void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) { } void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset, - const string &aug) { + const std::string &aug) { fprintf(stderr, "%s: CFI frame description entry at offset 0x%llx in '%s':" " CIE specifies unrecognized augmentation: '%s'\n", diff --git a/thirdparty/breakpad/common/dwarf/dwarf2reader.h b/thirdparty/breakpad/common/dwarf/dwarf2reader.h index 5a255238f..cd61fb555 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2reader.h +++ b/thirdparty/breakpad/common/dwarf/dwarf2reader.h @@ -50,8 +50,6 @@ #include "common/dwarf/dwarf2enums.h" #include "common/dwarf/types.h" -using namespace std; - namespace dwarf2reader { struct LineStateMachine; class Dwarf2Handler; @@ -59,8 +57,9 @@ class LineInfoHandler; // This maps from a string naming a section to a pair containing a // the data for the section, and the size of the section. -typedef map > SectionMap; -typedef list > AttributeList; +typedef std::map > SectionMap; +typedef std::list > + AttributeList; typedef AttributeList::iterator AttributeIterator; typedef AttributeList::const_iterator ConstAttributeIterator; @@ -75,7 +74,7 @@ struct LineInfoHeader { uint8 opcode_base; // Use a pointer so that signalsafe_addr2line is able to use this structure // without heap allocation problem. - vector *std_opcode_lengths; + std::vector *std_opcode_lengths; }; class LineInfo { @@ -157,7 +156,7 @@ class LineInfoHandler { // Called when we define a directory. NAME is the directory name, // DIR_NUM is the directory number - virtual void DefineDir(const string& name, uint32 dir_num) { } + virtual void DefineDir(const std::string& name, uint32 dir_num) { } // Called when we define a filename. NAME is the filename, FILE_NUM // is the file number which is -1 if the file index is the next @@ -166,7 +165,7 @@ class LineInfoHandler { // directory index for the directory name of this file, MOD_TIME is // the modification time of the file, and LENGTH is the length of // the file - virtual void DefineFile(const string& name, int32 file_num, + virtual void DefineFile(const std::string& name, int32 file_num, uint32 dir_num, uint64 mod_time, uint64 length) { } @@ -313,7 +312,7 @@ class CompilationUnit { // Set of DWARF2/3 abbreviations for this compilation unit. Indexed // by abbreviation number, which means that abbrevs_[0] is not // valid. - vector* abbrevs_; + std::vector* abbrevs_; // String section buffer and length, if we have a string section. // This is here to avoid doing a section lookup for strings in @@ -392,7 +391,16 @@ class Dwarf2Handler { virtual void ProcessAttributeString(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, - const string& data) { } + const std::string& data) { } + + // Called when we have an attribute whose value is the 64-bit signature + // of a type unit in the .debug_types section. OFFSET is the offset of + // the DIE whose attribute we're reporting. ATTR and FORM are the + // attribute's name and form. SIGNATURE is the type unit's signature. + virtual void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { } // Called when finished processing the DIE at OFFSET. // Because DWARF2/3 specifies a tree of DIEs, you may get starts @@ -691,7 +699,7 @@ class CallFrameInfo { // A common information entry (CIE). struct CIE: public Entry { uint8 version; // CFI data version number - string augmentation; // vendor format extension markers + std::string augmentation; // vendor format extension markers uint64 code_alignment_factor; // scale for code address adjustments int data_alignment_factor; // scale for stack pointer adjustments unsigned return_address_register; // which register holds the return addr @@ -825,7 +833,7 @@ class CallFrameInfo::Handler { // process a given FDE, the parser reiterates the appropriate CIE's // contents at the beginning of the FDE's rules. virtual bool Entry(size_t offset, uint64 address, uint64 length, - uint8 version, const string &augmentation, + uint8 version, const std::string &augmentation, unsigned return_address) = 0; // When the Entry function returns true, the parser calls these @@ -874,13 +882,13 @@ class CallFrameInfo::Handler { // At ADDRESS, the DWARF expression EXPRESSION yields the address at // which REG was saved. virtual bool ExpressionRule(uint64 address, int reg, - const string &expression) = 0; + const std::string &expression) = 0; // At ADDRESS, the DWARF expression EXPRESSION yields the caller's // value for REG. (This rule doesn't provide an address at which the // register's value is saved.) virtual bool ValExpressionRule(uint64 address, int reg, - const string &expression) = 0; + const std::string &expression) = 0; // Indicate that the rules for the address range reported by the // last call to Entry are complete. End should return true if @@ -957,8 +965,8 @@ class CallFrameInfo::Reporter { // in a Mach-O section named __debug_frame. If we support // Linux-style exception handling data, we could be reading an // .eh_frame section. - Reporter(const string &filename, - const string §ion = ".debug_frame") + Reporter(const std::string &filename, + const std::string §ion = ".debug_frame") : filename_(filename), section_(section) { } virtual ~Reporter() { } @@ -990,7 +998,7 @@ class CallFrameInfo::Reporter { // which we don't recognize. We cannot parse DWARF CFI if it uses // augmentations we don't recognize. virtual void UnrecognizedAugmentation(uint64 offset, - const string &augmentation); + const std::string &augmentation); // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not // a valid encoding. @@ -1031,10 +1039,10 @@ class CallFrameInfo::Reporter { protected: // The name of the file whose CFI we're reading. - string filename_; + std::string filename_; // The name of the CFI section in that file. - string section_; + std::string section_; }; } // namespace dwarf2reader diff --git a/thirdparty/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc b/thirdparty/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc index 1a2431c7b..271d1b6a7 100644 --- a/thirdparty/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc +++ b/thirdparty/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc @@ -33,6 +33,7 @@ #include +#include #include // The '.eh_frame' format, used by the Linux C++ ABI for exception @@ -75,6 +76,7 @@ using dwarf2reader::ENDIANNESS_LITTLE; using dwarf2reader::ByteReader; using dwarf2reader::CallFrameInfo; +using std::string; using std::vector; using testing::InSequence; using testing::Return; diff --git a/thirdparty/breakpad/common/dwarf/dwarf2reader_die_unittest.cc b/thirdparty/breakpad/common/dwarf/dwarf2reader_die_unittest.cc new file mode 100644 index 000000000..e76fcae77 --- /dev/null +++ b/thirdparty/breakpad/common/dwarf/dwarf2reader_die_unittest.cc @@ -0,0 +1,486 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit + +#include + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader_test_common.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; + +using dwarf2reader::AttributeList; +using dwarf2reader::ByteReader; +using dwarf2reader::CompilationUnit; +using dwarf2reader::Dwarf2Handler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfHasChild; +using dwarf2reader::DwarfTag; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::SectionMap; + +using std::string; +using std::vector; +using testing::InSequence; +using testing::Pointee; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::TestWithParam; +using testing::_; + +class MockDwarf2Handler: public Dwarf2Handler { + public: + MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version)); + MOCK_METHOD3(StartDIE, bool(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs)); + MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data)); + MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len)); + MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data)); + MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 signature)); + MOCK_METHOD1(EndDIE, void(uint64 offset)); +}; + +struct DIEFixture { + + DIEFixture() { + // Fix the initial offset of the .debug_info and .debug_abbrev sections. + info.start() = 0; + abbrevs.start() = 0; + + // Default expectations for the data handler. + EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, StartDIE(_, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); + EXPECT_CALL(handler, EndDIE(_)).Times(0); + } + + // Return a reference to a section map whose .debug_info section refers + // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This + // function returns a reference to the same SectionMap each time; new + // calls wipe out maps established by earlier calls. + const SectionMap &MakeSectionMap() { + // Copy the sections' contents into strings that will live as long as + // the map itself. + assert(info.GetContents(&info_contents)); + assert(abbrevs.GetContents(&abbrevs_contents)); + section_map.clear(); + section_map[".debug_info"].first = info_contents.data(); + section_map[".debug_info"].second = info_contents.size(); + section_map[".debug_abbrev"].first = abbrevs_contents.data(); + section_map[".debug_abbrev"].second = abbrevs_contents.size(); + return section_map; + } + + TestCompilationUnit info; + TestAbbrevTable abbrevs; + MockDwarf2Handler handler; + string abbrevs_contents, info_contents; + SectionMap section_map; +}; + +struct DwarfHeaderParams { + DwarfHeaderParams(Endianness endianness, size_t format_size, + int version, size_t address_size) + : endianness(endianness), format_size(format_size), + version(version), address_size(address_size) { } + Endianness endianness; + size_t format_size; // 4-byte or 8-byte DWARF offsets + int version; + size_t address_size; +}; + +class DwarfHeader: public DIEFixture, + public TestWithParam { }; + +TEST_P(DwarfHeader, Header) { + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_children_yes) + .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + + info.Header(GetParam().version, abbrev_table, GetParam().address_size) + .ULEB128(1) // DW_TAG_compile_unit, with children + .AppendCString("sam") // DW_AT_name, DW_FORM_string + .D8(0); // end of children + info.Finish(); + + { + InSequence s; + EXPECT_CALL(handler, + StartCompilationUnit(0, GetParam().address_size, + GetParam().format_size, _, + GetParam().version)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit, _)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string, + "sam")) + .WillOnce(Return()); + EXPECT_CALL(handler, EndDIE(_)) + .WillOnce(Return()); + } + + ByteReader byte_reader(GetParam().endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); + EXPECT_EQ(parser.Start(), info_contents.size()); +} + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfHeader, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); + +struct DwarfFormsFixture: public DIEFixture { + // Start a compilation unit, as directed by |params|, containing one + // childless DIE of the given tag, with one attribute of the given name + // and form. The 'info' fixture member is left just after the abbrev + // code, waiting for the attribute value to be appended. + void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, + DwarfTag tag, DwarfAttribute name, + DwarfForm form) { + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) + .Attribute(name, form) + .EndAbbrev() + .EndTable(); + + // Create the compilation unit, up to the attribute value. + info.set_format_size(params.format_size); + info.set_endianness(params.endianness); + info.Header(params.version, abbrev_table, params.address_size) + .ULEB128(1); // abbrev code + } + + // Set up handler to expect a compilation unit matching |params|, + // containing one childless DIE of the given tag, in the sequence s. Stop + // just before the expectations. + void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, + DwarfTag tag, uint64 offset=0) { + EXPECT_CALL(handler, + StartCompilationUnit(offset, params.address_size, + params.format_size, _, + params.version)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, tag, _)) + .InSequence(s) + .WillOnce(Return(true)); + } + + void ExpectEndCompilationUnit() { + EXPECT_CALL(handler, EndDIE(_)) + .InSequence(s) + .WillOnce(Return()); + } + + void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { + ByteReader byte_reader(params.endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler); + EXPECT_EQ(offset + parser.Start(), info_contents.size()); + } + + // The sequence to which the fixture's methods append expectations. + Sequence s; +}; + +struct DwarfForms: public DwarfFormsFixture, + public TestWithParam { }; + +TEST_P(DwarfForms, addr) { + StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr); + u_int64_t value; + if (GetParam().address_size == 4) { + value = 0xc8e9ffcc; + info.D32(value); + } else { + value = 0xe942517fc2768564ULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2_empty) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + info.D16(0); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + _, 0)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + unsigned char data[258]; + memset(data, '*', sizeof(data)); + info.D16(sizeof(data)) + .Append(data, sizeof(data)); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + Pointee('*'), 258)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, flag_present) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, + (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present); + // DW_FORM_flag_present occupies no space in the DIE. + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); + EXPECT_CALL(handler, + ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present, + 1)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, sec_offset) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, + (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset); + u_int64_t value; + if (GetParam().format_size == 4) { + value = 0xacc9c388; + info.D32(value); + } else { + value = 0xcffe5696ffe3ed0aULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, exprloc) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, + (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc); + info.ULEB128(29) + .Append(29, 173); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc, + Pointee(173), 29)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, ref_sig8) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// A value passed to ProcessAttributeSignature is just an absolute number, +// not an offset within the compilation unit as most of the other +// DW_FORM_ref forms are. Check that the reader doesn't try to apply any +// offset to the signature, by reading it from a compilation unit that does +// not start at the beginning of the section. +TEST_P(DwarfForms, ref_sig8_not_first) { + info.Append(98, '*'); + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam(), 98); +} + +// Tests for the other attribute forms could go here. + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfForms, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); diff --git a/thirdparty/breakpad/common/dwarf/dwarf2reader_test_common.h b/thirdparty/breakpad/common/dwarf/dwarf2reader_test_common.h new file mode 100644 index 000000000..e46931a4e --- /dev/null +++ b/thirdparty/breakpad/common/dwarf/dwarf2reader_test_common.h @@ -0,0 +1,149 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader_test_common.h: Define TestCompilationUnit and +// TestAbbrevTable, classes for creating properly (and improperly) +// formatted DWARF compilation unit data for unit tests. + +#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ +#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ + +#include "common/test_assembler.h" +#include "common/dwarf/dwarf2enums.h" + +// A subclass of test_assembler::Section, specialized for constructing +// DWARF compilation units. +class TestCompilationUnit: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef google_breakpad::test_assembler::Label Label; + + // Set the section's DWARF format size (the 32-bit DWARF format or the + // 64-bit DWARF format, for lengths and section offsets --- not the + // address size) to format_size. + void set_format_size(size_t format_size) { + assert(format_size == 4 || format_size == 8); + format_size_ = format_size; + } + + // Append a DWARF section offset value, of the appropriate size for this + // compilation unit. + template + void SectionOffset(T offset) { + if (format_size_ == 4) + D32(offset); + else + D64(offset); + } + + // Append a DWARF compilation unit header to the section, with the given + // DWARF version, abbrev table offset, and address size. + TestCompilationUnit &Header(int version, const Label &abbrev_offset, + size_t address_size) { + if (format_size_ == 4) { + D32(length_); + } else { + D32(0xffffffff); + D64(length_); + } + post_length_offset_ = Size(); + D16(version); + SectionOffset(abbrev_offset); + D8(address_size); + return *this; + } + + // Mark the end of this header's DIEs. + TestCompilationUnit &Finish() { + length_ = Size() - post_length_offset_; + return *this; + } + + private: + // The DWARF format size for this compilation unit. + size_t format_size_; + + // The offset of the point in the compilation unit header immediately + // after the initial length field. + u_int64_t post_length_offset_; + + // The length of the compilation unit, not including the initial length field. + Label length_; +}; + +// A subclass of test_assembler::Section specialized for constructing DWARF +// abbreviation tables. +class TestAbbrevTable: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef dwarf2reader::DwarfHasChild DwarfHasChild; + typedef google_breakpad::test_assembler::Label Label; + + // Start a new abbreviation table entry for abbreviation code |code|, + // encoding a DIE whose tag is |tag|, and which has children if and only + // if |has_children| is true. + TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { + assert(code != 0); + ULEB128(code); + ULEB128(static_cast(tag)); + D8(static_cast(has_children)); + return *this; + }; + + // Add an attribute to the current abbreviation code whose name is |name| + // and whose form is |form|. + TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { + ULEB128(static_cast(name)); + ULEB128(static_cast(form)); + return *this; + } + + // Finish the current abbreviation code. + TestAbbrevTable &EndAbbrev() { + ULEB128(0); + ULEB128(0); + return *this; + } + + // Finish the current abbreviation table. + TestAbbrevTable &EndTable() { + ULEB128(0); + return *this; + } +}; + +#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ diff --git a/thirdparty/breakpad/common/dwarf/functioninfo.cc b/thirdparty/breakpad/common/dwarf/functioninfo.cc index d8ab7da9d..4a080458d 100644 --- a/thirdparty/breakpad/common/dwarf/functioninfo.cc +++ b/thirdparty/breakpad/common/dwarf/functioninfo.cc @@ -45,8 +45,8 @@ namespace dwarf2reader { -CULineInfoHandler::CULineInfoHandler(vector* files, - vector* dirs, +CULineInfoHandler::CULineInfoHandler(std::vector* files, + std::vector* dirs, LineMap* linemap):linemap_(linemap), files_(files), dirs_(dirs) { @@ -61,13 +61,13 @@ CULineInfoHandler::CULineInfoHandler(vector* files, files->push_back(s); } -void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { +void CULineInfoHandler::DefineDir(const std::string& name, uint32 dir_num) { // These should never come out of order, actually assert(dir_num == dirs_->size()); dirs_->push_back(name); } -void CULineInfoHandler::DefineFile(const string& name, +void CULineInfoHandler::DefineFile(const std::string& name, int32 file_num, uint32 dir_num, uint64 mod_time, uint64 length) { assert(dir_num >= 0); @@ -75,7 +75,7 @@ void CULineInfoHandler::DefineFile(const string& name, // These should never come out of order, actually. if (file_num == (int32)files_->size() || file_num == -1) { - string dir = dirs_->at(dir_num); + std::string dir = dirs_->at(dir_num); SourceFileInfo s; s.lowpc = ULLONG_MAX; @@ -95,8 +95,10 @@ void CULineInfoHandler::DefineFile(const string& name, void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, uint32 line_num, uint32 column_num) { if (file_num < files_->size()) { - linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(), - line_num))); + linemap_->insert( + std::make_pair(address, + std::make_pair(files_->at(file_num).name.c_str(), + line_num))); if(address < files_->at(file_num).lowpc) { files_->at(file_num).lowpc = address; @@ -130,7 +132,8 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag, current_function_info_->name = ""; current_function_info_->line = 0; current_function_info_->file = ""; - offset_to_funcinfo_->insert(make_pair(offset, current_function_info_)); + offset_to_funcinfo_->insert(std::make_pair(offset, + current_function_info_)); }; // FALLTHROUGH case DW_TAG_compile_unit: @@ -146,7 +149,7 @@ bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag, void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, - const string &data) { + const std::string &data) { if (current_function_info_) { if (attr == DW_AT_name) current_function_info_->name = data; @@ -164,9 +167,9 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, assert(iter != sections_.end()); // this should be a scoped_ptr but we dont' use boost :-( - auto_ptr lireader(new LineInfo(iter->second.first + data, - iter->second.second - data, - reader_, linehandler_)); + std::auto_ptr lireader(new LineInfo(iter->second.first + data, + iter->second.second - data, + reader_, linehandler_)); lireader->Start(); } else if (current_function_info_) { switch (attr) { @@ -219,8 +222,8 @@ void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, void CUFunctionInfoHandler::EndDIE(uint64 offset) { if (current_function_info_ && current_function_info_->lowpc) - address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc, - current_function_info_)); + address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, + current_function_info_)); } } // namespace dwarf2reader diff --git a/thirdparty/breakpad/common/dwarf/functioninfo.h b/thirdparty/breakpad/common/dwarf/functioninfo.h index 901b7df50..85a31ff4b 100644 --- a/thirdparty/breakpad/common/dwarf/functioninfo.h +++ b/thirdparty/breakpad/common/dwarf/functioninfo.h @@ -46,11 +46,11 @@ namespace dwarf2reader { struct FunctionInfo { // Name of the function - string name; + std::string name; // Mangled name of the function - string mangled_name; + std::string mangled_name; // File containing this function - string file; + std::string file; // Line number for start of function. uint32 line; // Beginning address for this function @@ -61,13 +61,13 @@ struct FunctionInfo { struct SourceFileInfo { // Name of the source file name - string name; + std::string name; // Low address of source file name uint64 lowpc; }; -typedef map FunctionMap; -typedef map > LineMap; +typedef std::map FunctionMap; +typedef std::map > LineMap; // This class is a basic line info handler that fills in the dirs, // file, and linemap passed into it with the data produced from the @@ -76,18 +76,18 @@ class CULineInfoHandler: public LineInfoHandler { public: // - CULineInfoHandler(vector* files, - vector* dirs, + CULineInfoHandler(std::vector* files, + std::vector* dirs, LineMap* linemap); virtual ~CULineInfoHandler() { } // Called when we define a directory. We just place NAME into dirs_ // at position DIR_NUM. - virtual void DefineDir(const string& name, uint32 dir_num); + virtual void DefineDir(const std::string& name, uint32 dir_num); // Called when we define a filename. We just place // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. - virtual void DefineFile(const string& name, int32 file_num, + virtual void DefineFile(const std::string& name, int32 file_num, uint32 dir_num, uint64 mod_time, uint64 length); @@ -102,14 +102,14 @@ class CULineInfoHandler: public LineInfoHandler { private: LineMap* linemap_; - vector* files_; - vector* dirs_; + std::vector* files_; + std::vector* dirs_; }; class CUFunctionInfoHandler: public Dwarf2Handler { public: - CUFunctionInfoHandler(vector* files, - vector* dirs, + CUFunctionInfoHandler(std::vector* files, + std::vector* dirs, LineMap* linemap, FunctionMap* offset_to_funcinfo, FunctionMap* address_to_funcinfo, @@ -163,7 +163,7 @@ class CUFunctionInfoHandler: public Dwarf2Handler { virtual void ProcessAttributeString(uint64 offset, enum DwarfAttribute attr, enum DwarfForm form, - const string& data); + const std::string& data); // Called when finished processing the DIE at OFFSET. // Because DWARF2/3 specifies a tree of DIEs, you may get starts @@ -172,8 +172,8 @@ class CUFunctionInfoHandler: public Dwarf2Handler { virtual void EndDIE(uint64 offset); private: - vector* files_; - vector* dirs_; + std::vector* files_; + std::vector* dirs_; LineMap* linemap_; FunctionMap* offset_to_funcinfo_; FunctionMap* address_to_funcinfo_; diff --git a/thirdparty/breakpad/common/dwarf/types.h b/thirdparty/breakpad/common/dwarf/types.h index 08a325aaf..61ca4579a 100644 --- a/thirdparty/breakpad/common/dwarf/types.h +++ b/thirdparty/breakpad/common/dwarf/types.h @@ -33,6 +33,8 @@ #ifndef _COMMON_DWARF_TYPES_H__ #define _COMMON_DWARF_TYPES_H__ +#include + typedef signed char int8; typedef short int16; typedef int int32; diff --git a/thirdparty/breakpad/common/dwarf_cfi_to_module.cc b/thirdparty/breakpad/common/dwarf_cfi_to_module.cc index ed0b406dd..9aeb8ed20 100644 --- a/thirdparty/breakpad/common/dwarf_cfi_to_module.cc +++ b/thirdparty/breakpad/common/dwarf_cfi_to_module.cc @@ -132,7 +132,7 @@ string DwarfCFIToModule::RegisterName(int i) { if (reg == return_address_) return ra_name_; - if (0 <= reg && reg < register_names_.size()) + if (reg < register_names_.size()) return register_names_[reg]; reporter_->UnnamedRegister(entry_offset_, reg); diff --git a/thirdparty/breakpad/common/dwarf_cfi_to_module_unittest.cc b/thirdparty/breakpad/common/dwarf_cfi_to_module_unittest.cc index 9477296d7..3d1294902 100644 --- a/thirdparty/breakpad/common/dwarf_cfi_to_module_unittest.cc +++ b/thirdparty/breakpad/common/dwarf_cfi_to_module_unittest.cc @@ -31,9 +31,15 @@ // dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. +#include +#include + #include "breakpad_googletest_includes.h" #include "common/dwarf_cfi_to_module.h" +using std::string; +using std::vector; + using google_breakpad::Module; using google_breakpad::DwarfCFIToModule; using testing::ContainerEq; diff --git a/thirdparty/breakpad/common/dwarf_cu_to_module.cc b/thirdparty/breakpad/common/dwarf_cu_to_module.cc index 2a8b76ba1..ded5f83c1 100644 --- a/thirdparty/breakpad/common/dwarf_cu_to_module.cc +++ b/thirdparty/breakpad/common/dwarf_cu_to_module.cc @@ -440,7 +440,11 @@ void DwarfCUToModule::FuncHandler::Finish() { func->address = low_pc_; func->size = high_pc_ - low_pc_; func->parameter_size = 0; - cu_context_->functions.push_back(func); + if (func->address) { + // If the function address is zero this is a sign that this function + // description is just empty debug data and should just be discarded. + cu_context_->functions.push_back(func); + } } else if (inline_) { AbstractOrigin origin(name_); cu_context_->file_context->file_private->origins[offset_] = origin; @@ -553,7 +557,7 @@ void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) { void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { CUHeading(); - fprintf(stderr, "%s: warning: function at offset 0x%" PRIx64 " has no name\n", + fprintf(stderr, "%s: warning: function at offset 0x%llx has no name\n", filename_.c_str(), offset); } diff --git a/thirdparty/breakpad/common/dwarf_cu_to_module.h b/thirdparty/breakpad/common/dwarf_cu_to_module.h index a262f3b5c..9ab86a1d9 100644 --- a/thirdparty/breakpad/common/dwarf_cu_to_module.h +++ b/thirdparty/breakpad/common/dwarf_cu_to_module.h @@ -58,9 +58,10 @@ using dwarf2reader::DwarfTag; // Populate a google_breakpad::Module with DWARF debugging information. // // An instance of this class can be provided as a handler to a -// dwarf2reader::CompilationUnit DWARF parser. The handler uses the -// results of parsing to populate a google_breakpad::Module with -// source file, function, and source line information. +// dwarf2reader::DIEDispatcher, which can in turn be a handler for a +// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results +// of parsing to populate a google_breakpad::Module with source file, +// function, and source line information. class DwarfCUToModule: public dwarf2reader::RootDIEHandler { struct FilePrivate; public: diff --git a/thirdparty/breakpad/common/dwarf_cu_to_module_unittest.cc b/thirdparty/breakpad/common/dwarf_cu_to_module_unittest.cc index cea1a4876..03b0954e6 100644 --- a/thirdparty/breakpad/common/dwarf_cu_to_module_unittest.cc +++ b/thirdparty/breakpad/common/dwarf_cu_to_module_unittest.cc @@ -31,11 +31,15 @@ // dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. +#include +#include #include #include "breakpad_googletest_includes.h" #include "common/dwarf_cu_to_module.h" +using std::make_pair; +using std::string; using std::vector; using dwarf2reader::AttributeList; @@ -144,8 +148,8 @@ class CUFixtureBase { // The handler will consult this section map to decide what to // pass to our line reader. - file_context_.section_map[".debug_line"] = std::make_pair(dummy_line_program_, - dummy_line_size_); + file_context_.section_map[".debug_line"] = make_pair(dummy_line_program_, + dummy_line_size_); } // Add a line with the given address, size, filename, and line @@ -158,7 +162,7 @@ class CUFixtureBase { // Use LANGUAGE for the compilation unit. More precisely, arrange // for StartCU to pass the compilation unit's root DIE a // DW_AT_language attribute whose value is LANGUAGE. - void SetLanguage(dwarf2reader::DwarfLanguage language) { + void SetLanguage(dwarf2reader::DwarfLanguage language) { language_ = language; } @@ -192,7 +196,7 @@ class CUFixtureBase { // not Finish. If NAME is non-zero, use it as the DW_AT_name // attribute. DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, - uint64 offset, const char *name = NULL); + uint64 specification, const char *name = NULL); // Define a function as a child of PARENT with the given name, // address, and size. Call EndAttributes and Finish; one cannot diff --git a/thirdparty/breakpad/common/dwarf_line_to_module.cc b/thirdparty/breakpad/common/dwarf_line_to_module.cc index 60922cb4c..d987370b3 100644 --- a/thirdparty/breakpad/common/dwarf_line_to_module.cc +++ b/thirdparty/breakpad/common/dwarf_line_to_module.cc @@ -41,13 +41,14 @@ // it until we actually have to deal with DWARF on Windows. // Return true if PATH is an absolute path, false if it is relative. -static bool PathIsAbsolute(const string &path) { +static bool PathIsAbsolute(const std::string &path) { return (path.size() >= 1 && path[0] == '/'); } // If PATH is an absolute path, return PATH. If PATH is a relative path, // treat it as relative to BASE and return the combined path. -static string ExpandPath(const string &path, const string &base) { +static std::string ExpandPath(const std::string &path, + const std::string &base) { if (PathIsAbsolute(path)) return path; return base + "/" + path; @@ -55,14 +56,14 @@ static string ExpandPath(const string &path, const string &base) { namespace google_breakpad { -void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) { +void DwarfLineToModule::DefineDir(const std::string &name, uint32 dir_num) { // Directory number zero is reserved to mean the compilation // directory. Silently ignore attempts to redefine it. if (dir_num != 0) directories_[dir_num] = name; } -void DwarfLineToModule::DefineFile(const string &name, int32 file_num, +void DwarfLineToModule::DefineFile(const std::string &name, int32 file_num, uint32 dir_num, uint64 mod_time, uint64 length) { if (file_num == -1) diff --git a/thirdparty/breakpad/common/dwarf_line_to_module_unittest.cc b/thirdparty/breakpad/common/dwarf_line_to_module_unittest.cc index 82bef685d..1e123e972 100644 --- a/thirdparty/breakpad/common/dwarf_line_to_module_unittest.cc +++ b/thirdparty/breakpad/common/dwarf_line_to_module_unittest.cc @@ -31,9 +31,13 @@ // dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. +#include + #include "breakpad_googletest_includes.h" #include "common/dwarf_line_to_module.h" +using std::vector; + using google_breakpad::DwarfLineToModule; using google_breakpad::Module; using google_breakpad::Module; @@ -44,7 +48,7 @@ TEST(SimpleModule, One) { DwarfLineToModule h(&m, &lines); h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); - h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, + h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, 0x4c090cbf, 0x1cf9fe0d); vector files; diff --git a/thirdparty/breakpad/common/language.cc b/thirdparty/breakpad/common/language.cc index 4e90d9160..c2fd81f64 100644 --- a/thirdparty/breakpad/common/language.cc +++ b/thirdparty/breakpad/common/language.cc @@ -49,7 +49,7 @@ class CPPLanguage: public Language { } }; -const CPPLanguage CPPLanguageSingleton; +CPPLanguage CPPLanguageSingleton; // Java language-specific operations. class JavaLanguage: public Language { diff --git a/thirdparty/breakpad/common/linux/dump_symbols.cc b/thirdparty/breakpad/common/linux/dump_symbols.cc index fc312983a..f02503ac1 100644 --- a/thirdparty/breakpad/common/linux/dump_symbols.cc +++ b/thirdparty/breakpad/common/linux/dump_symbols.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 Google Inc. +// Copyright (c) 2011 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -226,7 +226,7 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor { explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) : byte_reader_(byte_reader) { } void operator()(const char *program, uint64 length, - Module *module, vector *lines) { + Module *module, std::vector *lines) { DwarfLineToModule handler(module, lines); dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); parser.Start(); @@ -235,7 +235,7 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor { dwarf2reader::ByteReader *byte_reader_; }; -static bool LoadDwarf(const string &dwarf_filename, +static bool LoadDwarf(const std::string &dwarf_filename, const ElfW(Ehdr) *elf_header, const bool big_endian, Module *module) { @@ -253,8 +253,8 @@ static bool LoadDwarf(const string &dwarf_filename, const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; for (int i = 0; i < num_sections; i++) { const ElfW(Shdr) *section = §ions[i]; - string name = reinterpret_cast(section_names->sh_offset - + section->sh_name); + std::string name = reinterpret_cast(section_names->sh_offset + + section->sh_name); const char *contents = reinterpret_cast(section->sh_offset); uint64 length = section->sh_size; file_context.section_map[name] = std::make_pair(contents, length); @@ -292,7 +292,7 @@ static bool LoadDwarf(const string &dwarf_filename, // success, or false if we don't recognize HEADER's machine // architecture. static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, - vector *register_names) { + std::vector *register_names) { switch (elf_header->e_machine) { case EM_386: *register_names = DwarfCFIToModule::RegisterNames::I386(); @@ -308,7 +308,7 @@ static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, } } -static bool LoadDwarfCFI(const string &dwarf_filename, +static bool LoadDwarfCFI(const std::string &dwarf_filename, const ElfW(Ehdr) *elf_header, const char *section_name, const ElfW(Shdr) *section, @@ -319,7 +319,7 @@ static bool LoadDwarfCFI(const string &dwarf_filename, Module *module) { // Find the appropriate set of register names for this file's // architecture. - vector register_names; + std::vector register_names; if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) { fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" " cannot convert DWARF call frame information\n", @@ -728,6 +728,7 @@ namespace google_breakpad { bool WriteSymbolFileInternal(uint8_t* obj_file, const std::string &obj_filename, const std::string &debug_dir, + bool cfi, std::ostream &sym_stream) { ElfW(Ehdr) *elf_header = reinterpret_cast(obj_file); @@ -803,7 +804,7 @@ bool WriteSymbolFileInternal(uint8_t* obj_file, return false; } } - if (!module.Write(sym_stream)) + if (!module.Write(sym_stream, cfi)) return false; return true; @@ -811,6 +812,7 @@ bool WriteSymbolFileInternal(uint8_t* obj_file, bool WriteSymbolFile(const std::string &obj_file, const std::string &debug_dir, + bool cfi, std::ostream &sym_stream) { MmapWrapper map_wrapper; ElfW(Ehdr) *elf_header = NULL; @@ -818,7 +820,7 @@ bool WriteSymbolFile(const std::string &obj_file, return false; return WriteSymbolFileInternal(reinterpret_cast(elf_header), - obj_file, debug_dir, sym_stream); + obj_file, debug_dir, cfi, sym_stream); } } // namespace google_breakpad diff --git a/thirdparty/breakpad/common/linux/dump_symbols.h b/thirdparty/breakpad/common/linux/dump_symbols.h index 3749d1f6f..9bf54d377 100644 --- a/thirdparty/breakpad/common/linux/dump_symbols.h +++ b/thirdparty/breakpad/common/linux/dump_symbols.h @@ -1,6 +1,6 @@ // -*- mode: c++ -*- -// Copyright (c) 2010, Google Inc. +// Copyright (c) 2011, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -45,8 +45,10 @@ namespace google_breakpad { // file format. // If OBJ_FILE has been stripped but contains a .gnu_debuglink section, // then look for the debug file in DEBUG_DIR. +// If CFI is set to false, then omit the CFI section. bool WriteSymbolFile(const std::string &obj_file, const std::string &debug_dir, + bool cfi, std::ostream &sym_stream); } // namespace google_breakpad diff --git a/thirdparty/breakpad/common/linux/dump_symbols_unittest.cc b/thirdparty/breakpad/common/linux/dump_symbols_unittest.cc index 062630766..c6d4d2d37 100644 --- a/thirdparty/breakpad/common/linux/dump_symbols_unittest.cc +++ b/thirdparty/breakpad/common/linux/dump_symbols_unittest.cc @@ -47,6 +47,7 @@ namespace google_breakpad { bool WriteSymbolFileInternal(uint8_t* obj_file, const std::string &obj_filename, const std::string &debug_dir, + bool cfi, std::ostream &sym_stream); } @@ -62,7 +63,7 @@ using std::vector; using ::testing::Test; class DumpSymbols : public Test { -public: + public: void GetElfContents(ELF& elf) { string contents; ASSERT_TRUE(elf.GetContents(&contents)); @@ -84,6 +85,7 @@ TEST_F(DumpSymbols, Invalid) { EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast(&header), "foo", "", + true, s)); } @@ -105,11 +107,11 @@ TEST_F(DumpSymbols, SimplePublic32) { SHN_UNDEF + 1); int index = elf.AddSection(".dynstr", table, SHT_STRTAB); elf.AddSection(".dynsym", syms, - SHT_DYNSYM, // type - SHF_ALLOC, // flags - 0, // addr - index, // link - sizeof(Elf32_Sym)); // entsize + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(Elf32_Sym)); // entsize elf.Finish(); GetElfContents(elf); @@ -118,6 +120,7 @@ TEST_F(DumpSymbols, SimplePublic32) { ASSERT_TRUE(WriteSymbolFileInternal(elfdata, "foo", "", + true, s)); EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n" "PUBLIC 1000 0 superfunc\n", @@ -141,11 +144,11 @@ TEST_F(DumpSymbols, SimplePublic64) { SHN_UNDEF + 1); int index = elf.AddSection(".dynstr", table, SHT_STRTAB); elf.AddSection(".dynsym", syms, - SHT_DYNSYM, // type - SHF_ALLOC, // flags - 0, // addr - index, // link - sizeof(Elf64_Sym)); // entsize + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(Elf64_Sym)); // entsize elf.Finish(); GetElfContents(elf); @@ -154,6 +157,7 @@ TEST_F(DumpSymbols, SimplePublic64) { ASSERT_TRUE(WriteSymbolFileInternal(elfdata, "foo", "", + true, s)); EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n" "PUBLIC 1000 0 superfunc\n", diff --git a/thirdparty/breakpad/common/linux/elf_core_dump.cc b/thirdparty/breakpad/common/linux/elf_core_dump.cc new file mode 100644 index 000000000..0e7db7b1f --- /dev/null +++ b/thirdparty/breakpad/common/linux/elf_core_dump.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// elf_core_dump.cc: Implement google_breakpad::ElfCoreDump. +// See elf_core_dump.h for details. + +#include "common/linux/elf_core_dump.h" + +#include +#include + +namespace google_breakpad { + +// Implementation of ElfCoreDump::Note. + +ElfCoreDump::Note::Note() {} + +ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {} + +bool ElfCoreDump::Note::IsValid() const { + return GetHeader() != NULL; +} + +const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const { + return content_.GetData(0); +} + +ElfCoreDump::Word ElfCoreDump::Note::GetType() const { + const Nhdr* header = GetHeader(); + // 0 is not being used as a NOTE type. + return header ? header->n_type : 0; +} + +MemoryRange ElfCoreDump::Note::GetName() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(sizeof(Nhdr), header->n_namesz); + } + return MemoryRange(); +} + +MemoryRange ElfCoreDump::Note::GetDescription() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz), + header->n_descsz); + } + return MemoryRange(); +} + +ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const { + MemoryRange next_content; + const Nhdr* header = GetHeader(); + if (header) { + size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz); + next_offset = AlignedSize(next_offset + header->n_descsz); + next_content = + content_.Subrange(next_offset, content_.length() - next_offset); + } + return Note(next_content); +} + +// static +size_t ElfCoreDump::Note::AlignedSize(size_t size) { + size_t mask = sizeof(Word) - 1; + return (size + mask) & ~mask; +} + + +// Implementation of ElfCoreDump. + +ElfCoreDump::ElfCoreDump() {} + +ElfCoreDump::ElfCoreDump(const MemoryRange& content) + : content_(content) { +} + +void ElfCoreDump::SetContent(const MemoryRange& content) { + content_ = content; +} + +bool ElfCoreDump::IsValid() const { + const Ehdr* header = GetHeader(); + return (header && + header->e_ident[0] == ELFMAG0 && + header->e_ident[1] == ELFMAG1 && + header->e_ident[2] == ELFMAG2 && + header->e_ident[3] == ELFMAG3 && + header->e_ident[4] == kClass && + header->e_version == EV_CURRENT && + header->e_type == ET_CORE); +} + +const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const { + return content_.GetData(0); +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const { + const Ehdr* header = GetHeader(); + if (header) { + return reinterpret_cast(content_.GetArrayElement( + header->e_phoff, header->e_phentsize, index)); + } + return NULL; +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType( + Word type) const { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type == type) { + return program; + } + } + return NULL; +} + +unsigned ElfCoreDump::GetProgramHeaderCount() const { + const Ehdr* header = GetHeader(); + return header ? header->e_phnum : 0; +} + +bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type != PT_LOAD) + continue; + + size_t offset_in_segment = virtual_address - program->p_vaddr; + if (virtual_address >= program->p_vaddr && + offset_in_segment < program->p_filesz) { + const void* data = + content_.GetData(program->p_offset + offset_in_segment, length); + if (data) { + memcpy(buffer, data, length); + return true; + } + } + } + return false; +} + +ElfCoreDump::Note ElfCoreDump::GetFirstNote() const { + MemoryRange note_content; + const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE); + if (program_header) { + note_content = content_.Subrange(program_header->p_offset, + program_header->p_filesz); + } + return Note(note_content); +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/common/linux/elf_core_dump.h b/thirdparty/breakpad/common/linux/elf_core_dump.h new file mode 100644 index 000000000..dd846f09d --- /dev/null +++ b/thirdparty/breakpad/common/linux/elf_core_dump.h @@ -0,0 +1,153 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which +// encapsulates an ELF core dump file mapped into memory. + +#ifndef COMMON_LINUX_ELF_CORE_DUMP_H_ +#define COMMON_LINUX_ELF_CORE_DUMP_H_ + +#include +#if !defined(__ANDROID__) +#include +#endif +#include + +#include "common/memory_range.h" +#if defined(__ANDROID__) +#include "common/linux/android_link.h" +#endif + +namespace google_breakpad { + +// A class encapsulating an ELF core dump file mapped into memory, which +// provides methods for accessing program headers and the note section. +class ElfCoreDump { + public: + // ELF types based on the value of __WORDSIZE. + typedef ElfW(Ehdr) Ehdr; + typedef ElfW(Nhdr) Nhdr; + typedef ElfW(Phdr) Phdr; + typedef ElfW(Word) Word; + typedef ElfW(Addr) Addr; +#if __WORDSIZE == 32 + static const int kClass = ELFCLASS32; +#elif __WORDSIZE == 64 + static const int kClass = ELFCLASS64; +#else +#error "Unsupported __WORDSIZE for ElfCoreDump." +#endif + + // A class encapsulating the note content in a core dump, which provides + // methods for accessing the name and description of a note. + class Note { + public: + Note(); + + // Constructor that takes the note content from |content|. + explicit Note(const MemoryRange& content); + + // Returns true if this note is valid, i,e. a note header is found in + // |content_|, or false otherwise. + bool IsValid() const; + + // Returns the note header, or NULL if no note header is found in + // |content_|. + const Nhdr* GetHeader() const; + + // Returns the note type, or 0 if no note header is found in |content_|. + Word GetType() const; + + // Returns a memory range covering the note name, or an empty range + // if no valid note name is found in |content_|. + MemoryRange GetName() const; + + // Returns a memory range covering the note description, or an empty + // range if no valid note description is found in |content_|. + MemoryRange GetDescription() const; + + // Returns the note following this note, or an empty note if no valid + // note is found after this note. + Note GetNextNote() const; + + private: + // Returns the size in bytes round up to the word alignment, specified + // for the note section, of a given size in bytes. + static size_t AlignedSize(size_t size); + + // Note content. + MemoryRange content_; + }; + + ElfCoreDump(); + + // Constructor that takes the core dump content from |content|. + explicit ElfCoreDump(const MemoryRange& content); + + // Sets the core dump content to |content|. + void SetContent(const MemoryRange& content); + + // Returns true if a valid ELF header in the core dump, or false otherwise. + bool IsValid() const; + + // Returns the ELF header in the core dump, or NULL if no ELF header + // is found in |content_|. + const Ehdr* GetHeader() const; + + // Returns the |index|-th program header in the core dump, or NULL if no + // ELF header is found in |content_| or |index| is out of bounds. + const Phdr* GetProgramHeader(unsigned index) const; + + // Returns the first program header of |type| in the core dump, or NULL if + // no ELF header is found in |content_| or no program header of |type| is + // found. + const Phdr* GetFirstProgramHeaderOfType(Word type) const; + + // Returns the number of program headers in the core dump, or 0 if no + // ELF header is found in |content_|. + unsigned GetProgramHeaderCount() const; + + // Copies |length| bytes of data starting at |virtual_address| in the core + // dump to |buffer|. |buffer| should be a valid pointer to a buffer of at + // least |length| bytes. Returns true if the data to be copied is found in + // the core dump, or false otherwise. + bool CopyData(void* buffer, Addr virtual_address, size_t length); + + // Returns the first note found in the note section of the core dump, or + // an empty note if no note is found. + Note GetFirstNote() const; + + private: + // Core dump content. + MemoryRange content_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELF_CORE_DUMP_H_ diff --git a/thirdparty/breakpad/common/linux/elf_core_dump_unittest.cc b/thirdparty/breakpad/common/linux/elf_core_dump_unittest.cc new file mode 100644 index 000000000..11920f258 --- /dev/null +++ b/thirdparty/breakpad/common/linux/elf_core_dump_unittest.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump. + +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/file_utils.h" +#include "common/linux/tests/crash_generator.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::CrashGenerator; +using google_breakpad::ElfCoreDump; +using google_breakpad::MemoryMappedFile; +using google_breakpad::MemoryRange; +using google_breakpad::WriteFile; +using std::set; +using std::string; + +TEST(ElfCoreDumpTest, DefaultConstructor) { + ElfCoreDump core; + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); +} + +TEST(ElfCoreDumpTest, TestElfHeader) { + ElfCoreDump::Ehdr header; + memset(&header, 0, sizeof(header)); + + AutoTempDir temp_dir; + string core_path = temp_dir.path() + "/core"; + const char* core_file = core_path.c_str(); + MemoryMappedFile mapped_core_file; + ElfCoreDump core; + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1)); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[0] = ELFMAG0; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[1] = ELFMAG1; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[2] = ELFMAG2; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[3] = ELFMAG3; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[4] = ElfCoreDump::kClass; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_version = EV_CURRENT; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_type = ET_CORE; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); +} + +TEST(ElfCoreDumpTest, ValidCoreFile) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to non-default core pattern"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in + // CrashGenerator is identified and fixed. + if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, NULL)) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to no core dump generated"); + return; + } + pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); + set expected_thread_ids; + for (unsigned i = 0; i < kNumOfThreads; ++i) { + expected_thread_ids.insert(crash_generator.GetThreadId(i)); + } + + MemoryMappedFile mapped_core_file; + ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str())); + + ElfCoreDump core; + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); + + // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are + // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): + // Thread Name Type + // ------------------------------------------------------------------- + // 1st thread CORE NT_PRSTATUS + // process-wide CORE NT_PRPSINFO + // process-wide CORE NT_AUXV + // 1st thread CORE NT_FPREGSET + // 1st thread LINUX NT_PRXFPREG + // 1st thread LINUX NT_386_TLS + // + // 2nd thread CORE NT_PRSTATUS + // 2nd thread CORE NT_FPREGSET + // 2nd thread LINUX NT_PRXFPREG + // 2nd thread LINUX NT_386_TLS + // + // 3rd thread CORE NT_PRSTATUS + // 3rd thread CORE NT_FPREGSET + // 3rd thread LINUX NT_PRXFPREG + // 3rd thread LINUX NT_386_TLS + + size_t num_nt_prpsinfo = 0; + size_t num_nt_prstatus = 0; + size_t num_nt_fpregset = 0; + size_t num_nt_prxfpreg = 0; + set actual_thread_ids; + ElfCoreDump::Note note = core.GetFirstNote(); + while (note.IsValid()) { + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + EXPECT_FALSE(name.IsEmpty()); + EXPECT_FALSE(description.IsEmpty()); + + switch (note.GetType()) { + case NT_PRPSINFO: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prpsinfo), description.length()); + ++num_nt_prpsinfo; + break; + } + case NT_PRSTATUS: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prstatus), description.length()); + const elf_prstatus* status = description.GetData(0); + actual_thread_ids.insert(status->pr_pid); + if (num_nt_prstatus == 0) { + EXPECT_EQ(expected_crash_thread_id, status->pr_pid); + EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); + } + ++num_nt_prstatus; + break; + } +#if defined(__i386) || defined(__x86_64) + case NT_FPREGSET: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpregs_struct), description.length()); + ++num_nt_fpregset; + break; + } +#endif +#if defined(__i386) + case NT_PRXFPREG: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpxregs_struct), description.length()); + ++num_nt_prxfpreg; + break; + } +#endif + default: + break; + } + note = note.GetNextNote(); + } + + EXPECT_TRUE(expected_thread_ids == actual_thread_ids); + EXPECT_EQ(1, num_nt_prpsinfo); + EXPECT_EQ(kNumOfThreads, num_nt_prstatus); +#if defined(__i386) || defined(__x86_64) + EXPECT_EQ(kNumOfThreads, num_nt_fpregset); +#endif +#if defined(__i386) + EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg); +#endif +} diff --git a/thirdparty/breakpad/common/linux/file_id.cc b/thirdparty/breakpad/common/linux/file_id.cc index acd448a94..ff2f29e2c 100644 --- a/thirdparty/breakpad/common/linux/file_id.cc +++ b/thirdparty/breakpad/common/linux/file_id.cc @@ -36,22 +36,19 @@ #include #include -#include -#include #if defined(__ANDROID__) +#include #include "client/linux/android_link.h" #else +#include #include #endif -#include #include -#include -#include -#include #include #include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" #include "third_party/lss/linux_syscall_support.h" namespace google_breakpad { @@ -107,10 +104,10 @@ static void FindElfClassSection(const char *elf_base, const Shdr* section = NULL; for (int i = 0; i < elf_header->e_shnum; ++i) { if (sections[i].sh_type == section_type) { - const char* section_name = (char*)(elf_base + - string_section->sh_offset + - sections[i].sh_name); - if (!my_strncmp(section_name, section_name, name_len)) { + const char* current_section_name = (char*)(elf_base + + string_section->sh_offset + + sections[i].sh_name); + if (!my_strncmp(current_section_name, section_name, name_len)) { section = §ions[i]; break; } @@ -166,8 +163,7 @@ static bool FindElfSection(const void *elf_mapped_base, template static bool ElfClassBuildIDNoteIdentifier(const void *section, - uint8_t identifier[kMDGUIDSize]) -{ + uint8_t identifier[kMDGUIDSize]) { typedef typename ElfClass::Nhdr Nhdr; const Nhdr* note_header = reinterpret_cast(section); @@ -190,8 +186,7 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, // Attempt to locate a .note.gnu.build-id section in an ELF binary // and copy as many bytes of it as will fit into |identifier|. static bool FindElfBuildIDNote(const void *elf_mapped_base, - uint8_t identifier[kMDGUIDSize]) -{ + uint8_t identifier[kMDGUIDSize]) { void* note_section; int note_size, elfclass; if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, @@ -213,7 +208,6 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base, // a simple hash by XORing the first page worth of bytes into |identifier|. static bool HashElfTextSection(const void *elf_mapped_base, uint8_t identifier[kMDGUIDSize]) { - void* text_section; int text_size; if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, @@ -234,9 +228,8 @@ static bool HashElfTextSection(const void *elf_mapped_base, } // static -bool FileID::ElfFileIdentifierFromMappedFile(void* base, - uint8_t identifier[kMDGUIDSize]) -{ +bool FileID::ElfFileIdentifierFromMappedFile(const void* base, + uint8_t identifier[kMDGUIDSize]) { // Look for a build id note first. if (FindElfBuildIDNote(base, identifier)) return true; @@ -246,23 +239,11 @@ bool FileID::ElfFileIdentifierFromMappedFile(void* base, } bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { - int fd = open(path_, O_RDONLY); - if (fd < 0) - return false; - struct stat st; - if (fstat(fd, &st) != 0) { - close(fd); - return false; - } - void* base = mmap(NULL, st.st_size, - PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - close(fd); - if (base == MAP_FAILED) + MemoryMappedFile mapped_file(path_); + if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? return false; - bool success = ElfFileIdentifierFromMappedFile(base, identifier); - munmap(base, st.st_size); - return success; + return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); } // static diff --git a/thirdparty/breakpad/common/linux/file_id.h b/thirdparty/breakpad/common/linux/file_id.h index 4d5b52dc1..70a6b3f58 100644 --- a/thirdparty/breakpad/common/linux/file_id.h +++ b/thirdparty/breakpad/common/linux/file_id.h @@ -57,7 +57,7 @@ class FileID { // Load the identifier for the elf file mapped into memory at |base| into // |identifier|. Return false if the identifier could not be created for the // file. - static bool ElfFileIdentifierFromMappedFile(void* base, + static bool ElfFileIdentifierFromMappedFile(const void* base, uint8_t identifier[kMDGUIDSize]); // Convert the |identifier| data to a NULL terminated string. The string will diff --git a/thirdparty/breakpad/common/linux/file_id_unittest.cc b/thirdparty/breakpad/common/linux/file_id_unittest.cc index 242089833..94bc80e4f 100644 --- a/thirdparty/breakpad/common/linux/file_id_unittest.cc +++ b/thirdparty/breakpad/common/linux/file_id_unittest.cc @@ -33,39 +33,52 @@ #include #include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" #include "common/linux/synth_elf.h" #include "common/test_assembler.h" +#include "common/tests/auto_tempdir.h" #include "breakpad_googletest_includes.h" using namespace google_breakpad; +using google_breakpad::SafeReadLink; using google_breakpad::synth_elf::BuildIDNote; using google_breakpad::synth_elf::ELF; using google_breakpad::test_assembler::kLittleEndian; using google_breakpad::test_assembler::Section; +namespace { + +// Simply calling Section::Append(size, byte) produces a uninteresting pattern +// that tends to get hashed to 0000...0000. This populates the section with +// data to produce better hashes. +void PopulateSection(Section* section, int size, int prime_number) { + for (int i = 0; i < size; i++) + section->Append(1, (i % prime_number) % 256); +} + +} // namespace + TEST(FileIDStripTest, StripSelf) { // Calculate the File ID of this binary using // FileID::ElfFileIdentifier, then make a copy of this binary, // strip it, and ensure that the result is the same. 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'; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); // copy our binary to a temp file, and strip it - char templ[] = "/tmp/file-id-unittest-XXXXXX"; - mktemp(templ); + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/file-id-unittest"; char cmdline[4096]; - sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ); + sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); ASSERT_EQ(system(cmdline), 0); - sprintf(cmdline, "strip \"%s\"", templ); + sprintf(cmdline, "strip \"%s\"", templ.c_str()); ASSERT_EQ(system(cmdline), 0); uint8_t identifier1[sizeof(MDGUID)]; uint8_t identifier2[sizeof(MDGUID)]; FileID fileid1(exe_name); EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); - FileID fileid2(templ); + FileID fileid2(templ.c_str()); EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); char identifier_string1[37]; char identifier_string2[37]; @@ -74,7 +87,6 @@ TEST(FileIDStripTest, StripSelf) { FileID::ConvertIdentifierToString(identifier2, identifier_string2, 37); EXPECT_STREQ(identifier_string1, identifier_string2); - unlink(templ); } class FileIDTest : public testing::Test { @@ -181,3 +193,92 @@ TEST_F(FileIDTest, BuildID) { sizeof(identifier_string)); EXPECT_STREQ(expected_identifier_string, identifier_string); } + +// Test to make sure two files with different text sections produce +// different hashes when not using a build id. +TEST_F(FileIDTest, UniqueHashes32) { + char identifier_string_1[] = + "00000000-0000-0000-0000-000000000000"; + char identifier_string_2[] = + "00000000-0000-0000-0000-000000000000"; + uint8_t identifier_1[sizeof(MDGUID)]; + uint8_t identifier_2[sizeof(MDGUID)]; + + { + ELF elf1(EM_386, ELFCLASS32, kLittleEndian); + Section foo_1(kLittleEndian); + PopulateSection(&foo_1, 32, 5); + elf1.AddSection(".foo", foo_1, SHT_PROGBITS); + Section text_1(kLittleEndian); + PopulateSection(&text_1, 4096, 17); + elf1.AddSection(".text", text_1, SHT_PROGBITS); + elf1.Finish(); + GetElfContents(elf1); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1)); + FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, + sizeof(identifier_string_1)); + + { + ELF elf2(EM_386, ELFCLASS32, kLittleEndian); + Section text_2(kLittleEndian); + Section foo_2(kLittleEndian); + PopulateSection(&foo_2, 32, 5); + elf2.AddSection(".foo", foo_2, SHT_PROGBITS); + PopulateSection(&text_2, 4096, 31); + elf2.AddSection(".text", text_2, SHT_PROGBITS); + elf2.Finish(); + GetElfContents(elf2); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2)); + FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, + sizeof(identifier_string_2)); + + EXPECT_STRNE(identifier_string_1, identifier_string_2); +} + +// Same as UniqueHashes32, for x86-64. +TEST_F(FileIDTest, UniqueHashes64) { + char identifier_string_1[] = + "00000000-0000-0000-0000-000000000000"; + char identifier_string_2[] = + "00000000-0000-0000-0000-000000000000"; + uint8_t identifier_1[sizeof(MDGUID)]; + uint8_t identifier_2[sizeof(MDGUID)]; + + { + ELF elf1(EM_X86_64, ELFCLASS64, kLittleEndian); + Section foo_1(kLittleEndian); + PopulateSection(&foo_1, 32, 5); + elf1.AddSection(".foo", foo_1, SHT_PROGBITS); + Section text_1(kLittleEndian); + PopulateSection(&text_1, 4096, 17); + elf1.AddSection(".text", text_1, SHT_PROGBITS); + elf1.Finish(); + GetElfContents(elf1); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1)); + FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, + sizeof(identifier_string_1)); + + { + ELF elf2(EM_X86_64, ELFCLASS64, kLittleEndian); + Section text_2(kLittleEndian); + Section foo_2(kLittleEndian); + PopulateSection(&foo_2, 32, 5); + elf2.AddSection(".foo", foo_2, SHT_PROGBITS); + PopulateSection(&text_2, 4096, 31); + elf2.AddSection(".text", text_2, SHT_PROGBITS); + elf2.Finish(); + GetElfContents(elf2); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2)); + FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, + sizeof(identifier_string_2)); + + EXPECT_STRNE(identifier_string_1, identifier_string_2); +} diff --git a/thirdparty/breakpad/common/linux/guid_creator.cc b/thirdparty/breakpad/common/linux/guid_creator.cc index 678f59045..426f93a1c 100644 --- a/thirdparty/breakpad/common/linux/guid_creator.cc +++ b/thirdparty/breakpad/common/linux/guid_creator.cc @@ -30,6 +30,7 @@ #include "common/linux/guid_creator.h" #include +#include #include #include #include @@ -45,10 +46,6 @@ // class GUIDGenerator { public: - GUIDGenerator() { - srandom(time(NULL)); - } - static u_int32_t BytesToUInt32(const u_int8_t bytes[]) { return ((u_int32_t) bytes[0] | ((u_int32_t) bytes[1] << 8) @@ -63,7 +60,8 @@ class GUIDGenerator { bytes[3] = (n >> 24) & 0xff; } - bool CreateGUID(GUID *guid) const { + static bool CreateGUID(GUID *guid) { + InitOnce(); guid->data1 = random(); guid->data2 = (u_int16_t)(random()); guid->data3 = (u_int16_t)(random()); @@ -71,13 +69,23 @@ class GUIDGenerator { UInt32ToBytes(&guid->data4[4], random()); return true; } + + private: + static void InitOnce() { + pthread_once(&once_control, &InitOnceImpl); + } + + static void InitOnceImpl() { + srandom(time(NULL)); + } + + static pthread_once_t once_control; }; -// Guid generator. -const GUIDGenerator kGuidGenerator; +pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; bool CreateGUID(GUID *guid) { - return kGuidGenerator.CreateGUID(guid); + return GUIDGenerator::CreateGUID(guid); } // Parse guid to string. diff --git a/thirdparty/breakpad/common/linux/http_upload.cc b/thirdparty/breakpad/common/linux/http_upload.cc index 857dc1416..f69237c83 100644 --- a/thirdparty/breakpad/common/linux/http_upload.cc +++ b/thirdparty/breakpad/common/linux/http_upload.cc @@ -62,7 +62,11 @@ bool HTTPUpload::SendRequest(const string &url, const string &proxy_user_pwd, const string &ca_certificate_file, string *response_body, + long *response_code, string *error_description) { + if (response_code != NULL) + *response_code = 0; + if (!CheckParameters(parameters)) return false; @@ -149,6 +153,11 @@ bool HTTPUpload::SendRequest(const string &url, CURLcode (*curl_easy_perform)(CURL *); *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform"); err_code = (*curl_easy_perform)(curl); + if (response_code != NULL) { + CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...); + *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo"); + (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); + } const char* (*curl_easy_strerror)(CURLcode); *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror"); #ifndef NDEBUG diff --git a/thirdparty/breakpad/common/linux/http_upload.h b/thirdparty/breakpad/common/linux/http_upload.h index e98b25ded..22b62960d 100644 --- a/thirdparty/breakpad/common/linux/http_upload.h +++ b/thirdparty/breakpad/common/linux/http_upload.h @@ -53,6 +53,8 @@ class HTTPUpload { // Only HTTP(S) URLs are currently supported. Returns true on success. // If the request is successful and response_body is non-NULL, // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). // If the send fails, a description of the error will be // returned in error_description. static bool SendRequest(const string &url, @@ -63,6 +65,7 @@ class HTTPUpload { const string &proxy_user_pwd, const string &ca_certificate_file, string *response_body, + long *response_code, string *error_description); private: diff --git a/thirdparty/breakpad/common/linux/linux_libc_support.h b/thirdparty/breakpad/common/linux/linux_libc_support.h index e08f27f71..2ef2dc11f 100644 --- a/thirdparty/breakpad/common/linux/linux_libc_support.h +++ b/thirdparty/breakpad/common/linux/linux_libc_support.h @@ -105,7 +105,7 @@ my_strtoui(int* result, const char* s) { // Return the length of the given, non-negative integer when expressed in base // 10. static inline unsigned -my_int_len(int i) { +my_int_len(intmax_t i) { if (!i) return 1; @@ -125,7 +125,7 @@ my_int_len(int i) { // i: the non-negative integer to serialise. // i_len: the length of the integer in base 10 (see |my_int_len|). static inline void -my_itos(char* output, int i, unsigned i_len) { +my_itos(char* output, intmax_t i, unsigned i_len) { for (unsigned index = i_len; index; --index, i /= 10) output[index - 1] = '0' + (i % 10); } diff --git a/thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc b/thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc index d3907e947..a7c5a26a9 100644 --- a/thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc +++ b/thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc @@ -59,8 +59,12 @@ TEST(LinuxLibcSupportTest, strcmp) { for (unsigned i = 0; ; ++i) { if (!test_data[i*2]) break; - ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), - strcmp(test_data[i*2], test_data[i*2 + 1])); + int libc_result = strcmp(test_data[i*2], test_data[i*2 + 1]); + if (libc_result > 1) + libc_result = 1; + else if (libc_result < -1) + libc_result = -1; + ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), libc_result); } } diff --git a/thirdparty/breakpad/common/linux/memory_mapped_file.cc b/thirdparty/breakpad/common/linux/memory_mapped_file.cc new file mode 100644 index 000000000..32fc3b18c --- /dev/null +++ b/thirdparty/breakpad/common/linux/memory_mapped_file.cc @@ -0,0 +1,108 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile. +// See memory_mapped_file.h for details. + +#include "common/linux/memory_mapped_file.h" + +#include +#include +#if defined(__ANDROID__) +#include +#endif +#include + +#include "common/memory_range.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +MemoryMappedFile::MemoryMappedFile() {} + +MemoryMappedFile::MemoryMappedFile(const char* path) { + Map(path); +} + +MemoryMappedFile::~MemoryMappedFile() { + Unmap(); +} + +bool MemoryMappedFile::Map(const char* path) { + Unmap(); + + int fd = sys_open(path, O_RDONLY, 0); + if (fd == -1) { + return false; + } + +#if defined(__ANDROID__) + struct stat st; + if (fstat(fd, &st) != 0) { +#elif defined(__x86_64__) + struct kernel_stat st; + if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { +#else + struct kernel_stat64 st; + if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) { +#endif + sys_close(fd); + return false; + } + + // If the file size is zero, simply use an empty MemoryRange and return + // true. Don't bother to call mmap() even though mmap() can handle an + // empty file on some platforms. + if (st.st_size == 0) { + sys_close(fd); + return true; + } + +#if defined(__x86_64__) + void* data = sys_mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +#else + void* data = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +#endif + sys_close(fd); + if (data == MAP_FAILED) { + return false; + } + + content_.Set(data, st.st_size); + return true; +} + +void MemoryMappedFile::Unmap() { + if (content_.data()) { + sys_munmap(const_cast(content_.data()), content_.length()); + content_.Set(NULL, 0); + } +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/common/linux/memory_mapped_file.h b/thirdparty/breakpad/common/linux/memory_mapped_file.h new file mode 100644 index 000000000..6abd5b0c7 --- /dev/null +++ b/thirdparty/breakpad/common/linux/memory_mapped_file.h @@ -0,0 +1,86 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile +// class, which maps a file into memory for read-only access. + +#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_ +#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_ + +#include "common/basictypes.h" +#include "common/memory_range.h" + +namespace google_breakpad { + +// A utility class for mapping a file into memory for read-only access of +// the file content. Its implementation avoids calling into libc functions +// by directly making system calls for open, close, mmap, and munmap. +class MemoryMappedFile { + public: + MemoryMappedFile(); + + // Constructor that calls Map() to map a file at |path| into memory. + // If Map() fails, the object behaves as if it is default constructed. + explicit MemoryMappedFile(const char* path); + + ~MemoryMappedFile(); + + // Maps a file at |path| into memory, which can then be accessed via + // content() as a MemoryRange object or via data(), and returns true on + // success. Mapping an empty file will succeed but with data() and size() + // returning NULL and 0, respectively. An existing mapping is unmapped + // before a new mapping is created. + bool Map(const char* path); + + // Unmaps the memory for the mapped file. It's a no-op if no file is + // mapped. + void Unmap(); + + // Returns a MemoryRange object that covers the memory for the mapped + // file. The MemoryRange object is empty if no file is mapped. + const MemoryRange& content() const { return content_; } + + // Returns a pointer to the beginning of the memory for the mapped file. + // or NULL if no file is mapped or the mapped file is empty. + const void* data() const { return content_.data(); } + + // Returns the size in bytes of the mapped file, or zero if no file + // is mapped. + size_t size() const { return content_.length(); } + + private: + // Mapped file content as a MemoryRange object. + MemoryRange content_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_ diff --git a/thirdparty/breakpad/common/linux/memory_mapped_file_unittest.cc b/thirdparty/breakpad/common/linux/memory_mapped_file_unittest.cc new file mode 100644 index 000000000..cf89bca99 --- /dev/null +++ b/thirdparty/breakpad/common/linux/memory_mapped_file_unittest.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// memory_mapped_file_unittest.cc: +// Unit tests for google_breakpad::MemoryMappedFile. + +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::MemoryMappedFile; +using google_breakpad::WriteFile; +using std::string; + +namespace { + +class MemoryMappedFileTest : public testing::Test { + protected: + void ExpectNoMappedData(const MemoryMappedFile& mapped_file) { + EXPECT_TRUE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() == NULL); + EXPECT_EQ(0, mapped_file.size()); + } +}; + +} // namespace + +TEST_F(MemoryMappedFileTest, DefaultConstructor) { + MemoryMappedFile mapped_file; + ExpectNoMappedData(mapped_file); +} + +TEST_F(MemoryMappedFileTest, UnmapWithoutMap) { + MemoryMappedFile mapped_file; + mapped_file.Unmap(); +} + +TEST_F(MemoryMappedFileTest, MapNonexistentFile) { + { + MemoryMappedFile mapped_file("nonexistent-file"); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_FALSE(mapped_file.Map("nonexistent-file")); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapEmptyFile) { + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/empty_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), NULL, 0)); + + { + MemoryMappedFile mapped_file(test_file.c_str()); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str())); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapNonEmptyFile) { + char data[256]; + size_t data_size = sizeof(data); + for (size_t i = 0; i < data_size; ++i) { + data[i] = i; + } + + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/test_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), data, data_size)); + + { + MemoryMappedFile mapped_file(test_file.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str())); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } +} + +TEST_F(MemoryMappedFileTest, RemapAfterMap) { + char data1[256]; + size_t data1_size = sizeof(data1); + for (size_t i = 0; i < data1_size; ++i) { + data1[i] = i; + } + + char data2[50]; + size_t data2_size = sizeof(data2); + for (size_t i = 0; i < data2_size; ++i) { + data2[i] = 255 - i; + } + + AutoTempDir temp_dir; + string test_file1 = temp_dir.path() + "/test_file1"; + string test_file2 = temp_dir.path() + "/test_file2"; + ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size)); + ASSERT_TRUE(WriteFile(test_file2.c_str(), data2, data2_size)); + + { + MemoryMappedFile mapped_file(test_file1.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file1.c_str())); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } +} diff --git a/thirdparty/breakpad/client/mac/tests/auto_tempdir.h b/thirdparty/breakpad/common/linux/safe_readlink.cc similarity index 62% rename from thirdparty/breakpad/client/mac/tests/auto_tempdir.h rename to thirdparty/breakpad/common/linux/safe_readlink.cc index b6473a5cd..870c28af3 100644 --- a/thirdparty/breakpad/client/mac/tests/auto_tempdir.h +++ b/thirdparty/breakpad/common/linux/safe_readlink.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010, Google Inc. +// Copyright (c) 2011, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -27,46 +27,27 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Utility class for creating a temporary directory for unit tests -// that is deleted in the destructor. -#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR -#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR +// safe_readlink.cc: Implement google_breakpad::SafeReadLink. +// See safe_readlink.h for details. -#include -#include +#include -#include +#include "third_party/lss/linux_syscall_support.h" namespace google_breakpad { -class AutoTempDir { - public: - AutoTempDir() { - char tempDir[16] = "/tmp/XXXXXXXXXX"; - mkdtemp(tempDir); - path = tempDir; +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) { + // sys_readlink() does not add a NULL byte to |buffer|. In order to return + // a NULL-terminated string in |buffer|, |buffer_size| should be at least + // one byte longer than the expected path length. Also, sys_readlink() + // returns the actual path length on success, which does not count the + // NULL byte, so |result_size| should be less than |buffer_size|. + ssize_t result_size = sys_readlink(path, buffer, buffer_size); + if (result_size >= 0 && static_cast(result_size) < buffer_size) { + buffer[result_size] = '\0'; + return true; } - - ~AutoTempDir() { - // First remove any files in the dir - DIR* dir = opendir(path.c_str()); - if (!dir) - return; - - dirent* entry; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - std::string entryPath = path + "/" + entry->d_name; - unlink(entryPath.c_str()); - } - closedir(dir); - rmdir(path.c_str()); - } - - std::string path; -}; + return false; +} } // namespace google_breakpad - -#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_AUTO_TEMPDIR diff --git a/thirdparty/breakpad/common/linux/safe_readlink.h b/thirdparty/breakpad/common/linux/safe_readlink.h new file mode 100644 index 000000000..4ae131b58 --- /dev/null +++ b/thirdparty/breakpad/common/linux/safe_readlink.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// safe_readlink.h: Define the google_breakpad::SafeReadLink function, +// which wraps sys_readlink and gurantees the result is NULL-terminated. + +#ifndef COMMON_LINUX_SAFE_READLINK_H_ +#define COMMON_LINUX_SAFE_READLINK_H_ + +#include + +namespace google_breakpad { + +// This function wraps sys_readlink() and performs the same functionalty, +// but guarantees |buffer| is NULL-terminated if sys_readlink() returns +// no error. It takes the same arguments as sys_readlink(), but unlike +// sys_readlink(), it returns true on success. +// +// |buffer_size| specifies the size of |buffer| in bytes. As this function +// always NULL-terminates |buffer| on success, |buffer_size| should be +// at least one byte longer than the expected path length (e.g. PATH_MAX, +// which is typically defined as the maximum length of a path name +// including the NULL byte). +// +// The implementation of this function calls sys_readlink() instead of +// readlink(), it can thus be used in the context where calling to libc +// functions is discouraged. +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size); + +// Same as the three-argument version of SafeReadLink() but deduces the +// size of |buffer| if it is a char array of known size. +template +bool SafeReadLink(const char* path, char (&buffer)[N]) { + return SafeReadLink(path, buffer, sizeof(buffer)); +} + +} // namespace google_breakpad + +#endif // COMMON_LINUX_SAFE_READLINK_H_ diff --git a/thirdparty/breakpad/common/linux/safe_readlink_unittest.cc b/thirdparty/breakpad/common/linux/safe_readlink_unittest.cc new file mode 100644 index 000000000..191fb9f18 --- /dev/null +++ b/thirdparty/breakpad/common/linux/safe_readlink_unittest.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// safe_readlink_unittest.cc: Unit tests for google_breakpad::SafeReadLink. + +#include "breakpad_googletest_includes.h" +#include "common/linux/safe_readlink.h" + +using google_breakpad::SafeReadLink; + +TEST(SafeReadLinkTest, ZeroBufferSize) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 0)); +} + +TEST(SafeReadLinkTest, BufferSizeTooSmall) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 1)); +} + +TEST(SafeReadLinkTest, BoundaryBufferSize) { + char buffer[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer))); + size_t path_length = strlen(buffer); + EXPECT_LT(0, path_length); + EXPECT_GT(sizeof(buffer), path_length); + + // Buffer size equals to the expected path length plus 1 for the NULL byte. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2, path_length + 1)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); + + // Buffer size equals to the expected path length. + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, path_length)); +} + +TEST(SafeReadLinkTest, NonexistentPath) { + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink("nonexistent_path", buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, NonSymbolicLinkPath) { + char actual_path[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", actual_path, sizeof(actual_path))); + + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink(actual_path, buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, DeduceBufferSizeFromCharArray) { + char buffer[PATH_MAX]; + char* buffer_pointer = buffer; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer_pointer, sizeof(buffer))); + size_t path_length = strlen(buffer); + + // Use the template version of SafeReadLink to deduce the buffer size + // from the char array. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); +} diff --git a/thirdparty/breakpad/common/linux/tests/crash_generator.cc b/thirdparty/breakpad/common/linux/tests/crash_generator.cc new file mode 100644 index 000000000..59aced272 --- /dev/null +++ b/thirdparty/breakpad/common/linux/tests/crash_generator.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// crash_generator.cc: Implement google_breakpad::CrashGenerator. +// See crash_generator.h for details. + +#include "common/linux/tests/crash_generator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/linux/eintr_wrapper.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" + +namespace { + +struct ThreadData { + pthread_t thread; + pthread_barrier_t* barrier; + pid_t* thread_id_ptr; +}; + +const char* const kProcFilesToCopy[] = { + "auxv", "cmdline", "environ", "maps", "status" +}; +const size_t kNumProcFilesToCopy = + sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]); + +// Core file size limit set to 1 MB, which is big enough for test purposes. +const rlim_t kCoreSizeLimit = 1024 * 1024; + +void *thread_function(void *data) { + ThreadData* thread_data = reinterpret_cast(data); + volatile pid_t thread_id = syscall(__NR_gettid); + *(thread_data->thread_id_ptr) = thread_id; + int result = pthread_barrier_wait(thread_data->barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + exit(1); + } + while (true) { + pthread_yield(); + } +} + +} // namespace + +namespace google_breakpad { + +CrashGenerator::CrashGenerator() + : shared_memory_(NULL), + shared_memory_size_(0) { +} + +CrashGenerator::~CrashGenerator() { + UnmapSharedMemory(); +} + +bool CrashGenerator::HasDefaultCorePattern() const { + char buffer[8]; + ssize_t buffer_size = sizeof(buffer); + return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) && + buffer_size == 5 && memcmp(buffer, "core", 4) == 0; +} + +std::string CrashGenerator::GetCoreFilePath() const { + return temp_dir_.path() + "/core"; +} + +std::string CrashGenerator::GetDirectoryOfProcFilesCopy() const { + return temp_dir_.path() + "/proc"; +} + +pid_t CrashGenerator::GetThreadId(unsigned index) const { + return reinterpret_cast(shared_memory_)[index]; +} + +pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) { + return reinterpret_cast(shared_memory_) + index; +} + +bool CrashGenerator::MapSharedMemory(size_t memory_size) { + if (!UnmapSharedMemory()) + return false; + + void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mapped_memory == MAP_FAILED) { + perror("CrashGenerator: Failed to map shared memory"); + return false; + } + + memset(mapped_memory, 0, memory_size); + shared_memory_ = mapped_memory; + shared_memory_size_ = memory_size; + return true; +} + +bool CrashGenerator::UnmapSharedMemory() { + if (!shared_memory_) + return true; + + if (munmap(shared_memory_, shared_memory_size_) == 0) { + shared_memory_ = NULL; + shared_memory_size_ = 0; + return true; + } + + perror("CrashGenerator: Failed to unmap shared memory"); + return false; +} + +bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const { + struct rlimit limits = { limit, limit }; + if (setrlimit(RLIMIT_CORE, &limits) == -1) { + perror("CrashGenerator: Failed to set core file size limit"); + return false; + } + return true; +} + +bool CrashGenerator::CreateChildCrash( + unsigned num_threads, unsigned crash_thread, int crash_signal, + pid_t* child_pid) { + if (num_threads == 0 || crash_thread >= num_threads) + return false; + + if (!MapSharedMemory(num_threads * sizeof(pid_t))) + return false; + + pid_t pid = fork(); + if (pid == 0) { + if (chdir(temp_dir_.path().c_str()) == -1) { + perror("CrashGenerator: Failed to change directory"); + exit(1); + } + if (SetCoreFileSizeLimit(kCoreSizeLimit)) { + CreateThreadsInChildProcess(num_threads); + std::string proc_dir = GetDirectoryOfProcFilesCopy(); + if (mkdir(proc_dir.c_str(), 0755) == -1) { + perror("CrashGenerator: Failed to create proc directory"); + exit(1); + } + if (!CopyProcFiles(getpid(), proc_dir.c_str())) { + fprintf(stderr, "CrashGenerator: Failed to copy proc files\n"); + exit(1); + } + if (kill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) { + perror("CrashGenerator: Failed to kill thread by signal"); + } + } + exit(1); + } else if (pid == -1) { + perror("CrashGenerator: Failed to create child process"); + return false; + } + + int status; + if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) { + perror("CrashGenerator: Failed to wait for child process"); + return false; + } + if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) { + fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n" + " exit status=0x%x signaled=%s sig=%d expected=%d\n", + status, WIFSIGNALED(status) ? "true" : "false", + WTERMSIG(status), crash_signal); + return false; + } + + if (child_pid) + *child_pid = pid; + return true; +} + +bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const { + char from_path[PATH_MAX], to_path[PATH_MAX]; + for (size_t i = 0; i < kNumProcFilesToCopy; ++i) { + int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s", + pid, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + num_chars = snprintf(to_path, PATH_MAX, "%s/%s", + path, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + if (!CopyFile(from_path, to_path)) + return false; + } + return true; +} + +void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) { + *GetThreadIdPointer(0) = getpid(); + + if (num_threads <= 1) + return; + + // This method does not clean up any pthread resource, as the process + // is expected to be killed anyway. + ThreadData* thread_data = new ThreadData[num_threads]; + + // Create detached threads so that we do not worry about pthread_join() + // later being called or not. + pthread_attr_t thread_attributes; + if (pthread_attr_init(&thread_attributes) != 0 || + pthread_attr_setdetachstate(&thread_attributes, + PTHREAD_CREATE_DETACHED) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n"); + exit(1); + } + + pthread_barrier_t thread_barrier; + if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n"); + exit(1); + } + + for (unsigned i = 1; i < num_threads; ++i) { + thread_data[i].barrier = &thread_barrier; + thread_data[i].thread_id_ptr = GetThreadIdPointer(i); + if (pthread_create(&thread_data[i].thread, &thread_attributes, + thread_function, &thread_data[i]) != 0) { + fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i); + exit(1); + } + } + + int result = pthread_barrier_wait(&thread_barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n"); + exit(1); + } + + pthread_barrier_destroy(&thread_barrier); + pthread_attr_destroy(&thread_attributes); + delete[] thread_data; +} + +} // namespace google_breakpad diff --git a/thirdparty/breakpad/common/linux/tests/crash_generator.h b/thirdparty/breakpad/common/linux/tests/crash_generator.h new file mode 100644 index 000000000..d05ce73d3 --- /dev/null +++ b/thirdparty/breakpad/common/linux/tests/crash_generator.h @@ -0,0 +1,116 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// crash_generator.h: Define the google_breakpad::CrashGenerator class, +// which is used to generate a crash (and a core dump file) for testing. + +#ifndef COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ +#define COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ + +#include + +#include + +#include "common/tests/auto_tempdir.h" + +namespace google_breakpad { + +// A utility class for generating a crash (and a core dump file) for +// testing. It creates a child process with the specified number of +// threads, which is then termainated by the specified signal. A core +// dump file is expected to be created upon the termination of the child +// process, which can then be used for testing code that processes core +// dump files. +class CrashGenerator { + public: + CrashGenerator(); + + ~CrashGenerator(); + + // Returns true if a core dump file named 'core' will be generated in + // the current directory for a test that produces a crash by checking + // if /proc/sys/kernel/core_pattern has the default value 'core'. + bool HasDefaultCorePattern() const; + + // Returns the expected path of the core dump file. + std::string GetCoreFilePath() const; + + // Returns the directory of a copy of proc files of the child process. + std::string GetDirectoryOfProcFilesCopy() const; + + // Creates a crash (and a core dump file) by creating a child process with + // |num_threads| threads, and the terminating the child process by sending + // a signal with number |crash_signal| to the |crash_thread|-th thread. + // Returns true on success. + bool CreateChildCrash(unsigned num_threads, unsigned crash_thread, + int crash_signal, pid_t* child_pid); + + // Returns the thread ID of the |index|-th thread in the child process. + // This method does not validate |index|. + pid_t GetThreadId(unsigned index) const; + + private: + // Copies the following proc files of the process with |pid| to the directory + // at |path|: auxv, cmdline, environ, maps, status + // The directory must have been created. Returns true on success. + bool CopyProcFiles(pid_t pid, const char* path) const; + + // Creates |num_threads| threads in the child process. + void CreateThreadsInChildProcess(unsigned num_threads); + + // Sets the maximum size of core dump file (both the soft and hard limit) + // to |limit| bytes. Returns true on success. + bool SetCoreFileSizeLimit(rlim_t limit) const; + + // Creates a shared memory of |memory_size| bytes for communicating thread + // IDs between the parent and child process. Returns true on success. + bool MapSharedMemory(size_t memory_size); + + // Releases any shared memory created by MapSharedMemory(). Returns true on + // success. + bool UnmapSharedMemory(); + + // Returns the pointer to the thread ID of the |index|-th thread in the child + // process. This method does not validate |index|. + pid_t* GetThreadIdPointer(unsigned index); + + // Temporary directory in which a core file is generated. + AutoTempDir temp_dir_; + + // Shared memory for communicating thread IDs between the parent and + // child process. + void* shared_memory_; + + // Number of bytes mapped for |shared_memory_|. + size_t shared_memory_size_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ diff --git a/thirdparty/breakpad/common/mac/Breakpad.xcconfig b/thirdparty/breakpad/common/mac/Breakpad.xcconfig index b6bf92b59..672be2cee 100644 --- a/thirdparty/breakpad/common/mac/Breakpad.xcconfig +++ b/thirdparty/breakpad/common/mac/Breakpad.xcconfig @@ -29,8 +29,6 @@ ARCHS = $(ARCHS_STANDARD_32_64_BIT) SDKROOT = macosx10.5 -SDKROOT[arch=i386] = macosx10.4 -SDKROOT[arch=ppc] = macosx10.4 GCC_VERSION = 4.2 GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0 diff --git a/thirdparty/breakpad/common/mac/HTTPMultipartUpload.h b/thirdparty/breakpad/common/mac/HTTPMultipartUpload.h index 95f259b92..42e8fed39 100644 --- a/thirdparty/breakpad/common/mac/HTTPMultipartUpload.h +++ b/thirdparty/breakpad/common/mac/HTTPMultipartUpload.h @@ -32,7 +32,7 @@ // Each file is sent with a name field in addition to the filename and data // The data will be sent synchronously. -#import +#import @interface HTTPMultipartUpload : NSObject { @protected diff --git a/thirdparty/breakpad/common/mac/HTTPMultipartUpload.m b/thirdparty/breakpad/common/mac/HTTPMultipartUpload.m index eee66a9c9..76f38f8a2 100644 --- a/thirdparty/breakpad/common/mac/HTTPMultipartUpload.m +++ b/thirdparty/breakpad/common/mac/HTTPMultipartUpload.m @@ -32,6 +32,8 @@ @interface HTTPMultipartUpload(PrivateMethods) - (NSString *)multipartBoundary; +// Each of the following methods will append the starting multipart boundary, +// but not the ending one. - (NSData *)formDataForKey:(NSString *)key value:(NSString *)value; - (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name; - (NSData *)formDataForFile:(NSString *)file name:(NSString *)name; @@ -67,11 +69,9 @@ NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; " "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n"; NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped]; - NSString *post = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:contents]; - [data appendData:[post dataUsingEncoding:NSUTF8StringEncoding]]; return data; } @@ -182,6 +182,9 @@ [postBody appendData:fileData]; } + NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; + [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]]; + [req setHTTPBody:postBody]; [req setHTTPMethod:@"POST"]; @@ -193,7 +196,8 @@ error:error]; [response_ retain]; - + [req release]; + return data; } diff --git a/thirdparty/breakpad/common/mac/MachIPC.mm b/thirdparty/breakpad/common/mac/MachIPC.mm index 4f58c02d6..dc9773f77 100644 --- a/thirdparty/breakpad/common/mac/MachIPC.mm +++ b/thirdparty/breakpad/common/mac/MachIPC.mm @@ -32,6 +32,7 @@ #import #import "MachIPC.h" +#include "common/mac/bootstrap_compat.h" namespace google_breakpad { //============================================================================== @@ -187,9 +188,10 @@ ReceivePort::ReceivePort(const char *receive_port_name) { if (init_result_ != KERN_SUCCESS) return; - init_result_ = bootstrap_register(bootstrap_port, - const_cast(receive_port_name), - port_); + init_result_ = breakpad::BootstrapRegister( + bootstrap_port, + const_cast(receive_port_name), + port_); } //============================================================================== diff --git a/thirdparty/breakpad/common/mac/SimpleStringDictionary.h b/thirdparty/breakpad/common/mac/SimpleStringDictionary.h index 3d6c27bd4..814a6f7a5 100644 --- a/thirdparty/breakpad/common/mac/SimpleStringDictionary.h +++ b/thirdparty/breakpad/common/mac/SimpleStringDictionary.h @@ -134,7 +134,7 @@ class SimpleStringDictionary { // Given |key|, returns its corresponding |value|. // If |key| is NULL, an assert will fire or NULL will be returned. If |key| // is not found or is an empty string, NULL is returned. - const char *GetValueForKey(const char *key); + const char *GetValueForKey(const char *key) const; // Stores a string |value| represented by |key|. If |key| is NULL or an empty // string, this will assert (or do nothing). If |value| is NULL then diff --git a/thirdparty/breakpad/common/mac/SimpleStringDictionary.mm b/thirdparty/breakpad/common/mac/SimpleStringDictionary.mm index d9c791cc1..b97b760c6 100644 --- a/thirdparty/breakpad/common/mac/SimpleStringDictionary.mm +++ b/thirdparty/breakpad/common/mac/SimpleStringDictionary.mm @@ -55,13 +55,13 @@ int SimpleStringDictionary::GetCount() const { } //============================================================================== -const char *SimpleStringDictionary::GetValueForKey(const char *key) { +const char *SimpleStringDictionary::GetValueForKey(const char *key) const { assert(key); if (!key) return NULL; for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { - KeyValueEntry &entry = entries_[i]; + const KeyValueEntry &entry = entries_[i]; if (entry.IsActive() && !strcmp(entry.GetKey(), key)) { return entry.GetValue(); } diff --git a/thirdparty/breakpad/common/mac/bootstrap_compat.cc b/thirdparty/breakpad/common/mac/bootstrap_compat.cc new file mode 100644 index 000000000..d875d95b5 --- /dev/null +++ b/thirdparty/breakpad/common/mac/bootstrap_compat.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "common/mac/bootstrap_compat.h" + +namespace breakpad { + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp) { + return bootstrap_register(bp, service_name, sp); +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + +} // namesapce breakpad diff --git a/thirdparty/breakpad/common/mac/bootstrap_compat.h b/thirdparty/breakpad/common/mac/bootstrap_compat.h new file mode 100644 index 000000000..8ca7357c3 --- /dev/null +++ b/thirdparty/breakpad/common/mac/bootstrap_compat.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef COMMON_MAC_BOOTSTRAP_COMPAT_H_ +#define COMMON_MAC_BOOTSTRAP_COMPAT_H_ + +#include + +namespace breakpad { + +// Wrapper for bootstrap_register to avoid deprecation warnings. +// +// In 10.6, it's possible to call bootstrap_check_in as the one-stop-shop for +// handling what bootstrap_register is used for. In 10.5, bootstrap_check_in +// can't check in a service whose name has not yet been registered, despite +// bootstrap_register being marked as deprecated in that OS release. Breakpad +// needs to register new service names, and in 10.5, calling +// bootstrap_register is the only way to achieve that. Attempts to call +// bootstrap_check_in for a new service name on 10.5 will result in +// BOOTSTRAP_UNKNOWN_SERVICE being returned rather than registration of the +// new service name. +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp); + +} // namespace breakpad + +#endif // COMMON_MAC_BOOTSTRAP_COMPAT_H_ diff --git a/thirdparty/breakpad/common/mac/dump_syms.h b/thirdparty/breakpad/common/mac/dump_syms.h index f197ca287..0e2f464d7 100644 --- a/thirdparty/breakpad/common/mac/dump_syms.h +++ b/thirdparty/breakpad/common/mac/dump_syms.h @@ -1,6 +1,6 @@ // -*- mode: c++ -*- -// Copyright (c) 2010, Google Inc. +// Copyright (c) 2011, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -52,9 +52,9 @@ namespace google_breakpad { class DumpSymbols { public: - DumpSymbols() + DumpSymbols() : input_pathname_(), - object_filename_(), + object_filename_(), contents_(), selected_object_file_(), selected_object_name_() { } @@ -84,9 +84,9 @@ class DumpSymbols { // object file, then the dumper will dump the object file whose // architecture matches that of this dumper program. bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); - - // If this dumper's file includes an object file for |arch_name|, then select - // that object file for dumping, and return true. Otherwise, return false, + + // If this dumper's file includes an object file for |arch_name|, then select + // that object file for dumping, and return true. Otherwise, return false, // and leave this dumper's selected architecture unchanged. // // By default, if this dumper's file contains only one object file, then @@ -94,7 +94,7 @@ class DumpSymbols { // object file, then the dumper will dump the object file whose // architecture matches that of this dumper program. bool SetArchitecture(const std::string &arch_name); - + // Return a pointer to an array of 'struct fat_arch' structures, // describing the object files contained in this dumper's file. Set // *|count| to the number of elements in the array. The returned array is @@ -109,10 +109,10 @@ class DumpSymbols { return NULL; } - // Read the selected object file's debugging information, and write it - // out to |stream|. Return true on success; if an error occurs, report it - // and return false. - bool WriteSymbolFile(std::ostream &stream); + // Read the selected object file's debugging information, and write it out to + // |stream|. Write the CFI section if |cfi| is true. Return true on success; + // if an error occurs, report it and return false. + bool WriteSymbolFile(std::ostream &stream, bool cfi); private: // Used internally. @@ -158,7 +158,7 @@ class DumpSymbols { // has exactly one element. vector object_files_; - // The object file in object_files_ selected to dump, or NULL if + // The object file in object_files_ selected to dump, or NULL if // SetArchitecture hasn't been called yet. const struct fat_arch *selected_object_file_; diff --git a/thirdparty/breakpad/common/mac/dump_syms.mm b/thirdparty/breakpad/common/mac/dump_syms.mm index 5fd614f20..9783514f0 100644 --- a/thirdparty/breakpad/common/mac/dump_syms.mm +++ b/thirdparty/breakpad/common/mac/dump_syms.mm @@ -1,6 +1,6 @@ // -*- mode: c++ -*- -// Copyright (c) 2010, Google Inc. +// Copyright (c) 2011, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -79,7 +79,7 @@ namespace google_breakpad { bool DumpSymbols::Read(NSString *filename) { if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { fprintf(stderr, "Object file does not exist: %s\n", - [filename fileSystemRepresentation]); + [filename fileSystemRepresentation]); return false; } @@ -101,15 +101,15 @@ bool DumpSymbols::Read(NSString *filename) { // pathForResource:ofType:inDirectory likes. NSString *base_name = [input_pathname_ lastPathComponent]; NSString *dwarf_resource; - + do { NSString *new_base_name = [base_name stringByDeletingPathExtension]; // If stringByDeletingPathExtension returned the name unchanged, then // there's nothing more for us to strip off --- lose. if ([new_base_name isEqualToString:base_name]) { - fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", - [input_pathname_ fileSystemRepresentation]); + fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", + [input_pathname_ fileSystemRepresentation]); return false; } @@ -141,12 +141,12 @@ bool DumpSymbols::Read(NSString *filename) { // file don't affect memory and vice versa). NSError *error; contents_ = [NSData dataWithContentsOfFile:object_filename_ - options:0 - error:&error]; + options:0 + error:&error]; if (!contents_) { fprintf(stderr, "Error reading object file: %s: %s\n", - [object_filename_ fileSystemRepresentation], - [[error localizedDescription] UTF8String]); + [object_filename_ fileSystemRepresentation], + [[error localizedDescription] UTF8String]); return false; } [contents_ retain]; @@ -166,7 +166,7 @@ bool DumpSymbols::Read(NSString *filename) { fat_reader.object_files(&object_files_count); if (object_files_count == 0) { fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", - [object_filename_ fileSystemRepresentation]); + [object_filename_ fileSystemRepresentation]); return false; } object_files_.resize(object_files_count); @@ -197,14 +197,14 @@ bool DumpSymbols::SetArchitecture(const std::string &arch_name) { } return arch_set; } - + string DumpSymbols::Identifier() { FileID file_id([object_filename_ fileSystemRepresentation]); unsigned char identifier_bytes[16]; cpu_type_t cpu_type = selected_object_file_->cputype; if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) { fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", - [object_filename_ fileSystemRepresentation]); + [object_filename_ fileSystemRepresentation]); return ""; } @@ -243,7 +243,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, const mach_o::Reader &macho_reader, const mach_o::SectionMap &dwarf_sections) const { // Build a byte reader of the appropriate endianness. - ByteReader byte_reader(macho_reader.big_endian() + ByteReader byte_reader(macho_reader.big_endian() ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE); @@ -265,10 +265,10 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, // There had better be a __debug_info section! if (!debug_info_section.first) { fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", - selected_object_name_.c_str()); + selected_object_name_.c_str()); return false; } - + // Build a line-to-module loader for the root handler to use. DumperLineToModule line_to_module(&byte_reader); @@ -343,7 +343,7 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module, // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so // this is the only base address the CFI parser will need. byte_reader.SetCFIDataBase(section.address, cfi); - + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, section.section_name); dwarf2reader::CallFrameInfo parser(cfi, cfi_size, @@ -421,7 +421,7 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, return true; } -bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { +bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) { // Select an object file, if SetArchitecture hasn't been called to set one // explicitly. if (!selected_object_file_) { @@ -433,10 +433,10 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { const NXArchInfo *local_arch = NXGetLocalArchInfo(); if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { fprintf(stderr, "%s: object file contains more than one" - " architecture, none of which match the current" + " architecture, none of which match the current" " architecture; specify an architecture explicitly" - " with '-a ARCH' to resolve the ambiguity\n", - [object_filename_ fileSystemRepresentation]); + " with '-a ARCH' to resolve the ambiguity\n", + [object_filename_ fileSystemRepresentation]); return false; } } @@ -472,7 +472,7 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { identifier += "0"; // Create a module to hold the debugging information. - Module module([module_name UTF8String], "mac", selected_arch_name, + Module module([module_name UTF8String], "mac", selected_arch_name, identifier); // Parse the selected object file. @@ -481,8 +481,8 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { if (!reader.Read(reinterpret_cast([contents_ bytes]) + selected_object_file_->offset, selected_object_file_->size, - selected_object_file_->cputype, - selected_object_file_->cpusubtype)) + selected_object_file_->cputype, + selected_object_file_->cpusubtype)) return false; // Walk its load commands, and deal with whatever is there. @@ -490,7 +490,7 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) { if (!reader.WalkLoadCommands(&load_command_dumper)) return false; - return module.Write(stream); + return module.Write(stream, cfi); } } // namespace google_breakpad diff --git a/thirdparty/breakpad/common/mac/macho_id.cc b/thirdparty/breakpad/common/mac/macho_id.cc index 44e78205f..abe1fabdf 100644 --- a/thirdparty/breakpad/common/mac/macho_id.cc +++ b/thirdparty/breakpad/common/mac/macho_id.cc @@ -51,18 +51,29 @@ extern "C" { // necessary for Leopard namespace MacFileUtilities { +using google_breakpad::MD5Init; +using google_breakpad::MD5Update; +using google_breakpad::MD5Final; + MachoID::MachoID(const char *path) - : file_(0), + : memory_(0), + memory_size_(0), + crc_(0), + md5_context_(), + update_function_(NULL) { + strlcpy(path_, path, sizeof(path_)); +} + +MachoID::MachoID(const char *path, void *memory, size_t size) + : memory_(memory), + memory_size_(size), crc_(0), md5_context_(), update_function_(NULL) { strlcpy(path_, path, sizeof(path_)); - file_ = open(path, O_RDONLY); } MachoID::~MachoID() { - if (file_ != -1) - close(file_); } // The CRC info is from http://en.wikipedia.org/wiki/Adler-32 @@ -144,10 +155,8 @@ void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) { bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) { struct breakpad_uuid_command uuid_cmd; - MachoWalker walker(path_, UUIDWalkerCB, &uuid_cmd); - uuid_cmd.cmd = 0; - if (!walker.WalkHeader(cpu_type)) + if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd)) return false; // If we found the command, we'll have initialized the uuid_command @@ -162,10 +171,8 @@ bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) { bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) { struct dylib_command dylib_cmd; - MachoWalker walker(path_, IDWalkerCB, &dylib_cmd); - dylib_cmd.cmd = 0; - if (!walker.WalkHeader(cpu_type)) + if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd)) return false; // If we found the command, we'll have initialized the dylib_command @@ -204,29 +211,39 @@ bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) { } uint32_t MachoID::Adler32(int cpu_type) { - MachoWalker walker(path_, WalkerCB, this); update_function_ = &MachoID::UpdateCRC; crc_ = 0; - if (!walker.WalkHeader(cpu_type)) + if (!WalkHeader(cpu_type, WalkerCB, this)) return 0; return crc_; } bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) { - MachoWalker walker(path_, WalkerCB, this); update_function_ = &MachoID::UpdateMD5; MD5Init(&md5_context_); - if (!walker.WalkHeader(cpu_type)) + if (!WalkHeader(cpu_type, WalkerCB, this)) return false; MD5Final(identifier, &md5_context_); return true; } +bool MachoID::WalkHeader(int cpu_type, + MachoWalker::LoadCommandCallback callback, + void *context) { + if (memory_) { + MachoWalker walker(memory_, memory_size_, callback, context); + return walker.WalkHeader(cpu_type); + } else { + MachoWalker walker(path_, callback, context); + return walker.WalkHeader(cpu_type); + } +} + // static bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, bool swap, void *context) { diff --git a/thirdparty/breakpad/common/mac/macho_id.h b/thirdparty/breakpad/common/mac/macho_id.h index 9bcefc561..ccb126d42 100644 --- a/thirdparty/breakpad/common/mac/macho_id.h +++ b/thirdparty/breakpad/common/mac/macho_id.h @@ -37,15 +37,15 @@ #include #include +#include "common/mac/macho_walker.h" #include "common/md5.h" namespace MacFileUtilities { -class MachoWalker; - class MachoID { public: MachoID(const char *path); + MachoID(const char *path, void *memory, size_t size); ~MachoID(); // For the given |cpu_type|, return a UUID from the LC_UUID command. @@ -80,6 +80,10 @@ class MachoID { // Bottleneck for update routines void Update(MachoWalker *walker, off_t offset, size_t size); + // Factory for the MachoWalker + bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback, + void *context); + // The callback from the MachoWalker for CRC and MD5 static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, bool swap, void *context); @@ -95,14 +99,17 @@ class MachoID { // File path char path_[PATH_MAX]; - // File descriptor - int file_; + // Memory region to read from + void *memory_; + + // Size of the memory region + size_t memory_size_; // The current crc value uint32_t crc_; // The MD5 context - MD5Context md5_context_; + google_breakpad::MD5Context md5_context_; // The current update to call from the Update callback UpdateFunction update_function_; diff --git a/thirdparty/breakpad/common/mac/macho_reader.cc b/thirdparty/breakpad/common/mac/macho_reader.cc index ff16bc617..f1f0a179d 100644 --- a/thirdparty/breakpad/common/mac/macho_reader.cc +++ b/thirdparty/breakpad/common/mac/macho_reader.cc @@ -38,6 +38,11 @@ #include #include +// Unfortunately, CPU_TYPE_ARM is not define for 10.4. +#if !defined(CPU_TYPE_ARM) +#define CPU_TYPE_ARM 12 +#endif + namespace google_breakpad { namespace mach_o { @@ -230,6 +235,7 @@ bool Reader::Read(const uint8_t *buffer, uint32_t expected_magic; // validate that magic matches the expected cpu type switch (expected_cpu_type) { + case CPU_TYPE_ARM: case CPU_TYPE_I386: expected_magic = MH_CIGAM; break; diff --git a/thirdparty/breakpad/common/mac/macho_walker.cc b/thirdparty/breakpad/common/mac/macho_walker.cc index 3e8fe211d..92da7b1f2 100644 --- a/thirdparty/breakpad/common/mac/macho_walker.cc +++ b/thirdparty/breakpad/common/mac/macho_walker.cc @@ -52,6 +52,8 @@ namespace MacFileUtilities { MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback, void *context) : file_(0), + memory_(NULL), + memory_size_(0), callback_(callback), callback_context_(context), current_header_(NULL), @@ -60,6 +62,18 @@ MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback, file_ = open(path, O_RDONLY); } +MachoWalker::MachoWalker(void *memory, size_t size, + LoadCommandCallback callback, void *context) + : file_(0), + memory_(memory), + memory_size_(size), + callback_(callback), + callback_context_(context), + current_header_(NULL), + current_header_size_(0), + current_header_offset_(0) { +} + MachoWalker::~MachoWalker() { if (file_ != -1) close(file_); @@ -90,7 +104,21 @@ bool MachoWalker::WalkHeader(int cpu_type) { } bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) { - return pread(file_, buffer, size, offset) == (ssize_t)size; + if (memory_) { + if (offset < 0) + return false; + bool result = true; + if (offset + size > memory_size_) { + if (static_cast(offset) >= memory_size_) + return false; + size = memory_size_ - offset; + result = false; + } + memcpy(buffer, static_cast(memory_) + offset, size); + return result; + } else { + return pread(file_, buffer, size, offset) == (ssize_t)size; + } } bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) { diff --git a/thirdparty/breakpad/common/mac/macho_walker.h b/thirdparty/breakpad/common/mac/macho_walker.h index 0d30b5c05..cee3eb8db 100644 --- a/thirdparty/breakpad/common/mac/macho_walker.h +++ b/thirdparty/breakpad/common/mac/macho_walker.h @@ -52,7 +52,8 @@ class MachoWalker { off_t offset, bool swap, void *context); MachoWalker(const char *path, LoadCommandCallback callback, void *context); - MachoWalker(int file_descriptor, LoadCommandCallback callback, void *context); + MachoWalker(void *memory, size_t size, LoadCommandCallback callback, + void *context); ~MachoWalker(); // Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the @@ -68,7 +69,7 @@ class MachoWalker { // Read |size| bytes from the opened file at |offset| into |buffer| bool ReadBytes(void *buffer, size_t size, off_t offset); - + // Return the current header and header offset bool CurrentHeader(struct mach_header_64 *header, off_t *offset); @@ -87,19 +88,25 @@ class MachoWalker { // File descriptor to the opened file int file_; + // Memory location to read from. + void *memory_; + + // Size of the memory segment we can read from. + size_t memory_size_; + // User specified callback & context LoadCommandCallback callback_; void *callback_context_; - + // Current header, size, and offset. The mach_header_64 is used for both // 32-bit and 64-bit headers because they only differ in their last field - // (reserved). By adding the |current_header_size_| and the + // (reserved). By adding the |current_header_size_| and the // |current_header_offset_|, you can determine the offset in the file just // after the header. struct mach_header_64 *current_header_; unsigned long current_header_size_; off_t current_header_offset_; - + private: MachoWalker(const MachoWalker &); MachoWalker &operator=(const MachoWalker &); diff --git a/thirdparty/breakpad/common/md5.c b/thirdparty/breakpad/common/md5.cc similarity index 99% rename from thirdparty/breakpad/common/md5.c rename to thirdparty/breakpad/common/md5.cc index 539bd6d2f..bccf61c60 100644 --- a/thirdparty/breakpad/common/md5.c +++ b/thirdparty/breakpad/common/md5.cc @@ -17,6 +17,8 @@ #include "common/md5.h" +namespace google_breakpad { + #ifndef WORDS_BIGENDIAN #define byteReverse(buf, len) /* Nothing */ #else @@ -244,3 +246,6 @@ static void MD5Transform(u32 buf[4], u32 const in[16]) buf[2] += c; buf[3] += d; } + +} // namespace google_breakpad + diff --git a/thirdparty/breakpad/common/md5.h b/thirdparty/breakpad/common/md5.h index dbf4893cd..e96521eed 100644 --- a/thirdparty/breakpad/common/md5.h +++ b/thirdparty/breakpad/common/md5.h @@ -5,6 +5,8 @@ #include +namespace google_breakpad { + typedef uint32_t u32; typedef uint8_t u8; @@ -14,18 +16,12 @@ struct MD5Context { u8 in[64]; }; -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - void MD5Init(struct MD5Context *ctx); void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *ctx); -#ifdef __cplusplus -} -#endif +} // namespace google_breakpad #endif // COMMON_MD5_H__ diff --git a/thirdparty/breakpad/common/memory.h b/thirdparty/breakpad/common/memory.h index a02ac578b..e90bd52c2 100644 --- a/thirdparty/breakpad/common/memory.h +++ b/thirdparty/breakpad/common/memory.h @@ -145,6 +145,18 @@ class wasteful_vector { used_(0) { } + T& back() { + return a_[used_ - 1]; + } + + const T& back() const { + return a_[used_ - 1]; + } + + bool empty() const { + return used_ == 0; + } + void push_back(const T& new_element) { if (used_ == allocated_) Realloc(allocated_ * 2); diff --git a/thirdparty/breakpad/common/memory_range.h b/thirdparty/breakpad/common/memory_range.h new file mode 100644 index 000000000..86bd08c9b --- /dev/null +++ b/thirdparty/breakpad/common/memory_range.h @@ -0,0 +1,145 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// memory_range.h: Define the google_breakpad::MemoryRange class, which +// is a lightweight wrapper with a pointer and a length to encapsulate +// a contiguous range of memory. + +#ifndef COMMON_MEMORY_RANGE_H_ +#define COMMON_MEMORY_RANGE_H_ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +// A lightweight wrapper with a pointer and a length to encapsulate a +// contiguous range of memory. It provides helper methods for checked +// access of a subrange of the memory. Its implemementation does not +// allocate memory or call into libc functions, and is thus safer to use +// in a crashed environment. +class MemoryRange { + public: + MemoryRange() : data_(NULL), length_(0) {} + + MemoryRange(const void* data, size_t length) { + Set(data, length); + } + + // Returns true if this memory range contains no data. + bool IsEmpty() const { + // Set() guarantees that |length_| is zero if |data_| is NULL. + return length_ == 0; + } + + // Resets to an empty range. + void Reset() { + data_ = NULL; + length_ = 0; + } + + // Sets this memory range to point to |data| and its length to |length|. + void Set(const void* data, size_t length) { + data_ = reinterpret_cast(data); + // Always set |length_| to zero if |data_| is NULL. + length_ = data ? length : 0; + } + + // Returns true if this range covers a subrange of |sub_length| bytes + // at |sub_offset| bytes of this memory range, or false otherwise. + bool Covers(size_t sub_offset, size_t sub_length) const { + // The following checks verify that: + // 1. sub_offset is within [ 0 .. length_ - 1 ] + // 2. sub_offset + sub_length is within + // [ sub_offset .. length_ ] + return sub_offset < length_ && + sub_offset + sub_length >= sub_offset && + sub_offset + sub_length <= length_; + } + + // Returns a raw data pointer to a subrange of |sub_length| bytes at + // |sub_offset| bytes of this memory range, or NULL if the subrange + // is out of bounds. + const void* GetData(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL; + } + + // Same as the two-argument version of GetData() but uses sizeof(DataType) + // as the subrange length and returns an |DataType| pointer for convenience. + template + const DataType* GetData(size_t sub_offset) const { + return reinterpret_cast( + GetData(sub_offset, sizeof(DataType))); + } + + // Returns a raw pointer to the |element_index|-th element of an array + // of elements of length |element_size| starting at |sub_offset| bytes + // of this memory range, or NULL if the element is out of bounds. + const void* GetArrayElement(size_t element_offset, + size_t element_size, + unsigned element_index) const { + size_t sub_offset = element_offset + element_index * element_size; + return GetData(sub_offset, element_size); + } + + // Same as the three-argument version of GetArrayElement() but deduces + // the element size using sizeof(ElementType) and returns an |ElementType| + // pointer for convenience. + template + const ElementType* GetArrayElement(size_t element_offset, + unsigned element_index) const { + return reinterpret_cast( + GetArrayElement(element_offset, sizeof(ElementType), element_index)); + } + + // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of + // this memory range, or an empty range if the subrange is out of bounds. + MemoryRange Subrange(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? + MemoryRange(data_ + sub_offset, sub_length) : MemoryRange(); + } + + // Returns a pointer to the beginning of this memory range. + const u_int8_t* data() const { return data_; } + + // Returns the length, in bytes, of this memory range. + size_t length() const { return length_; } + + private: + // Pointer to the beginning of this memory range. + const u_int8_t* data_; + + // Length, in bytes, of this memory range. + size_t length_; +}; + +} // namespace google_breakpad + +#endif // COMMON_MEMORY_RANGE_H_ diff --git a/thirdparty/breakpad/common/memory_range_unittest.cc b/thirdparty/breakpad/common/memory_range_unittest.cc new file mode 100644 index 000000000..f0831e85d --- /dev/null +++ b/thirdparty/breakpad/common/memory_range_unittest.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// memory_range_unittest.cc: Unit tests for google_breakpad::MemoryRange. + +#include "breakpad_googletest_includes.h" +#include "common/memory_range.h" + +using google_breakpad::MemoryRange; +using testing::Message; + +namespace { + +const u_int32_t kBuffer[10] = { 0 }; +const size_t kBufferSize = sizeof(kBuffer); +const u_int8_t* kBufferPointer = reinterpret_cast(kBuffer); + +// Test vectors for verifying Covers, GetData, and Subrange. +const struct { + bool valid; + size_t offset; + size_t length; +} kSubranges[] = { + { true, 0, 0 }, + { true, 0, 2 }, + { true, 0, kBufferSize }, + { true, 2, 0 }, + { true, 2, 4 }, + { true, 2, kBufferSize - 2 }, + { true, kBufferSize - 1, 1 }, + { false, kBufferSize, 0 }, + { false, kBufferSize, -1 }, + { false, kBufferSize + 1, 0 }, + { false, -1, 2 }, + { false, 1, kBufferSize }, + { false, kBufferSize - 1, 2 }, + { false, 0, -1 }, + { false, 1, -1 }, +}; +const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]); + +// Test vectors for verifying GetArrayElement. +const struct { + size_t offset; + size_t size; + size_t index; + const void* const pointer; +} kElements[] = { + // Valid array elemenets + { 0, 1, 0, kBufferPointer }, + { 0, 1, 1, kBufferPointer + 1 }, + { 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 }, + { 0, 2, 1, kBufferPointer + 2 }, + { 0, 4, 2, kBufferPointer + 8 }, + { 0, 4, 9, kBufferPointer + 36 }, + { kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 }, + // Invalid array elemenets + { 0, 1, kBufferSize, NULL }, + { 0, 4, 10, NULL }, + { kBufferSize - 1, 1, 1, NULL }, + { kBufferSize - 1, 2, 0, NULL }, + { kBufferSize, 1, 0, NULL }, +}; +const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]); + +} // namespace + +TEST(MemoryRangeTest, DefaultConstructor) { + MemoryRange range; + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, ConstructorWithDataAndLength) { + MemoryRange range(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); +} + +TEST(MemoryRangeTest, Reset) { + MemoryRange range; + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); + + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, Set) { + MemoryRange range; + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Set(NULL, 0); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, SubrangeOfEmptyMemoryRange) { + MemoryRange range; + MemoryRange subrange = range.Subrange(0, 10); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0, subrange.length()); +} + +TEST(MemoryRangeTest, SubrangeAndGetData) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumSubranges; ++i) { + bool valid = kSubranges[i].valid; + size_t sub_offset = kSubranges[i].offset; + size_t sub_length = kSubranges[i].length; + SCOPED_TRACE(Message() << "offset=" << sub_offset + << ", length=" << sub_length); + + MemoryRange subrange = range.Subrange(sub_offset, sub_length); + if (valid) { + EXPECT_TRUE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, + range.GetData(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, subrange.data()); + EXPECT_EQ(sub_length, subrange.length()); + } else { + EXPECT_FALSE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length)); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0, subrange.length()); + } + } +} + +TEST(MemoryRangeTest, GetDataWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetData(0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetData(0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), int_pointer); +} + +TEST(MemoryRangeTest, GetArrayElement) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumElements; ++i) { + size_t element_offset = kElements[i].offset; + size_t element_size = kElements[i].size; + unsigned element_index = kElements[i].index; + const void* const element_pointer = kElements[i].pointer; + SCOPED_TRACE(Message() << "offset=" << element_offset + << ", size=" << element_size + << ", index=" << element_index); + EXPECT_EQ(element_pointer, range.GetArrayElement( + element_offset, element_size, element_index)); + } +} + +TEST(MemoryRangeTest, GetArrayElmentWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetArrayElement(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetArrayElement(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), int_pointer); +} diff --git a/thirdparty/breakpad/common/memory_unittest.cc b/thirdparty/breakpad/common/memory_unittest.cc index 8b2ec4104..d580c1fec 100644 --- a/thirdparty/breakpad/common/memory_unittest.cc +++ b/thirdparty/breakpad/common/memory_unittest.cc @@ -69,6 +69,7 @@ typedef testing::Test WastefulVectorTest; TEST(WastefulVectorTest, Setup) { PageAllocator allocator_; wasteful_vector v(&allocator_); + ASSERT_TRUE(v.empty()); ASSERT_EQ(v.size(), 0u); } @@ -76,8 +77,12 @@ TEST(WastefulVectorTest, Simple) { PageAllocator allocator_; wasteful_vector v(&allocator_); - for (unsigned i = 0; i < 256; ++i) + for (unsigned i = 0; i < 256; ++i) { v.push_back(i); + ASSERT_EQ(i, v.back()); + ASSERT_EQ(&v.back(), &v[i]); + } + ASSERT_FALSE(v.empty()); ASSERT_EQ(v.size(), 256u); for (unsigned i = 0; i < 256; ++i) ASSERT_EQ(v[i], i); diff --git a/thirdparty/breakpad/common/module.cc b/thirdparty/breakpad/common/module.cc index f02b26ea1..4e257d1d5 100644 --- a/thirdparty/breakpad/common/module.cc +++ b/thirdparty/breakpad/common/module.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 Google Inc. +// Copyright (c) 2011 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -39,6 +39,7 @@ #include #include +#include namespace google_breakpad { @@ -56,16 +57,17 @@ Module::Module(const string &name, const string &os, load_address_(0) { } Module::~Module() { - for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) delete it->second; for (FunctionSet::iterator it = functions_.begin(); - it != functions_.end(); it++) + it != functions_.end(); ++it) { delete *it; + } for (vector::iterator it = stack_frame_entries_.begin(); - it != stack_frame_entries_.end(); it++) + it != stack_frame_entries_.end(); ++it) { delete *it; - for (ExternSet::iterator it = externs_.begin(); - it != externs_.end(); it++) + } + for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it) delete *it; } @@ -87,7 +89,7 @@ void Module::AddFunction(Function *function) { void Module::AddFunctions(vector::iterator begin, vector::iterator end) { - for (vector::iterator it = begin; it != end; it++) + for (vector::iterator it = begin; it != end; ++it) AddFunction(*it); } @@ -149,7 +151,7 @@ Module::File *Module::FindExistingFile(const string &name) { void Module::GetFiles(vector *vec) { vec->clear(); - for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) vec->push_back(it->second); } @@ -160,16 +162,17 @@ void Module::GetStackFrameEntries(vector *vec) { void Module::AssignSourceIds() { // First, give every source file an id of -1. for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) + file_it != files_.end(); ++file_it) { file_it->second->source_id = -1; + } // Next, mark all files actually cited by our functions' line number // info, by setting each one's source id to zero. for (FunctionSet::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); func_it++) { + func_it != functions_.end(); ++func_it) { Function *func = *func_it; for (vector::iterator line_it = func->lines.begin(); - line_it != func->lines.end(); line_it++) + line_it != func->lines.end(); ++line_it) line_it->file->source_id = 0; } @@ -179,9 +182,10 @@ void Module::AssignSourceIds() { // lexicographical order by name, which is neat. int next_source_id = 0; for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) + file_it != files_.end(); ++file_it) { if (!file_it->second->source_id) file_it->second->source_id = next_source_id++; + } } bool Module::ReportError() { @@ -192,7 +196,7 @@ bool Module::ReportError() { bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { for (RuleMap::const_iterator it = rule_map.begin(); - it != rule_map.end(); it++) { + it != rule_map.end(); ++it) { if (it != rule_map.begin()) stream << ' '; stream << it->first << ": " << it->second; @@ -200,7 +204,7 @@ bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { return stream.good(); } -bool Module::Write(std::ostream &stream) { +bool Module::Write(std::ostream &stream, bool cfi) { stream << "MODULE " << os_ << " " << architecture_ << " " << id_ << " " << name_ << endl; if (!stream.good()) @@ -210,7 +214,7 @@ bool Module::Write(std::ostream &stream) { // Write out files. for (FileByNameMap::iterator file_it = files_.begin(); - file_it != files_.end(); file_it++) { + file_it != files_.end(); ++file_it) { File *file = file_it->second; if (file->source_id >= 0) { stream << "FILE " << file->source_id << " " << file->name << endl; @@ -221,18 +225,18 @@ bool Module::Write(std::ostream &stream) { // Write out functions and their lines. for (FunctionSet::const_iterator func_it = functions_.begin(); - func_it != functions_.end(); func_it++) { + func_it != functions_.end(); ++func_it) { Function *func = *func_it; stream << "FUNC " << hex << (func->address - load_address_) << " " << func->size << " " << func->parameter_size << " " << func->name << dec << endl; - + if (!stream.good()) return ReportError(); for (vector::iterator line_it = func->lines.begin(); - line_it != func->lines.end(); line_it++) { + line_it != func->lines.end(); ++line_it) { stream << hex << (line_it->address - load_address_) << " " << line_it->size << " " @@ -246,7 +250,7 @@ bool Module::Write(std::ostream &stream) { // Write out 'PUBLIC' records. for (ExternSet::const_iterator extern_it = externs_.begin(); - extern_it != externs_.end(); extern_it++) { + extern_it != externs_.end(); ++extern_it) { Extern *ext = *extern_it; stream << "PUBLIC " << hex << (ext->address - load_address_) << " 0 " @@ -255,34 +259,36 @@ bool Module::Write(std::ostream &stream) { return ReportError(); } - // Write out 'STACK CFI INIT' and 'STACK CFI' records. - vector::const_iterator frame_it; - for (frame_it = stack_frame_entries_.begin(); - frame_it != stack_frame_entries_.end(); frame_it++) { - StackFrameEntry *entry = *frame_it; - stream << "STACK CFI INIT " << hex - << (entry->address - load_address_) << " " - << entry->size << " " << dec; - if (!stream.good() - || !WriteRuleMap(entry->initial_rules, stream)) - return ReportError(); - - stream << endl; - - // Write out this entry's delta rules as 'STACK CFI' records. - for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); - delta_it != entry->rule_changes.end(); delta_it++) { - stream << "STACK CFI " << hex - << (delta_it->first - load_address_) << " " << dec; + if (cfi) { + // Write out 'STACK CFI INIT' and 'STACK CFI' records. + vector::const_iterator frame_it; + for (frame_it = stack_frame_entries_.begin(); + frame_it != stack_frame_entries_.end(); ++frame_it) { + StackFrameEntry *entry = *frame_it; + stream << "STACK CFI INIT " << hex + << (entry->address - load_address_) << " " + << entry->size << " " << dec; if (!stream.good() - || !WriteRuleMap(delta_it->second, stream)) + || !WriteRuleMap(entry->initial_rules, stream)) return ReportError(); stream << endl; + + // Write out this entry's delta rules as 'STACK CFI' records. + for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); + delta_it != entry->rule_changes.end(); ++delta_it) { + stream << "STACK CFI " << hex + << (delta_it->first - load_address_) << " " << dec; + if (!stream.good() + || !WriteRuleMap(delta_it->second, stream)) + return ReportError(); + + stream << endl; + } } } return true; } -} // namespace google_breakpad +} // namespace google_breakpad diff --git a/thirdparty/breakpad/common/module.h b/thirdparty/breakpad/common/module.h index 55f260f14..734a1afcb 100644 --- a/thirdparty/breakpad/common/module.h +++ b/thirdparty/breakpad/common/module.h @@ -259,14 +259,15 @@ class Module { // breakpad symbol format. Return true if all goes well, or false if // an error occurs. This method writes out: // - a header based on the values given to the constructor, - // - the source files added via FindFile, and finally - // - the functions added via AddFunctions, each with its lines. + // - the source files added via FindFile, + // - the functions added via AddFunctions, each with its lines, + // - all public records, + // - and if CFI is true, all CFI records. // Addresses in the output are all relative to the load address // established by SetLoadAddress. - bool Write(std::ostream &stream); + bool Write(std::ostream &stream, bool cfi); private: - // Report an error that has occurred writing the symbol file, using // errno to find the appropriate cause. Return false. static bool ReportError(); @@ -287,7 +288,7 @@ class Module { // Relation for maps whose keys are strings shared with some other // structure. struct CompareStringPtrs { - bool operator()(const string *x, const string *y) { return *x < *y; }; + bool operator()(const string *x, const string *y) { return *x < *y; } }; // A map from filenames to File structures. The map's keys are @@ -315,6 +316,6 @@ class Module { ExternSet externs_; }; -} // namespace google_breakpad +} // namespace google_breakpad #endif // COMMON_LINUX_MODULE_H__ diff --git a/thirdparty/breakpad/common/module_unittest.cc b/thirdparty/breakpad/common/module_unittest.cc index 4972bbe80..85c3b1c62 100644 --- a/thirdparty/breakpad/common/module_unittest.cc +++ b/thirdparty/breakpad/common/module_unittest.cc @@ -70,7 +70,7 @@ static Module::Function *generate_duplicate_function(const string &name) { TEST(Write, Header) { stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", contents.c_str()); @@ -91,7 +91,7 @@ TEST(Write, OneLineFunc) { function->lines.push_back(line); m.AddFunction(function); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 file_name.cc\n" @@ -141,7 +141,7 @@ TEST(Write, RelativeLoadAddress) { // the module must work fine. m.SetLoadAddress(0x2ab698b0b6407073LL); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 filename-a.cc\n" @@ -164,7 +164,7 @@ TEST(Write, OmitUnusedFiles) { // Create some source files. Module::File *file1 = m.FindFile("filename1"); - m.FindFile("filename2"); // not used by any line + m.FindFile("filename2"); // not used by any line Module::File *file3 = m.FindFile("filename3"); // Create a function. @@ -197,7 +197,7 @@ TEST(Write, OmitUnusedFiles) { EXPECT_NE(-1, vec[2]->source_id); stringstream s; - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FILE 0 filename1\n" @@ -209,6 +209,52 @@ TEST(Write, OmitUnusedFiles) { contents.c_str()); } +TEST(Write, NoCFI) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Some source files. We will expect to see them in lexicographic order. + Module::File *file1 = m.FindFile("filename.cc"); + + // A function. + Module::Function *function = new(Module::Function); + function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)"; + function->address = 0xbec774ea5dd935f3LL; + function->size = 0x2922088f98d3f6fcLL; + function->parameter_size = 0xe5e9aa008bd5f0d0LL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL, + file1, 41676901 }; + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry *entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + // Set the load address. Doing this after adding all the data to + // the module must work fine. + m.SetLoadAddress(0x2ab698b0b6407073LL); + + m.Write(s, false); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n", + contents.c_str()); +} + TEST(Construct, AddFunctions) { stringstream s; Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); @@ -233,7 +279,7 @@ TEST(Construct, AddFunctions) { m.AddFunctions(vec.begin(), vec.end()); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" @@ -285,7 +331,7 @@ TEST(Construct, AddFrames) { m.AddStackFrameEntry(entry3); // Check that Write writes STACK CFI records properly. - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n" @@ -361,7 +407,7 @@ TEST(Construct, DuplicateFunctions) { m.AddFunction(function1); m.AddFunction(function2); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" @@ -380,7 +426,7 @@ TEST(Construct, FunctionsWithSameAddress) { m.AddFunction(function1); m.AddFunction(function2); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" @@ -407,7 +453,7 @@ TEST(Construct, Externs) { m.AddExtern(extern1); m.AddExtern(extern2); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " @@ -434,7 +480,7 @@ TEST(Construct, DuplicateExterns) { m.AddExtern(extern1); m.AddExtern(extern2); - m.Write(s); + m.Write(s, true); string contents = s.str(); EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " diff --git a/thirdparty/breakpad/common/stabs_reader.cc b/thirdparty/breakpad/common/stabs_reader.cc index 7dd2eecd0..4897361e1 100644 --- a/thirdparty/breakpad/common/stabs_reader.cc +++ b/thirdparty/breakpad/common/stabs_reader.cc @@ -130,7 +130,7 @@ bool StabsReader::ProcessCompilationUnit() { // There may be an N_SO entry whose name ends with a slash, // indicating the directory in which the compilation occurred. // The build directory defaults to NULL. - const char *build_directory = NULL; + const char *build_directory = NULL; { const char *name = SymbolString(); if (name[0] && name[strlen(name) - 1] == '/') { @@ -138,7 +138,7 @@ bool StabsReader::ProcessCompilationUnit() { ++iterator_; } } - + // We expect to see an N_SO entry with a filename next, indicating // the start of the compilation unit. { @@ -212,7 +212,7 @@ bool StabsReader::ProcessCompilationUnit() { queued_lines_.clear(); return true; -} +} bool StabsReader::ProcessFunction() { assert(!iterator_->at_end && iterator_->type == N_FUN); @@ -237,7 +237,7 @@ bool StabsReader::ProcessFunction() { return false; } queued_lines_.clear(); - + while (!iterator_->at_end) { if (iterator_->type == N_SO || iterator_->type == N_FUN) break; @@ -266,8 +266,8 @@ bool StabsReader::ProcessFunction() { if (!iterator_->at_end) { assert(iterator_->type == N_SO || iterator_->type == N_FUN); if (iterator_->type == N_FUN) { - const char *name = SymbolString(); - if (name[0] == '\0') { + const char *symbol_name = SymbolString(); + if (symbol_name[0] == '\0') { // An N_FUN entry with no name is a terminator for this function; // its value is the function's size. ending_address = function_address + iterator_->value; diff --git a/thirdparty/breakpad/common/string_conversion.cc b/thirdparty/breakpad/common/string_conversion.cc index 5af12f5ac..ac4f1da56 100644 --- a/thirdparty/breakpad/common/string_conversion.cc +++ b/thirdparty/breakpad/common/string_conversion.cc @@ -42,7 +42,7 @@ void UTF8ToUTF16(const char *in, vector *out) { const UTF8 *source_ptr = reinterpret_cast(in); const UTF8 *source_end_ptr = source_ptr + source_length; // Erase the contents and zero fill to the expected size - out->empty(); + out->clear(); out->insert(out->begin(), source_length, 0); u_int16_t *target_ptr = &(*out)[0]; u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t); @@ -86,7 +86,7 @@ void UTF32ToUTF16(const wchar_t *in, vector *out) { const UTF32 *source_ptr = reinterpret_cast(in); const UTF32 *source_end_ptr = source_ptr + source_length; // Erase the contents and zero fill to the expected size - out->empty(); + out->clear(); out->insert(out->begin(), source_length, 0); u_int16_t *target_ptr = &(*out)[0]; u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t); diff --git a/thirdparty/breakpad/common/windows/guid_string.h b/thirdparty/breakpad/common/windows/guid_string.h index 57716e504..f8aa8a231 100644 --- a/thirdparty/breakpad/common/windows/guid_string.h +++ b/thirdparty/breakpad/common/windows/guid_string.h @@ -32,7 +32,7 @@ #ifndef COMMON_WINDOWS_GUID_STRING_H__ #define COMMON_WINDOWS_GUID_STRING_H__ -#include +#include #include diff --git a/thirdparty/breakpad/common/windows/pdb_source_line_writer.cc b/thirdparty/breakpad/common/windows/pdb_source_line_writer.cc index 74a8a70c5..8d8e55c94 100644 --- a/thirdparty/breakpad/common/windows/pdb_source_line_writer.cc +++ b/thirdparty/breakpad/common/windows/pdb_source_line_writer.cc @@ -92,13 +92,13 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { switch (format) { case PDB_FILE: if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { - fprintf(stderr, "loadDataFromPdb failed\n"); + fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str()); return false; } break; case EXE_FILE: if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { - fprintf(stderr, "loadDataForExe failed\n"); + fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str()); return false; } code_file_ = file; @@ -106,7 +106,7 @@ bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { case ANY_FILE: if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { - fprintf(stderr, "loadDataForPdb and loadDataFromExe failed\n"); + fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", file.c_str()); return false; } code_file_ = file; diff --git a/thirdparty/breakpad/google_breakpad/common/breakpad_types.h b/thirdparty/breakpad/google_breakpad/common/breakpad_types.h index c46bb7848..926b47f2a 100644 --- a/thirdparty/breakpad/google_breakpad/common/breakpad_types.h +++ b/thirdparty/breakpad/google_breakpad/common/breakpad_types.h @@ -57,7 +57,7 @@ typedef uint64_t u_int64_t; #else /* !_WIN32 */ -#include +#include typedef unsigned __int8 u_int8_t; typedef unsigned __int16 u_int16_t; diff --git a/thirdparty/breakpad/google_breakpad/common/minidump_cpu_arm.h b/thirdparty/breakpad/google_breakpad/common/minidump_cpu_arm.h index aa1c88f52..dd0712930 100644 --- a/thirdparty/breakpad/google_breakpad/common/minidump_cpu_arm.h +++ b/thirdparty/breakpad/google_breakpad/common/minidump_cpu_arm.h @@ -120,10 +120,11 @@ typedef struct { * purpose. */ enum MDARMRegisterNumbers { - MD_CONTEXT_ARM_REG_FP = 11, - MD_CONTEXT_ARM_REG_SP = 13, - MD_CONTEXT_ARM_REG_LR = 14, - MD_CONTEXT_ARM_REG_PC = 15 + MD_CONTEXT_ARM_REG_IOS_FP = 7, + MD_CONTEXT_ARM_REG_FP = 11, + MD_CONTEXT_ARM_REG_SP = 13, + MD_CONTEXT_ARM_REG_LR = 14, + MD_CONTEXT_ARM_REG_PC = 15 }; /* For (MDRawContextARM).context_flags. These values indicate the type of diff --git a/thirdparty/breakpad/google_breakpad/common/minidump_exception_mac.h b/thirdparty/breakpad/google_breakpad/common/minidump_exception_mac.h index 5fba44ca1..01f8febb8 100644 --- a/thirdparty/breakpad/google_breakpad/common/minidump_exception_mac.h +++ b/thirdparty/breakpad/google_breakpad/common/minidump_exception_mac.h @@ -87,9 +87,11 @@ typedef enum { /* KERN_MEMORY_ERROR */ /* With MD_EXCEPTION_SOFTWARE */ - MD_EXCEPTION_CODE_MAC_BAD_SYSCALL = 0x00010000, /* Mach SIGSYS */ - MD_EXCEPTION_CODE_MAC_BAD_PIPE = 0x00010001, /* Mach SIGPIPE */ - MD_EXCEPTION_CODE_MAC_ABORT = 0x00010002, /* Mach SIGABRT */ + MD_EXCEPTION_CODE_MAC_BAD_SYSCALL = 0x00010000, /* Mach SIGSYS */ + MD_EXCEPTION_CODE_MAC_BAD_PIPE = 0x00010001, /* Mach SIGPIPE */ + MD_EXCEPTION_CODE_MAC_ABORT = 0x00010002, /* Mach SIGABRT */ + /* Custom values */ + MD_EXCEPTION_CODE_MAC_NS_EXCEPTION = 0xDEADC0DE, /* uncaught NSException */ /* With MD_EXCEPTION_MAC_BAD_ACCESS on ppc */ MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ = 0x0101, diff --git a/thirdparty/breakpad/google_breakpad/common/minidump_format.h b/thirdparty/breakpad/google_breakpad/common/minidump_format.h index 6c0513057..a92007a58 100644 --- a/thirdparty/breakpad/google_breakpad/common/minidump_format.h +++ b/thirdparty/breakpad/google_breakpad/common/minidump_format.h @@ -609,6 +609,7 @@ typedef enum { /* The following values are Breakpad-defined. */ MD_OS_UNIX = 0x8000, /* Generic Unix-ish */ MD_OS_MAC_OS_X = 0x8101, /* Mac OS X/Darwin */ + MD_OS_IOS = 0x8102, /* iOS */ MD_OS_LINUX = 0x8201, /* Linux */ MD_OS_SOLARIS = 0x8202 /* Solaris */ } MDOSPlatform; diff --git a/thirdparty/breakpad/google_breakpad/processor/minidump_processor.h b/thirdparty/breakpad/google_breakpad/processor/minidump_processor.h index 384c60c1f..3ed0ebff6 100644 --- a/thirdparty/breakpad/google_breakpad/processor/minidump_processor.h +++ b/thirdparty/breakpad/google_breakpad/processor/minidump_processor.h @@ -42,7 +42,7 @@ class Minidump; class ProcessState; class SourceLineResolverInterface; class SymbolSupplier; -class SystemInfo; +struct SystemInfo; // Return type for Process() enum ProcessResult { PROCESS_OK, // The minidump was diff --git a/thirdparty/breakpad/google_breakpad/processor/source_line_resolver_interface.h b/thirdparty/breakpad/google_breakpad/processor/source_line_resolver_interface.h index 103f979ef..9fe7ef4e1 100644 --- a/thirdparty/breakpad/google_breakpad/processor/source_line_resolver_interface.h +++ b/thirdparty/breakpad/google_breakpad/processor/source_line_resolver_interface.h @@ -44,7 +44,7 @@ using std::string; struct StackFrame; struct WindowsFrameInfo; -struct CFIFrameInfo; +class CFIFrameInfo; class SourceLineResolverInterface { public: diff --git a/thirdparty/breakpad/google_breakpad/processor/stack_frame_cpu.h b/thirdparty/breakpad/google_breakpad/processor/stack_frame_cpu.h index 805b6bc3c..cc6b0149e 100644 --- a/thirdparty/breakpad/google_breakpad/processor/stack_frame_cpu.h +++ b/thirdparty/breakpad/google_breakpad/processor/stack_frame_cpu.h @@ -47,7 +47,7 @@ namespace google_breakpad { struct WindowsFrameInfo; -struct CFIFrameInfo; +class CFIFrameInfo; struct StackFrameX86 : public StackFrame { // ContextValidity has one entry for each relevant hardware pointer diff --git a/thirdparty/breakpad/google_breakpad/processor/stackwalker.h b/thirdparty/breakpad/google_breakpad/processor/stackwalker.h index 6822f16ac..b184d025a 100644 --- a/thirdparty/breakpad/google_breakpad/processor/stackwalker.h +++ b/thirdparty/breakpad/google_breakpad/processor/stackwalker.h @@ -54,7 +54,7 @@ class MinidumpContext; class SourceLineResolverInterface; struct StackFrame; class SymbolSupplier; -class SystemInfo; +struct SystemInfo; using std::set; @@ -108,6 +108,15 @@ class Stackwalker { // Returns false otherwise. bool InstructionAddressSeemsValid(u_int64_t address); + template + bool ScanForReturnAddress(InstructionType location_start, + InstructionType *location_found, + InstructionType *ip_found) { + const int kRASearchWords = 30; + return ScanForReturnAddress(location_start, location_found, ip_found, + kRASearchWords); + } + // Scan the stack starting at location_start, looking for an address // that looks like a valid instruction pointer. Addresses must // 1) be contained in the current stack memory @@ -120,10 +129,10 @@ class Stackwalker { template bool ScanForReturnAddress(InstructionType location_start, InstructionType *location_found, - InstructionType *ip_found) { - const int kRASearchWords = 30; + InstructionType *ip_found, + int searchwords) { for (InstructionType location = location_start; - location <= location_start + kRASearchWords * sizeof(InstructionType); + location <= location_start + searchwords * sizeof(InstructionType); location += sizeof(InstructionType)) { InstructionType ip; if (!memory_->GetMemoryAtAddress(location, &ip)) diff --git a/thirdparty/breakpad/google_breakpad/processor/symbol_supplier.h b/thirdparty/breakpad/google_breakpad/processor/symbol_supplier.h index 26f5d7fa4..8c4d7f718 100644 --- a/thirdparty/breakpad/google_breakpad/processor/symbol_supplier.h +++ b/thirdparty/breakpad/google_breakpad/processor/symbol_supplier.h @@ -39,7 +39,7 @@ namespace google_breakpad { using std::string; class CodeModule; -class SystemInfo; +struct SystemInfo; class SymbolSupplier { public: diff --git a/thirdparty/breakpad/processor/basic_source_line_resolver_unittest.cc b/thirdparty/breakpad/processor/basic_source_line_resolver_unittest.cc index c241ea0a0..5fda6f5b3 100644 --- a/thirdparty/breakpad/processor/basic_source_line_resolver_unittest.cc +++ b/thirdparty/breakpad/processor/basic_source_line_resolver_unittest.cc @@ -200,6 +200,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_EQ(frame.source_line_base, 0x1000); windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA); ASSERT_FALSE(windows_frame_info->allocates_base_pointer); ASSERT_EQ(windows_frame_info->program_string, "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ ="); @@ -219,6 +220,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_EQ(frame.source_line, 0); windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN); ASSERT_FALSE(windows_frame_info->allocates_base_pointer); ASSERT_TRUE(windows_frame_info->program_string.empty()); @@ -228,6 +230,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA); ASSERT_TRUE(windows_frame_info.get()); ASSERT_FALSE(windows_frame_info->allocates_base_pointer); ASSERT_FALSE(windows_frame_info->program_string.empty()); @@ -351,6 +354,7 @@ TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) ASSERT_EQ(frame.source_line_base, 0x2180); windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA); ASSERT_EQ(windows_frame_info->prolog_size, 1); frame.instruction = 0x216f; diff --git a/thirdparty/breakpad/processor/binarystream.h b/thirdparty/breakpad/processor/binarystream.h index 8cb3af989..8769c250c 100644 --- a/thirdparty/breakpad/processor/binarystream.h +++ b/thirdparty/breakpad/processor/binarystream.h @@ -77,6 +77,9 @@ class binarystream { void rewind() { stream_.seekg (0, ios::beg); stream_.seekp (0, ios::beg); + // This is to clear all the error flags, since only the EOF flag is cleared + // with seekg(). + stream_.clear(); } private: diff --git a/thirdparty/breakpad/processor/disassembler_x86.cc b/thirdparty/breakpad/processor/disassembler_x86.cc index 7e41e4bf5..45a235db5 100644 --- a/thirdparty/breakpad/processor/disassembler_x86.cc +++ b/thirdparty/breakpad/processor/disassembler_x86.cc @@ -48,6 +48,9 @@ DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode, } DisassemblerX86::~DisassemblerX86() { + if (instr_valid_) + libdis::x86_oplist_free(¤t_instr_); + libdis::x86_cleanup(); } diff --git a/thirdparty/breakpad/processor/exploitability.cc b/thirdparty/breakpad/processor/exploitability.cc index d8821d4b1..eb0b4d35d 100644 --- a/thirdparty/breakpad/processor/exploitability.cc +++ b/thirdparty/breakpad/processor/exploitability.cc @@ -75,6 +75,7 @@ Exploitability *Exploitability::ExploitabilityForPlatform( break; } case MD_OS_MAC_OS_X: + case MD_OS_IOS: case MD_OS_LINUX: case MD_OS_UNIX: case MD_OS_SOLARIS: diff --git a/thirdparty/breakpad/processor/fast_source_line_resolver.cc b/thirdparty/breakpad/processor/fast_source_line_resolver.cc index 45c1f0f0a..9224ffa2f 100644 --- a/thirdparty/breakpad/processor/fast_source_line_resolver.cc +++ b/thirdparty/breakpad/processor/fast_source_line_resolver.cc @@ -107,10 +107,15 @@ void FastSourceLineResolver::Module::LookupAddress(StackFrame *frame) const { // WFI: WindowsFrameInfo. // Returns a WFI object reading from a raw memory chunk of data WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) { - // The first 4Bytes of int data are unused. - // They corresponds to "int valid;" data member of WFI. + const WindowsFrameInfo::StackInfoTypes type = + static_cast( + *reinterpret_cast(raw)); + + // The first 8 bytes of int data are unused. + // They correspond to "StackInfoTypes type_;" and "int valid;" + // data member of WFI. const u_int32_t *para_uint32 = reinterpret_cast( - raw + sizeof(int32_t)); + raw + 2 * sizeof(int32_t)); u_int32_t prolog_size = para_uint32[0];; u_int32_t epilog_size = para_uint32[1]; @@ -122,7 +127,8 @@ WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) { bool allocates_base_pointer = (*boolean != 0); std::string program_string = boolean + 1; - return WindowsFrameInfo(prolog_size, + return WindowsFrameInfo(type, + prolog_size, epilog_size, parameter_size, saved_register_size, diff --git a/thirdparty/breakpad/processor/fast_source_line_resolver_unittest.cc b/thirdparty/breakpad/processor/fast_source_line_resolver_unittest.cc index a4a920973..d36d4ba66 100644 --- a/thirdparty/breakpad/processor/fast_source_line_resolver_unittest.cc +++ b/thirdparty/breakpad/processor/fast_source_line_resolver_unittest.cc @@ -248,6 +248,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_EQ(frame.source_line, 0); windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN); ASSERT_FALSE(windows_frame_info->allocates_base_pointer); ASSERT_TRUE(windows_frame_info->program_string.empty()); @@ -258,6 +259,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_EQ(frame.source_line, 0); windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA); ASSERT_FALSE(windows_frame_info->allocates_base_pointer); ASSERT_FALSE(windows_frame_info->program_string.empty()); @@ -380,6 +382,7 @@ TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { ASSERT_EQ(frame.source_line_base, 0x2180); windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA); ASSERT_EQ(windows_frame_info->prolog_size, 1); frame.instruction = 0x216f; diff --git a/thirdparty/breakpad/processor/minidump.cc b/thirdparty/breakpad/processor/minidump.cc index 331ebd9e0..3ca52ff74 100644 --- a/thirdparty/breakpad/processor/minidump.cc +++ b/thirdparty/breakpad/processor/minidump.cc @@ -1758,6 +1758,7 @@ string MinidumpModule::code_identifier() const { } case MD_OS_MAC_OS_X: + case MD_OS_IOS: case MD_OS_SOLARIS: case MD_OS_LINUX: { // TODO(mmentovai): support uuid extension if present, otherwise fall @@ -2482,7 +2483,7 @@ const MinidumpModule* MinidumpModuleList::GetMainModule() const { // The main code module is the first one present in a minidump file's // MDRawModuleList. - return GetModuleAtSequence(0); + return GetModuleAtIndex(0); } @@ -3095,6 +3096,10 @@ string MinidumpSystemInfo::GetOS() { os = "mac"; break; + case MD_OS_IOS: + os = "ios"; + break; + case MD_OS_LINUX: os = "linux"; break; diff --git a/thirdparty/breakpad/processor/minidump_processor.cc b/thirdparty/breakpad/processor/minidump_processor.cc index a74db3273..40b287354 100644 --- a/thirdparty/breakpad/processor/minidump_processor.cc +++ b/thirdparty/breakpad/processor/minidump_processor.cc @@ -386,6 +386,11 @@ bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) { break; } + case MD_OS_IOS: { + info->os = "iOS"; + break; + } + case MD_OS_LINUX: { info->os = "Linux"; break; @@ -451,7 +456,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { return reason; switch (raw_system_info->platform_id) { - case MD_OS_MAC_OS_X: { + case MD_OS_MAC_OS_X: + case MD_OS_IOS: { char flags_string[11]; snprintf(flags_string, sizeof(flags_string), "0x%08x", exception_flags); switch (exception_code) { @@ -637,6 +643,12 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { case MD_EXCEPTION_MAC_SOFTWARE: reason = "EXC_SOFTWARE / "; switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_ABORT: + reason.append("SIGABRT"); + break; + case MD_EXCEPTION_CODE_MAC_NS_EXCEPTION: + reason.append("UNCAUGHT_NS_EXCEPTION"); + break; // These are ppc only but shouldn't be a problem as they're // unused on x86 case MD_EXCEPTION_CODE_MAC_PPC_TRAP: diff --git a/thirdparty/breakpad/processor/module_comparer.cc b/thirdparty/breakpad/processor/module_comparer.cc index 837d854bf..09b85e032 100644 --- a/thirdparty/breakpad/processor/module_comparer.cc +++ b/thirdparty/breakpad/processor/module_comparer.cc @@ -240,6 +240,7 @@ bool ModuleComparer::ComparePubSymbol(const BasicPubSymbol* basic_ps, bool ModuleComparer::CompareWFI(const WindowsFrameInfo& wfi1, const WindowsFrameInfo& wfi2) const { + ASSERT_TRUE(wfi1.type_ == wfi2.type_); ASSERT_TRUE(wfi1.valid == wfi2.valid); ASSERT_TRUE(wfi1.prolog_size == wfi2.prolog_size); ASSERT_TRUE(wfi1.epilog_size == wfi2.epilog_size); diff --git a/thirdparty/breakpad/processor/postfix_evaluator-inl.h b/thirdparty/breakpad/processor/postfix_evaluator-inl.h index b24f092cb..d7dbeac20 100644 --- a/thirdparty/breakpad/processor/postfix_evaluator-inl.h +++ b/thirdparty/breakpad/processor/postfix_evaluator-inl.h @@ -66,6 +66,141 @@ class AutoStackClearer { }; +template +bool PostfixEvaluator::EvaluateToken( + const string &token, + const string &expression, + DictionaryValidityType *assigned) { + // There are enough binary operations that do exactly the same thing + // (other than the specific operation, of course) that it makes sense + // to share as much code as possible. + enum BinaryOperation { + BINARY_OP_NONE = 0, + BINARY_OP_ADD, + BINARY_OP_SUBTRACT, + BINARY_OP_MULTIPLY, + BINARY_OP_DIVIDE_QUOTIENT, + BINARY_OP_DIVIDE_MODULUS, + BINARY_OP_ALIGN + }; + + BinaryOperation operation = BINARY_OP_NONE; + if (token == "+") + operation = BINARY_OP_ADD; + else if (token == "-") + operation = BINARY_OP_SUBTRACT; + else if (token == "*") + operation = BINARY_OP_MULTIPLY; + else if (token == "/") + operation = BINARY_OP_DIVIDE_QUOTIENT; + else if (token == "%") + operation = BINARY_OP_DIVIDE_MODULUS; + else if (token == "@") + operation = BINARY_OP_ALIGN; + + if (operation != BINARY_OP_NONE) { + // Get the operands. + ValueType operand1 = ValueType(); + ValueType operand2 = ValueType(); + if (!PopValues(&operand1, &operand2)) { + BPLOG(ERROR) << "Could not PopValues to get two values for binary " + "operation " << token << ": " << expression; + return false; + } + + // Perform the operation. + ValueType result; + switch (operation) { + case BINARY_OP_ADD: + result = operand1 + operand2; + break; + case BINARY_OP_SUBTRACT: + result = operand1 - operand2; + break; + case BINARY_OP_MULTIPLY: + result = operand1 * operand2; + break; + case BINARY_OP_DIVIDE_QUOTIENT: + result = operand1 / operand2; + break; + case BINARY_OP_DIVIDE_MODULUS: + result = operand1 % operand2; + break; + case BINARY_OP_ALIGN: + result = + operand1 & (static_cast(-1) ^ (operand2 - 1)); + break; + case BINARY_OP_NONE: + // This will not happen, but compilers will want a default or + // BINARY_OP_NONE case. + BPLOG(ERROR) << "Not reached!"; + return false; + break; + } + + // Save the result. + PushValue(result); + } else if (token == "^") { + // ^ for unary dereference. Can't dereference without memory. + if (!memory_) { + BPLOG(ERROR) << "Attempt to dereference without memory: " << + expression; + return false; + } + + ValueType address; + if (!PopValue(&address)) { + BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << + expression; + return false; + } + + ValueType value; + if (!memory_->GetMemoryAtAddress(address, &value)) { + BPLOG(ERROR) << "Could not dereference memory at address " << + HexString(address) << ": " << expression; + return false; + } + + PushValue(value); + } else if (token == "=") { + // = for assignment. + ValueType value; + if (!PopValue(&value)) { + BPLOG(INFO) << "Could not PopValue to get value to assign: " << + expression; + return false; + } + + // Assignment is only meaningful when assigning into an identifier. + // The identifier must name a variable, not a constant. Variables + // begin with '$'. + string identifier; + if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { + BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " + "identifier is needed to assign " << + HexString(value) << ": " << expression; + return false; + } + if (identifier.empty() || identifier[0] != '$') { + BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << + identifier << ": " << expression; + return false; + } + + (*dictionary_)[identifier] = value; + if (assigned) + (*assigned)[identifier] = true; + } else { + // The token is not an operator, it's a literal value or an identifier. + // Push it onto the stack as-is. Use push_back instead of PushValue + // because PushValue pushes ValueType as a string, but token is already + // a string. + stack_.push_back(token); + } + return true; +} + template bool PostfixEvaluator::EvaluateInternal( const string &expression, @@ -74,125 +209,21 @@ bool PostfixEvaluator::EvaluateInternal( istringstream stream(expression); string token; while (stream >> token) { - // There are enough binary operations that do exactly the same thing - // (other than the specific operation, of course) that it makes sense - // to share as much code as possible. - enum BinaryOperation { - BINARY_OP_NONE = 0, - BINARY_OP_ADD, - BINARY_OP_SUBTRACT, - BINARY_OP_MULTIPLY, - BINARY_OP_DIVIDE_QUOTIENT, - BINARY_OP_DIVIDE_MODULUS - }; - - BinaryOperation operation = BINARY_OP_NONE; - if (token == "+") - operation = BINARY_OP_ADD; - else if (token == "-") - operation = BINARY_OP_SUBTRACT; - else if (token == "*") - operation = BINARY_OP_MULTIPLY; - else if (token == "/") - operation = BINARY_OP_DIVIDE_QUOTIENT; - else if (token == "%") - operation = BINARY_OP_DIVIDE_MODULUS; - - if (operation != BINARY_OP_NONE) { - // Get the operands. - ValueType operand1 = ValueType(); - ValueType operand2 = ValueType(); - if (!PopValues(&operand1, &operand2)) { - BPLOG(ERROR) << "Could not PopValues to get two values for binary " - "operation " << token << ": " << expression; + // Normally, tokens are whitespace-separated, but occasionally, the + // assignment operator is smashed up against the next token, i.e. + // $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ = + // This has been observed in program strings produced by MSVS 2010 in LTO + // mode. + if (token.size() > 1 && token[0] == '=') { + if (!EvaluateToken("=", expression, assigned)) { return false; } - // Perform the operation. - ValueType result; - switch (operation) { - case BINARY_OP_ADD: - result = operand1 + operand2; - break; - case BINARY_OP_SUBTRACT: - result = operand1 - operand2; - break; - case BINARY_OP_MULTIPLY: - result = operand1 * operand2; - break; - case BINARY_OP_DIVIDE_QUOTIENT: - result = operand1 / operand2; - break; - case BINARY_OP_DIVIDE_MODULUS: - result = operand1 % operand2; - break; - case BINARY_OP_NONE: - // This will not happen, but compilers will want a default or - // BINARY_OP_NONE case. - BPLOG(ERROR) << "Not reached!"; - return false; - break; - } - - // Save the result. - PushValue(result); - } else if (token == "^") { - // ^ for unary dereference. Can't dereference without memory. - if (!memory_) { - BPLOG(ERROR) << "Attempt to dereference without memory: " << - expression; + if (!EvaluateToken(token.substr(1), expression, assigned)) { return false; } - - ValueType address; - if (!PopValue(&address)) { - BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << - expression; - return false; - } - - ValueType value; - if (!memory_->GetMemoryAtAddress(address, &value)) { - BPLOG(ERROR) << "Could not dereference memory at address " << - HexString(address) << ": " << expression; - return false; - } - - PushValue(value); - } else if (token == "=") { - // = for assignment. - ValueType value; - if (!PopValue(&value)) { - BPLOG(INFO) << "Could not PopValue to get value to assign: " << - expression; - return false; - } - - // Assignment is only meaningful when assigning into an identifier. - // The identifier must name a variable, not a constant. Variables - // begin with '$'. - string identifier; - if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { - BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " - "identifier is needed to assign " << - HexString(value) << ": " << expression; - return false; - } - if (identifier.empty() || identifier[0] != '$') { - BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << - identifier << ": " << expression; - return false; - } - - (*dictionary_)[identifier] = value; - if (assigned) - (*assigned)[identifier] = true; - } else { - // The token is not an operator, it's a literal value or an identifier. - // Push it onto the stack as-is. Use push_back instead of PushValue - // because PushValue pushes ValueType as a string, but token is already - // a string. - stack_.push_back(token); + } else if (!EvaluateToken(token, expression, assigned)) { + return false; } } diff --git a/thirdparty/breakpad/processor/postfix_evaluator.h b/thirdparty/breakpad/processor/postfix_evaluator.h index 248075514..eb53b1ebc 100644 --- a/thirdparty/breakpad/processor/postfix_evaluator.h +++ b/thirdparty/breakpad/processor/postfix_evaluator.h @@ -44,10 +44,12 @@ // either literal values suitable for ValueType, or constants or variables, // which reference the dictionary. The supported binary operators are + // (addition), - (subtraction), * (multiplication), / (quotient of division), -// and % (modulus of division). The unary ^ (dereference) operator is also -// provided. These operators allow any operand to be either a literal -// value, constant, or variable. Assignment (=) of any type of operand into -// a variable is also supported. +// % (modulus of division), and @ (data alignment). The alignment operator (@) +// accepts a value and an alignment size, and produces a result that is a +// multiple of the alignment size by truncating the input value. +// The unary ^ (dereference) operator is also provided. These operators +// allow any operand to be either a literal value, constant, or variable. +// Assignment (=) of any type of operand into a variable is also supported. // // The dictionary is provided as a map with string keys. Keys beginning // with the '$' character are treated as variables. All other keys are @@ -151,6 +153,10 @@ class PostfixEvaluator { bool EvaluateInternal(const string &expression, DictionaryValidityType *assigned); + bool EvaluateToken(const string &token, + const string &expression, + DictionaryValidityType *assigned); + // The dictionary mapping constant and variable identifiers (strings) to // values. Keys beginning with '$' are treated as variable names, and // PostfixEvaluator is free to create and modify these keys. Weak pointer. diff --git a/thirdparty/breakpad/processor/postfix_evaluator_unittest.cc b/thirdparty/breakpad/processor/postfix_evaluator_unittest.cc index 0c2d8ecee..f6cbcf08e 100644 --- a/thirdparty/breakpad/processor/postfix_evaluator_unittest.cc +++ b/thirdparty/breakpad/processor/postfix_evaluator_unittest.cc @@ -148,7 +148,9 @@ static bool RunTests() { { "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3 { "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1 { "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3 - { "$rDeref 9 ^ =", true } // $rDeref = ^9 = 10 (FakeMemoryRegion) + { "$rDeref 9 ^ =", true }, // $rDeref = ^9 = 10 (FakeMemoryRegion) + { "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8 + { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization }; map validate_data_0; validate_data_0["$rAdd"] = 8; @@ -158,6 +160,9 @@ static bool RunTests() { validate_data_0["$rDivQ"] = 1; validate_data_0["$rDivM"] = 3; validate_data_0["$rDeref"] = 10; + validate_data_0["$rAlign"] = 32; + validate_data_0["$rAdd3"] = 4; + validate_data_0["$rMul2"] = 54; // The second test set simulates a couple of MSVC program strings. // The data is fudged a little bit because the tests use FakeMemoryRegion diff --git a/thirdparty/breakpad/processor/simple_serializer-inl.h b/thirdparty/breakpad/processor/simple_serializer-inl.h index a6f549612..1faf5ef53 100644 --- a/thirdparty/breakpad/processor/simple_serializer-inl.h +++ b/thirdparty/breakpad/processor/simple_serializer-inl.h @@ -132,6 +132,7 @@ class SimpleSerializer { public: static size_t SizeOf(const WindowsFrameInfo &wfi) { unsigned int size = 0; + size += sizeof(int32_t); // wfi.type_ size += SimpleSerializer::SizeOf(wfi.valid); size += SimpleSerializer::SizeOf(wfi.prolog_size); size += SimpleSerializer::SizeOf(wfi.epilog_size); @@ -144,6 +145,8 @@ class SimpleSerializer { return size; } static char *Write(const WindowsFrameInfo &wfi, char *dest) { + dest = SimpleSerializer::Write( + static_cast(wfi.type_), dest); dest = SimpleSerializer::Write(wfi.valid, dest); dest = SimpleSerializer::Write(wfi.prolog_size, dest); dest = SimpleSerializer::Write(wfi.epilog_size, dest); diff --git a/thirdparty/breakpad/processor/source_line_resolver_base.cc b/thirdparty/breakpad/processor/source_line_resolver_base.cc index b8362dcf1..49088c895 100644 --- a/thirdparty/breakpad/processor/source_line_resolver_base.cc +++ b/thirdparty/breakpad/processor/source_line_resolver_base.cc @@ -88,7 +88,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data, int error_code = stat(map_file.c_str(), &buf); if (error_code == -1) { string error_string; - int error_code = ErrnoString(&error_string); + error_code = ErrnoString(&error_string); BPLOG(ERROR) << "Could not open " << map_file << ", error " << error_code << ": " << error_string; return false; @@ -110,7 +110,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data, FILE *f = fopen(map_file.c_str(), "rt"); if (!f) { string error_string; - int error_code = ErrnoString(&error_string); + error_code = ErrnoString(&error_string); BPLOG(ERROR) << "Could not open " << map_file << ", error " << error_code << ": " << error_string; delete [] (*symbol_data); @@ -126,7 +126,7 @@ bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data, if (items_read != file_size) { string error_string; - int error_code = ErrnoString(&error_string); + error_code = ErrnoString(&error_string); BPLOG(ERROR) << "Could not slurp " << map_file << ", error " << error_code << ": " << error_string; delete [] (*symbol_data); @@ -240,11 +240,11 @@ void SourceLineResolverBase::UnloadModule(const CodeModule *code_module) { if (!code_module) return; - ModuleMap::iterator iter = modules_->find(code_module->code_file()); - if (iter != modules_->end()) { - Module *symbol_module = iter->second; + ModuleMap::iterator mod_iter = modules_->find(code_module->code_file()); + if (mod_iter != modules_->end()) { + Module *symbol_module = mod_iter->second; delete symbol_module; - modules_->erase(iter); + modules_->erase(mod_iter); } if (ShouldDeleteMemoryBufferAfterLoadModule()) { diff --git a/thirdparty/breakpad/processor/stackwalker.cc b/thirdparty/breakpad/processor/stackwalker.cc index 9ddb60ae2..2bd333ac8 100644 --- a/thirdparty/breakpad/processor/stackwalker.cc +++ b/thirdparty/breakpad/processor/stackwalker.cc @@ -44,6 +44,7 @@ #include "google_breakpad/processor/source_line_resolver_interface.h" #include "google_breakpad/processor/stack_frame.h" #include "google_breakpad/processor/symbol_supplier.h" +#include "google_breakpad/processor/system_info.h" #include "processor/linked_ptr.h" #include "processor/logging.h" #include "processor/scoped_ptr.h" @@ -187,10 +188,13 @@ Stackwalker* Stackwalker::StackwalkerForCPU( break; case MD_CONTEXT_ARM: + int fp_register = -1; + if (system_info->os_short == "ios") + fp_register = MD_CONTEXT_ARM_REG_IOS_FP; cpu_stackwalker = new StackwalkerARM(system_info, context->GetContextARM(), - memory, modules, supplier, - resolver); + fp_register, memory, modules, + supplier, resolver); break; } diff --git a/thirdparty/breakpad/processor/stackwalker_amd64.cc b/thirdparty/breakpad/processor/stackwalker_amd64.cc index fd9ccdf37..5a9ddb198 100644 --- a/thirdparty/breakpad/processor/stackwalker_amd64.cc +++ b/thirdparty/breakpad/processor/stackwalker_amd64.cc @@ -146,18 +146,13 @@ StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan( const vector &frames) { StackFrameAMD64 *last_frame = static_cast(frames.back()); u_int64_t last_rsp = last_frame->context.rsp; - u_int64_t caller_rsp, caller_rip; - - if (!ScanForReturnAddress(last_rsp, &caller_rsp, &caller_rip)) { + u_int64_t caller_rip_address, caller_rip; + + if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip)) { // No plausible return address was found. return NULL; } - // ScanForReturnAddress found a reasonable return address. Advance - // %rsp to the location above the one where the return address was - // found. - caller_rsp += 8; - // Create a new stack frame (ownership will be transferred to the caller) // and fill it in. StackFrameAMD64 *frame = new StackFrameAMD64(); @@ -165,10 +160,34 @@ StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan( frame->trust = StackFrame::FRAME_TRUST_SCAN; frame->context = last_frame->context; frame->context.rip = caller_rip; - frame->context.rsp = caller_rsp; + // The caller's %rsp is directly underneath the return address pushed by + // the call. + frame->context.rsp = caller_rip_address + 8; frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP | StackFrameAMD64::CONTEXT_VALID_RSP; + // Other unwinders give up if they don't have an %rbp value, so see if we + // can pass some plausible value on. + if (last_frame->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) { + // Functions typically push their caller's %rbp immediately upon entry, + // and then set %rbp to point to that. So if the callee's %rbp is + // pointing to the first word below the alleged return address, presume + // that the caller's %rbp is saved there. + if (caller_rip_address - 8 == last_frame->context.rbp) { + u_int64_t caller_rbp = 0; + if (memory_->GetMemoryAtAddress(last_frame->context.rbp, &caller_rbp) && + caller_rbp > caller_rip_address) { + frame->context.rbp = caller_rbp; + frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP; + } + } else if (last_frame->context.rbp >= caller_rip_address + 8) { + // If the callee's %rbp is plausible as a value for the caller's + // %rbp, presume that the callee left it unchanged. + frame->context.rbp = last_frame->context.rbp; + frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP; + } + } + return frame; } diff --git a/thirdparty/breakpad/processor/stackwalker_amd64_unittest.cc b/thirdparty/breakpad/processor/stackwalker_amd64_unittest.cc index 0cb5fd574..a107bee88 100644 --- a/thirdparty/breakpad/processor/stackwalker_amd64_unittest.cc +++ b/thirdparty/breakpad/processor/stackwalker_amd64_unittest.cc @@ -182,7 +182,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) { stack_section.start() = 0x8000000080000000ULL; u_int64_t return_address1 = 0x50000000b0000100ULL; u_int64_t return_address2 = 0x50000000b0000900ULL; - Label frame1_sp, frame2_sp; + Label frame1_sp, frame2_sp, frame1_rbp; stack_section // frame 0 .Append(16, 0) // space @@ -198,6 +198,11 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) { .D64(0x40000000b0000000ULL) // more junk .D64(0x50000000d0000000ULL) + .Mark(&frame1_rbp) + .D64(stack_section.start()) // This is in the right place to be + // a saved rbp, but it's bogus, so + // we shouldn't report it. + .D64(return_address2) // actual return address // frame 2 .Mark(&frame2_sp) @@ -206,6 +211,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) { RegionFromSection(); raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame1_rbp.Value(); raw_context.rsp = stack_section.start().Value(); StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, @@ -222,10 +228,12 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) { StackFrameAMD64 *frame1 = static_cast(frames->at(1)); EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | - StackFrameAMD64::CONTEXT_VALID_RSP), + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), frame1->context_validity); EXPECT_EQ(return_address1, frame1->context.rip); EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); StackFrameAMD64 *frame2 = static_cast(frames->at(2)); EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust); @@ -243,7 +251,7 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { // lies within a function's bounds. stack_section.start() = 0x8000000080000000ULL; u_int64_t return_address = 0x50000000b0000110ULL; - Label frame1_sp; + Label frame1_sp, frame1_rbp; stack_section // frame 0 @@ -258,10 +266,12 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { .D64(return_address) // actual return address // frame 1 .Mark(&frame1_sp) - .Append(32, 0); // end of stack + .Append(32, 0) // end of stack + .Mark(&frame1_rbp); RegionFromSection(); raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame1_rbp.Value(); raw_context.rsp = stack_section.start().Value(); SetModuleSymbols(&module1, @@ -286,14 +296,81 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { StackFrameAMD64 *frame1 = static_cast(frames->at(1)); EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | - StackFrameAMD64::CONTEXT_VALID_RSP), + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), frame1->context_validity); EXPECT_EQ(return_address, frame1->context.rip); EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); EXPECT_EQ("echidna", frame1->function_name); EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base); } +TEST_F(GetCallerFrame, CallerPushedRBP) { + // Functions typically push their %rbp upon entry and set %rbp pointing + // there. If stackwalking finds a plausible address for the next frame's + // %rbp directly below the return address, assume that it is indeed the + // next frame's %rbp. + stack_section.start() = 0x8000000080000000ULL; + u_int64_t return_address = 0x50000000b0000110ULL; + Label frame0_rbp, frame1_sp, frame1_rbp; + + stack_section + // frame 0 + .Append(16, 0) // space + + .D64(0x40000000b0000000ULL) // junk that's not + .D64(0x50000000b0000000ULL) // a return address + + .D64(0x40000000c0001000ULL) // a couple of plausible addresses + .D64(0x50000000b000aaaaULL) // that are not within functions + + .Mark(&frame0_rbp) + .D64(frame1_rbp) // caller-pushed %rbp + .D64(return_address) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(32, 0) // body of frame1 + .Mark(&frame1_rbp); // end of stack + RegionFromSection(); + + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame0_rbp.Value(); + raw_context.rsp = stack_section.start().Value(); + + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 100 400 10 sasquatch\n"); + SetModuleSymbols(&module2, + // The calling frame's function. + "FUNC 100 400 10 yeti\n"); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(frame0_rbp.Value(), frame0->context.rbp); + EXPECT_EQ("sasquatch", frame0->function_name); + EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.rip); + EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); + EXPECT_EQ("yeti", frame1->function_name); + EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base); +} + struct CFIFixture: public StackwalkerAMD64Fixture { CFIFixture() { // Provide a bunch of STACK CFI records; we'll walk to the caller diff --git a/thirdparty/breakpad/processor/stackwalker_arm.cc b/thirdparty/breakpad/processor/stackwalker_arm.cc index 3b91caff2..0a3c522d6 100644 --- a/thirdparty/breakpad/processor/stackwalker_arm.cc +++ b/thirdparty/breakpad/processor/stackwalker_arm.cc @@ -33,7 +33,6 @@ // // Author: Mark Mentovai, Ted Mielczarek, Jim Blandy - #include "google_breakpad/processor/call_stack.h" #include "google_breakpad/processor/memory_region.h" #include "google_breakpad/processor/source_line_resolver_interface.h" @@ -48,12 +47,13 @@ namespace google_breakpad { StackwalkerARM::StackwalkerARM(const SystemInfo *system_info, const MDRawContextARM *context, + int fp_register, MemoryRegion *memory, const CodeModules *modules, SymbolSupplier *supplier, SourceLineResolverInterface *resolver) : Stackwalker(system_info, memory, modules, supplier, resolver), - context_(context), + context_(context), fp_register_(fp_register), context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { } @@ -70,7 +70,7 @@ StackFrame* StackwalkerARM::GetContextFrame() { frame->context = *context_; frame->context_validity = context_frame_validity_; frame->trust = StackFrame::FRAME_TRUST_CONTEXT; - frame->instruction = frame->context.iregs[15]; + frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC]; return frame; } @@ -125,8 +125,18 @@ StackFrameARM *StackwalkerARM::GetCallerByCFIFrameInfo( CFIFrameInfo::RegisterValueMap::iterator entry = caller_registers.find(".ra"); if (entry != caller_registers.end()) { - frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; - frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second; + if (fp_register_ == -1) { + frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second; + } else { + // The CFI updated the link register and not the program counter. + // Handle getting the program counter from the link register. + frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; + frame->context_validity |= StackFrameARM::CONTEXT_VALID_LR; + frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = entry->second; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = + last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR]; + } } } // If the CFI doesn't recover the SP explicitly, then use .cfa. @@ -154,7 +164,7 @@ StackFrameARM *StackwalkerARM::GetCallerByStackScan( StackFrameARM *last_frame = static_cast(frames.back()); u_int32_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]; u_int32_t caller_sp, caller_pc; - + if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc)) { // No plausible return address was found. return NULL; @@ -179,6 +189,52 @@ StackFrameARM *StackwalkerARM::GetCallerByStackScan( return frame; } +StackFrameARM *StackwalkerARM::GetCallerByFramePointer( + const vector &frames) { + StackFrameARM *last_frame = static_cast(frames.back()); + + if (!(last_frame->context_validity & + StackFrameARM::RegisterValidFlag(fp_register_))) { + return NULL; + } + + u_int32_t last_fp = last_frame->context.iregs[fp_register_]; + + u_int32_t caller_fp = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) { + BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x" + << std::hex << last_fp; + return NULL; + } + + u_int32_t caller_lr = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 4, &caller_lr)) { + BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 4: 0x" + << std::hex << (last_fp + 4); + return NULL; + } + + u_int32_t caller_sp = last_fp ? last_fp + 8 : + last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameARM *frame = new StackFrameARM(); + + frame->trust = StackFrame::FRAME_TRUST_FP; + frame->context = last_frame->context; + frame->context.iregs[fp_register_] = caller_fp; + frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = + last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR]; + frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = caller_lr; + frame->context_validity = StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(fp_register_) | + StackFrameARM::CONTEXT_VALID_SP; + return frame; +} + StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { if (!memory_ || !stack) { BPLOG(ERROR) << "Can't get caller frame without memory or stack"; @@ -196,10 +252,13 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get())); // If CFI failed, or there wasn't CFI available, fall back - // to stack scanning. - if (!frame.get()) { + // to frame pointer, if this is configured. + if (fp_register_ >= 0 && !frame.get()) + frame.reset(GetCallerByFramePointer(frames)); + + // If everuthing failed, fall back to stack scanning. + if (!frame.get()) frame.reset(GetCallerByStackScan(frames)); - } // If nothing worked, tell the caller. if (!frame.get()) @@ -225,7 +284,7 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { // match up with the line that contains the function call. Callers that // require the exact return address value may access // frame->context.iregs[MD_CONTEXT_ARM_REG_PC]. - frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1; + frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 2; return frame.release(); } diff --git a/thirdparty/breakpad/processor/stackwalker_arm.h b/thirdparty/breakpad/processor/stackwalker_arm.h index 830579b20..24fc60ddb 100644 --- a/thirdparty/breakpad/processor/stackwalker_arm.h +++ b/thirdparty/breakpad/processor/stackwalker_arm.h @@ -40,7 +40,6 @@ #ifndef PROCESSOR_STACKWALKER_ARM_H__ #define PROCESSOR_STACKWALKER_ARM_H__ - #include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/common/minidump_format.h" #include "google_breakpad/processor/stackwalker.h" @@ -57,6 +56,7 @@ class StackwalkerARM : public Stackwalker { // to the base Stackwalker constructor. StackwalkerARM(const SystemInfo *system_info, const MDRawContextARM *context, + int fp_register, MemoryRegion *memory, const CodeModules *modules, SymbolSupplier *supplier, @@ -78,14 +78,22 @@ class StackwalkerARM : public Stackwalker { StackFrameARM *GetCallerByCFIFrameInfo(const vector &frames, CFIFrameInfo *cfi_frame_info); + // Use the frame pointer. The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameARM *GetCallerByFramePointer(const vector &frames); + // Scan the stack for plausible return addresses. The caller takes ownership - // of the returned frame. Return NULL on failure. + // of the returned frame. Return NULL on failure. StackFrameARM *GetCallerByStackScan(const vector &frames); // Stores the CPU context corresponding to the youngest stack frame, to // be returned by GetContextFrame. const MDRawContextARM *context_; + // The register to use a as frame pointer. The value is -1 if frame pointer + // cannot be used. + int fp_register_; + // Validity mask for youngest stack frame. This is always // CONTEXT_VALID_ALL in real use; it is only changeable for the sake of // unit tests. diff --git a/thirdparty/breakpad/processor/stackwalker_arm_unittest.cc b/thirdparty/breakpad/processor/stackwalker_arm_unittest.cc index cb7ce631e..6a623c276 100644 --- a/thirdparty/breakpad/processor/stackwalker_arm_unittest.cc +++ b/thirdparty/breakpad/processor/stackwalker_arm_unittest.cc @@ -116,7 +116,7 @@ class StackwalkerARMFixture { for (size_t i = 0; i < sizeof(*raw_context); i++) reinterpret_cast(raw_context)[i] = (x += 17); } - + SystemInfo system_info; MDRawContextARM raw_context; Section stack_section; @@ -136,7 +136,7 @@ TEST_F(SanityCheck, NoResolver) { // Since we have no call frame information, and all unwinding // requires call frame information, the stack walk will end after // the first frame. - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, NULL, NULL); // This should succeed even without a resolver or supplier. ASSERT_TRUE(walker.Walk(&call_stack)); @@ -154,7 +154,7 @@ TEST_F(GetContextFrame, Simple) { // Since we have no call frame information, and all unwinding // requires call frame information, the stack walk will end after // the first frame. - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, &supplier, &resolver); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); @@ -201,7 +201,7 @@ TEST_F(GetCallerFrame, ScanWithoutSymbols) { raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, &supplier, &resolver); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); @@ -264,7 +264,7 @@ TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { // The calling frame's function. "FUNC 100 400 10 marsupial\n"); - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, &supplier, &resolver); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); @@ -374,8 +374,8 @@ struct CFIFixture: public StackwalkerARMFixture { RegionFromSection(); raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, - &supplier, &resolver); + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, + &modules, &supplier, &resolver); walker.SetContextFrameValidity(context_frame_validity); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); @@ -417,7 +417,7 @@ struct CFIFixture: public StackwalkerARMFixture { EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], - frame1->instruction + 1); + frame1->instruction + 2); EXPECT_EQ("epictetus", frame1->function_name); } @@ -564,9 +564,9 @@ TEST_F(CFI, At4006) { // move in the wrong direction. TEST_F(CFI, RejectBackwards) { raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000; - raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510; - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, &supplier, &resolver); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); @@ -576,11 +576,189 @@ TEST_F(CFI, RejectBackwards) { // Check that we reject rules whose expressions' evaluation fails. TEST_F(CFI, RejectBadExpressions) { raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000; - raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; - StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, &supplier, &resolver); ASSERT_TRUE(walker.Walk(&call_stack)); frames = call_stack.frames(); ASSERT_EQ(1U, frames->size()); } +class StackwalkerARMFixtureIOS : public StackwalkerARMFixture { + public: + StackwalkerARMFixtureIOS() { + system_info.os = "iOS"; + system_info.os_short = "ios"; + } +}; + +class GetFramesByFramePointer: public StackwalkerARMFixtureIOS, public Test { }; + +TEST_F(GetFramesByFramePointer, OnlyFramePointer) { + stack_section.start() = 0x80000000; + u_int32_t return_address1 = 0x50000100; + u_int32_t return_address2 = 0x50000900; + Label frame1_sp, frame2_sp; + Label frame1_fp, frame2_fp; + stack_section + // frame 0 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame1_fp) // Next fp will point to the next value. + .D32(frame2_fp) // Save current frame pointer. + .D32(return_address2) // Save current link register. + .Mark(&frame1_sp) + + // frame 1 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame2_fp) + .D32(0) + .D32(0) + .Mark(&frame2_sp) + + // frame 2 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000); // a return address. + RegionFromSection(); + + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1; + raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP, + &stack_region, &modules, &supplier, &resolver); + + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(frame2_fp.Value(), + frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); + + StackFrameARM *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); +} + +TEST_F(GetFramesByFramePointer, FramePointerAndCFI) { + // Provide the standatd STACK CFI records that is obtained when exmining an + // executable produced by XCode. + SetModuleSymbols(&module1, + // Adding a function in CFI. + "FUNC 4000 1000 10 enchiridion\n" + + "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: lr\n" + "STACK CFI 4001 .cfa: sp 8 + .ra: .cfa -4 + ^" + " r7: .cfa -8 + ^\n" + "STACK CFI 4002 .cfa: r7 8 +\n" + ); + + stack_section.start() = 0x80000000; + u_int32_t return_address1 = 0x40004010; + u_int32_t return_address2 = 0x50000900; + Label frame1_sp, frame2_sp; + Label frame1_fp, frame2_fp; + stack_section + // frame 0 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame1_fp) // Next fp will point to the next value. + .D32(frame2_fp) // Save current frame pointer. + .D32(return_address2) // Save current link register. + .Mark(&frame1_sp) + + // frame 1 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame2_fp) + .D32(0) + .D32(0) + .Mark(&frame2_sp) + + // frame 2 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000); // a return address. + RegionFromSection(); + + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x50000400; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1; + raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP, + &stack_region, &modules, &supplier, &resolver); + + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(frame2_fp.Value(), + frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); + EXPECT_EQ("enchiridion", frame1->function_name); + EXPECT_EQ(0x40004000U, frame1->function_base); + + + StackFrameARM *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); +} diff --git a/thirdparty/breakpad/processor/stackwalker_x86.cc b/thirdparty/breakpad/processor/stackwalker_x86.cc index 2ee37bfb6..ba3b83960 100644 --- a/thirdparty/breakpad/processor/stackwalker_x86.cc +++ b/thirdparty/breakpad/processor/stackwalker_x86.cc @@ -203,10 +203,37 @@ StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo( dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size; dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size; dictionary[".cbLocals"] = last_frame_info->local_size; - dictionary[".raSearchStart"] = last_frame->context.esp + - last_frame_callee_parameter_size + - last_frame_info->local_size + - last_frame_info->saved_register_size; + + u_int32_t raSearchStart = last_frame->context.esp + + last_frame_callee_parameter_size + + last_frame_info->local_size + + last_frame_info->saved_register_size; + + u_int32_t raSearchStartOld = raSearchStart; + u_int32_t found = 0; // dummy value + // Scan up to three words above the calculated search value, in case + // the stack was aligned to a quadword boundary. + if (ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3) && + last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT && + last_frame->windows_frame_info != NULL && + last_frame_info->type_ == WindowsFrameInfo::STACK_INFO_FPO && + raSearchStartOld == raSearchStart && + found == last_frame->context.eip) { + // The context frame represents an FPO-optimized Windows system call. + // On the top of the stack we have a pointer to the current instruction. + // This means that the callee has returned but the return address is still + // on the top of the stack which is very atypical situaltion. + // Skip one slot from the stack and do another scan in order to get the + // actual return address. + raSearchStart += 4; + ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3); + } + + // The difference between raSearch and raSearchStart is unknown, + // but making them the same seems to work well in practice. + dictionary[".raSearchStart"] = raSearchStart; + dictionary[".raSearch"] = raSearchStart; + dictionary[".cbParams"] = last_frame_info->parameter_size; // Decide what type of program string to use. The program string is in diff --git a/thirdparty/breakpad/processor/stackwalker_x86_unittest.cc b/thirdparty/breakpad/processor/stackwalker_x86_unittest.cc index aa6b6310e..e57311e89 100644 --- a/thirdparty/breakpad/processor/stackwalker_x86_unittest.cc +++ b/thirdparty/breakpad/processor/stackwalker_x86_unittest.cc @@ -69,7 +69,11 @@ class StackwalkerX86Fixture { // Give the two modules reasonable standard locations and names // for tests to play with. module1(0x40000000, 0x10000, "module1", "version1"), - module2(0x50000000, 0x10000, "module2", "version2") { + module2(0x50000000, 0x10000, "module2", "version2"), + module3(0x771d0000, 0x180000, "module3", "version3"), + module4(0x75f90000, 0x46000, "module4", "version4"), + module5(0x75730000, 0x110000, "module5", "version5"), + module6(0x647f0000, 0x1ba8000, "module6", "version6") { // Identify the system as a Linux system. system_info.os = "Linux"; system_info.os_short = "linux"; @@ -83,6 +87,10 @@ class StackwalkerX86Fixture { // Create some modules with some stock debugging information. modules.Add(&module1); modules.Add(&module2); + modules.Add(&module3); + modules.Add(&module4); + modules.Add(&module5); + modules.Add(&module6); // By default, none of the modules have symbol info; call // SetModuleSymbols to override this. @@ -122,6 +130,10 @@ class StackwalkerX86Fixture { MockMemoryRegion stack_region; MockCodeModule module1; MockCodeModule module2; + MockCodeModule module3; + MockCodeModule module4; + MockCodeModule module5; + MockCodeModule module6; MockCodeModules modules; MockSymbolSupplier supplier; BasicSourceLineResolver resolver; @@ -196,24 +208,28 @@ TEST_F(GetCallerFrame, Traditional) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x4000c7a5U, frame0->instruction); - EXPECT_EQ(0x4000c7a5U, frame0->context.eip); - EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); - EXPECT_EQ(NULL, frame0->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000c7a5U, frame0->instruction); + EXPECT_EQ(0x4000c7a5U, frame0->context.eip); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x40008679U, frame1->instruction + 1); - EXPECT_EQ(0x40008679U, frame1->context.eip); - EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40008679U, frame1->instruction + 1); + EXPECT_EQ(0x40008679U, frame1->context.eip); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } } // Walk a traditional frame, but use a bogus %ebp value, forcing a scan @@ -247,30 +263,34 @@ TEST_F(GetCallerFrame, TraditionalScan) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x4000f49dU, frame0->instruction); - EXPECT_EQ(0x4000f49dU, frame0->context.eip); - EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); - EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); - EXPECT_EQ(NULL, frame0->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000f49dU, frame0->instruction); + EXPECT_EQ(0x4000f49dU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); - // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the - // walker does not actually fetch the EBP after a scan (forcing the - // next frame to be scanned as well). But let's grandfather the existing - // behavior in for now. - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x4000129dU, frame1->instruction + 1); - EXPECT_EQ(0x4000129dU, frame1->context.eip); - EXPECT_EQ(0x80000014U, frame1->context.esp); - EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000129dU, frame1->instruction + 1); + EXPECT_EQ(0x4000129dU, frame1->context.eip); + EXPECT_EQ(0x80000014U, frame1->context.esp); + EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } } // Force scanning for a return address a long way down the stack @@ -304,30 +324,34 @@ TEST_F(GetCallerFrame, TraditionalScanLongWay) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x4000f49dU, frame0->instruction); - EXPECT_EQ(0x4000f49dU, frame0->context.eip); - EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); - EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); - EXPECT_EQ(NULL, frame0->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000f49dU, frame0->instruction); + EXPECT_EQ(0x4000f49dU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); - // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the - // walker does not actually fetch the EBP after a scan (forcing the - // next frame to be scanned as well). But let's grandfather the existing - // behavior in for now. - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x4000129dU, frame1->instruction + 1); - EXPECT_EQ(0x4000129dU, frame1->context.eip); - EXPECT_EQ(0x80000064U, frame1->context.esp); - EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000129dU, frame1->instruction + 1); + EXPECT_EQ(0x4000129dU, frame1->context.eip); + EXPECT_EQ(0x80000064U, frame1->context.esp); + EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } } // Use Windows frame data (a "STACK WIN 4" record, from a @@ -371,32 +395,101 @@ TEST_F(GetCallerFrame, WindowsFrameData) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x4000aa85U, frame0->instruction); - EXPECT_EQ(0x4000aa85U, frame0->context.eip); - EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); - EXPECT_EQ(0xf052c1deU, frame0->context.ebp); - EXPECT_TRUE(frame0->windows_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000aa85U, frame0->instruction); + EXPECT_EQ(0x4000aa85U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xf052c1deU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP - | StackFrameX86::CONTEXT_VALID_EBX - | StackFrameX86::CONTEXT_VALID_ESI - | StackFrameX86::CONTEXT_VALID_EDI), - frame1->context_validity); - EXPECT_EQ(0x40001350U, frame1->instruction + 1); - EXPECT_EQ(0x40001350U, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); - EXPECT_EQ(0x9068a878U, frame1->context.ebx); - EXPECT_EQ(0xa7120d1aU, frame1->context.esi); - EXPECT_EQ(0x630891beU, frame1->context.edi); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX + | StackFrameX86::CONTEXT_VALID_ESI + | StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(0x40001350U, frame1->instruction + 1); + EXPECT_EQ(0x40001350U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(0x9068a878U, frame1->context.ebx); + EXPECT_EQ(0xa7120d1aU, frame1->context.esi); + EXPECT_EQ(0x630891beU, frame1->context.edi); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame where the stack +// is aligned and we must search +TEST_F(GetCallerFrame, WindowsFrameDataAligned) { + SetModuleSymbols(&module1, + "STACK WIN 4 aa85 176 0 0 4 4 8 0 1" + " $T1 .raSearch =" + " $T0 $T1 4 - 8 @ =" + " $ebp $T1 4 - ^ =" + " $eip $T1 ^ =" + " $esp $T1 4 + ="); + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .D32(0x0ffa0ffa) // unused saved register + .D32(0xdeaddead) // locals + .D32(0xbeefbeef) + .D32(0) // 8-byte alignment + .D32(frame1_ebp) + .D32(0x5000129d) // return address + // frame 1 + .Mark(&frame1_esp) + .D32(0x1) // parameter + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000aa85; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0xf052c1de; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000aa85U, frame0->instruction); + EXPECT_EQ(0x4000aa85U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xf052c1deU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + } + + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000129dU, frame1->instruction + 1); + EXPECT_EQ(0x5000129dU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } } // Use Windows frame data (a "STACK WIN 4" record, from a @@ -455,56 +548,66 @@ TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) { frames = call_stack.frames(); ASSERT_EQ(3U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x40001004U, frame0->instruction); - EXPECT_EQ(0x40001004U, frame0->context.eip); - EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); - EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); - EXPECT_EQ(&module1, frame0->module); - EXPECT_EQ("module1::wheedle", frame0->function_name); - EXPECT_EQ(0x40001000U, frame0->function_base); - // The FUNC record for module1::wheedle should have produced a - // WindowsFrameInfo structure with only the parameter size valid. - ASSERT_TRUE(frame0->windows_frame_info != NULL); - EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, - frame0->windows_frame_info->valid); - EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40001004U, frame0->instruction); + EXPECT_EQ(0x40001004U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::wheedle", frame0->function_name); + EXPECT_EQ(0x40001000U, frame0->function_base); + // The FUNC record for module1::wheedle should have produced a + // WindowsFrameInfo structure with only the parameter size valid. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_UNKNOWN, + frame0->windows_frame_info->type_); + EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x5000aa95U, frame1->instruction + 1); - EXPECT_EQ(0x5000aa95U, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_EQ(0x6fa902e0U, frame1->context.ebp); - EXPECT_EQ(&module2, frame1->module); - EXPECT_EQ("module2::whine", frame1->function_name); - EXPECT_EQ(0x5000aa85U, frame1->function_base); - ASSERT_TRUE(frame1->windows_frame_info != NULL); - EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); - // This should not see the 0xbeef parameter size from the FUNC - // record, but should instead see the STACK WIN record. - EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000aa95U, frame1->instruction + 1); + EXPECT_EQ(0x5000aa95U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(0x6fa902e0U, frame1->context.ebp); + EXPECT_EQ(&module2, frame1->module); + EXPECT_EQ("module2::whine", frame1->function_name); + EXPECT_EQ(0x5000aa85U, frame1->function_base); + ASSERT_TRUE(frame1->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, + frame1->windows_frame_info->type_); + // This should not see the 0xbeef parameter size from the FUNC + // record, but should instead see the STACK WIN record. + EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size); + } - StackFrameX86 *frame2 = static_cast(frames->at(2)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP - | StackFrameX86::CONTEXT_VALID_EBX), - frame2->context_validity); - EXPECT_EQ(0x2a179e38U, frame2->instruction + 1); - EXPECT_EQ(0x2a179e38U, frame2->context.eip); - EXPECT_EQ(frame2_esp.Value(), frame2->context.esp); - EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp); - EXPECT_EQ(0x2558c7f3U, frame2->context.ebx); - EXPECT_EQ(NULL, frame2->module); - EXPECT_EQ(NULL, frame2->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX), + frame2->context_validity); + EXPECT_EQ(0x2a179e38U, frame2->instruction + 1); + EXPECT_EQ(0x2a179e38U, frame2->context.eip); + EXPECT_EQ(frame2_esp.Value(), frame2->context.esp); + EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp); + EXPECT_EQ(0x2558c7f3U, frame2->context.ebx); + EXPECT_EQ(NULL, frame2->module); + EXPECT_EQ(NULL, frame2->windows_frame_info); + } } // Use Windows frame data (a "STACK WIN 4" record, from a @@ -539,29 +642,33 @@ TEST_F(GetCallerFrame, WindowsFrameDataScan) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x40000c9cU, frame0->instruction); - EXPECT_EQ(0x40000c9cU, frame0->context.eip); - EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); - EXPECT_EQ(0x2ae314cdU, frame0->context.ebp); - EXPECT_TRUE(frame0->windows_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000c9cU, frame0->instruction); + EXPECT_EQ(0x40000c9cU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0x2ae314cdU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); - // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker - // does not actually fetch the EBP after a scan (forcing the next frame - // to be scanned as well). But let's grandfather the existing behavior in - // for now. - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x50007ce9U, frame1->instruction + 1); - EXPECT_EQ(0x50007ce9U, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_TRUE(frame1->windows_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker + // does not actually fetch the EBP after a scan (forcing the next frame + // to be scanned as well). But let's grandfather the existing behavior in + // for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x50007ce9U, frame1->instruction + 1); + EXPECT_EQ(0x50007ce9U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); + } } // Use Windows frame data (a "STACK WIN 4" record, from a @@ -618,30 +725,34 @@ TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x40000700U, frame0->instruction); - EXPECT_EQ(0x40000700U, frame0->context.eip); - EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); - EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); - EXPECT_TRUE(frame0->windows_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000700U, frame0->instruction); + EXPECT_EQ(0x40000700U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust); - // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the - // walker does not actually fetch the EBP after a scan (forcing the - // next frame to be scanned as well). But let's grandfather the existing - // behavior in for now. - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x5000d000U, frame1->instruction + 1); - EXPECT_EQ(0x5000d000U, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); - EXPECT_TRUE(frame1->windows_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000d000U, frame1->instruction + 1); + EXPECT_EQ(0x5000d000U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); + } } // Use Windows FrameTypeFPO data to walk a stack frame for a function that @@ -681,35 +792,41 @@ TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x4000e8b8U, frame0->instruction); - EXPECT_EQ(0x4000e8b8U, frame0->context.eip); - EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); - EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller - EXPECT_EQ(&module1, frame0->module); - EXPECT_EQ("module1::discombobulated", frame0->function_name); - EXPECT_EQ(0x4000e8a8U, frame0->function_base); - // The STACK WIN record for module1::discombobulated should have - // produced a fully populated WindowsFrameInfo structure. - ASSERT_TRUE(frame0->windows_frame_info != NULL); - EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); - EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000e8b8U, frame0->instruction); + EXPECT_EQ(0x4000e8b8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::discombobulated", frame0->function_name); + EXPECT_EQ(0x4000e8a8U, frame0->function_base); + // The STACK WIN record for module1::discombobulated should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, + frame0->windows_frame_info->type_); + EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x40009b5bU, frame1->instruction + 1); - EXPECT_EQ(0x40009b5bU, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); - EXPECT_EQ(&module1, frame1->module); - EXPECT_EQ("", frame1->function_name); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40009b5bU, frame1->instruction + 1); + EXPECT_EQ(0x40009b5bU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } } // Use Windows FrameTypeFPO data to walk a stack frame for a function @@ -751,36 +868,188 @@ TEST_F(GetCallerFrame, WindowsFPOUsedEBP) { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ(0x40009ab8U, frame0->instruction); - EXPECT_EQ(0x40009ab8U, frame0->context.eip); - EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); - EXPECT_EQ(0xecbdd1a5, frame0->context.ebp); - EXPECT_EQ(&module1, frame0->module); - EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name); - EXPECT_EQ(0x40009aa8U, frame0->function_base); - // The STACK WIN record for module1::RaisedByTheAliens should have - // produced a fully populated WindowsFrameInfo structure. - ASSERT_TRUE(frame0->windows_frame_info != NULL); - EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); - EXPECT_EQ("", frame0->windows_frame_info->program_string); - EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40009ab8U, frame0->instruction); + EXPECT_EQ(0x40009ab8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(0xecbdd1a5, frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name); + EXPECT_EQ(0x40009aa8U, frame0->function_base); + // The STACK WIN record for module1::RaisedByTheAliens should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, + frame0->windows_frame_info->type_); + EXPECT_EQ("", frame0->windows_frame_info->program_string); + EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP - | StackFrameX86::CONTEXT_VALID_ESP - | StackFrameX86::CONTEXT_VALID_EBP), - frame1->context_validity); - EXPECT_EQ(0x4000debeU, frame1->instruction + 1); - EXPECT_EQ(0x4000debeU, frame1->context.eip); - EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); - EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); - EXPECT_EQ(&module1, frame1->module); - EXPECT_EQ("", frame1->function_name); - EXPECT_EQ(NULL, frame1->windows_frame_info); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000debeU, frame1->instruction + 1); + EXPECT_EQ(0x4000debeU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); + } +} + +// This is a regression unit test which covers a bug which has to do with +// FPO-optimized Windows system call stubs in the context frame. There is +// a more recent Windows system call dispatch mechanism which differs from +// the one which is being tested here. The newer system call dispatch +// mechanism creates an extra context frame (KiFastSystemCallRet). +TEST_F(GetCallerFrame, WindowsFPOSystemCall) { + SetModuleSymbols(&module3, // ntdll.dll + "PUBLIC 1f8ac c ZwWaitForSingleObject\n" + "STACK WIN 0 1f8ac 1b 0 0 c 0 0 0 0 0\n"); + SetModuleSymbols(&module4, // kernelbase.dll + "PUBLIC 109f9 c WaitForSingleObjectEx\n" + "PUBLIC 36590 0 _except_handler4\n" + "STACK WIN 4 109f9 df c 0 c c 48 0 1 $T0 $ebp = $eip " + "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " + "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n" + "STACK WIN 4 36590 154 17 0 10 0 14 0 1 $T0 $ebp = $eip " + "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 " + ".cbSavedRegs - = $P $T0 8 + .cbParams + =\n"); + SetModuleSymbols(&module5, // kernel32.dll + "PUBLIC 11136 8 WaitForSingleObject\n" + "PUBLIC 11151 c WaitForSingleObjectExImplementation\n" + "STACK WIN 4 11136 16 5 0 8 0 0 0 1 $T0 $ebp = $eip " + "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " + "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n" + "STACK WIN 4 11151 7a 5 0 c 0 0 0 1 $T0 $ebp = $eip " + "$T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " + "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =\n"); + SetModuleSymbols(&module6, // chrome.dll + "FILE 7038 some_file_name.h\n" + "FILE 839776 some_file_name.cc\n" + "FUNC 217fda 17 4 function_217fda\n" + "217fda 4 102 839776\n" + "FUNC 217ff1 a 4 function_217ff1\n" + "217ff1 0 594 7038\n" + "217ff1 a 596 7038\n" + "STACK WIN 0 217ff1 a 0 0 4 0 0 0 0 0\n"); + + Label frame0_esp, frame1_esp; + Label frame1_ebp, frame2_ebp, frame3_ebp; + stack_section.start() = 0x002ff290; + stack_section + .Mark(&frame0_esp) + .D32(0x771ef8c1) // EIP in frame 0 (system call) + .D32(0x75fa0a91) // return address of frame 0 + .Mark(&frame1_esp) + .D32(0x000017b0) // args to child + .D32(0x00000000) + .D32(0x002ff2d8) + .D32(0x88014a2e) + .D32(0x002ff364) + .D32(0x000017b0) + .D32(0x00000000) + .D32(0x00000024) + .D32(0x00000001) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x00000000) + .D32(0x9e3b9800) + .D32(0xfffffff7) + .D32(0x00000000) + .D32(0x002ff2a4) + .D32(0x64a07ff1) // random value to be confused with a return address + .D32(0x002ff8dc) + .D32(0x75fc6590) // random value to be confused with a return address + .D32(0xfdd2c6ea) + .D32(0x00000000) + .Mark(&frame1_ebp) + .D32(frame2_ebp) // Child EBP + .D32(0x75741194) // return address of frame 1 + .D32(0x000017b0) // args to child + .D32(0x0036ee80) + .D32(0x00000000) + .D32(0x65bc7d14) + .Mark(&frame2_ebp) + .D32(frame3_ebp) // Child EBP + .D32(0x75741148) // return address of frame 2 + .D32(0x000017b0) // args to child + .D32(0x0036ee80) + .D32(0x00000000) + .Mark(&frame3_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x771ef8c1; // in ntdll::ZwWaitForSingleObject + raw_context.esp = stack_section.start().Value(); + ASSERT_TRUE(raw_context.esp == frame0_esp.Value()); + raw_context.ebp = frame1_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + + ASSERT_EQ(4U, frames->size()); + + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x771ef8c1U, frame0->instruction); + EXPECT_EQ(0x771ef8c1U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(&module3, frame0->module); + EXPECT_EQ("ZwWaitForSingleObject", frame0->function_name); + // The STACK WIN record for module3!ZwWaitForSingleObject should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FPO, + frame0->windows_frame_info->type_); + EXPECT_EQ("", frame0->windows_frame_info->program_string); + EXPECT_FALSE(frame0->windows_frame_info->allocates_base_pointer); + } + + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x75fa0a91U, frame1->instruction + 1); + EXPECT_EQ(0x75fa0a91U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module4, frame1->module); + EXPECT_EQ("WaitForSingleObjectEx", frame1->function_name); + // The STACK WIN record for module4!WaitForSingleObjectEx should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame1->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); + EXPECT_EQ(WindowsFrameInfo::STACK_INFO_FRAME_DATA, + frame1->windows_frame_info->type_); + EXPECT_EQ("$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L " + "$T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", + frame1->windows_frame_info->program_string); + EXPECT_FALSE(frame1->windows_frame_info->allocates_base_pointer); + } } struct CFIFixture: public StackwalkerX86Fixture { @@ -836,32 +1105,36 @@ struct CFIFixture: public StackwalkerX86Fixture { frames = call_stack.frames(); ASSERT_EQ(2U, frames->size()); - StackFrameX86 *frame0 = static_cast(frames->at(0)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); - ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); - EXPECT_EQ("enchiridion", frame0->function_name); - EXPECT_EQ(0x40004000U, frame0->function_base); - ASSERT_TRUE(frame0->windows_frame_info != NULL); - ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, - frame0->windows_frame_info->valid); - ASSERT_TRUE(frame0->cfi_frame_info != NULL); + { // To avoid reusing locals by mistake + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + ASSERT_TRUE(frame0->windows_frame_info != NULL); + ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + ASSERT_TRUE(frame0->cfi_frame_info != NULL); + } - StackFrameX86 *frame1 = static_cast(frames->at(1)); - EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); - ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | - StackFrameX86::CONTEXT_VALID_ESP | - StackFrameX86::CONTEXT_VALID_EBP | - StackFrameX86::CONTEXT_VALID_EBX | - StackFrameX86::CONTEXT_VALID_ESI | - StackFrameX86::CONTEXT_VALID_EDI), - frame1->context_validity); - EXPECT_EQ(expected.eip, frame1->context.eip); - EXPECT_EQ(expected.esp, frame1->context.esp); - EXPECT_EQ(expected.ebp, frame1->context.ebp); - EXPECT_EQ(expected.ebx, frame1->context.ebx); - EXPECT_EQ(expected.esi, frame1->context.esi); - EXPECT_EQ(expected.edi, frame1->context.edi); - EXPECT_EQ("epictetus", frame1->function_name); + { // To avoid reusing locals by mistake + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP | + StackFrameX86::CONTEXT_VALID_EBX | + StackFrameX86::CONTEXT_VALID_ESI | + StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(expected.eip, frame1->context.eip); + EXPECT_EQ(expected.esp, frame1->context.esp); + EXPECT_EQ(expected.ebp, frame1->context.ebp); + EXPECT_EQ(expected.ebx, frame1->context.ebx); + EXPECT_EQ(expected.esi, frame1->context.esi); + EXPECT_EQ(expected.edi, frame1->context.edi); + EXPECT_EQ("epictetus", frame1->function_name); + } } // The values the stack walker should find for the caller's registers. diff --git a/thirdparty/breakpad/processor/synth_minidump_unittest.cc b/thirdparty/breakpad/processor/synth_minidump_unittest.cc index b5ae33570..1ce34ddf6 100644 --- a/thirdparty/breakpad/processor/synth_minidump_unittest.cc +++ b/thirdparty/breakpad/processor/synth_minidump_unittest.cc @@ -150,7 +150,7 @@ TEST(ContextDeathTest, X86BadFlags) { MDRawContextX86 raw; raw.context_flags = 0; ASSERT_DEATH(Context context(dump, raw);, - "context\\.context_flags & 0x[0-9a-f]+"); + "context\\.context_flags & (0x[0-9a-f]+|MD_CONTEXT_X86)"); } TEST(ContextDeathTest, X86BadEndianness) { diff --git a/thirdparty/breakpad/processor/windows_frame_info.h b/thirdparty/breakpad/processor/windows_frame_info.h index 067f3cfd0..3aa7b3f65 100644 --- a/thirdparty/breakpad/processor/windows_frame_info.h +++ b/thirdparty/breakpad/processor/windows_frame_info.h @@ -72,7 +72,8 @@ struct WindowsFrameInfo { STACK_INFO_UNKNOWN = -1 }; - WindowsFrameInfo() : valid(VALID_NONE), + WindowsFrameInfo() : type_(STACK_INFO_UNKNOWN), + valid(VALID_NONE), prolog_size(0), epilog_size(0), parameter_size(0), @@ -82,7 +83,8 @@ struct WindowsFrameInfo { allocates_base_pointer(0), program_string() {} - WindowsFrameInfo(u_int32_t set_prolog_size, + WindowsFrameInfo(StackInfoTypes type, + u_int32_t set_prolog_size, u_int32_t set_epilog_size, u_int32_t set_parameter_size, u_int32_t set_saved_register_size, @@ -90,7 +92,8 @@ struct WindowsFrameInfo { u_int32_t set_max_stack_size, int set_allocates_base_pointer, const std::string set_program_string) - : valid(VALID_ALL), + : type_(type), + valid(VALID_ALL), prolog_size(set_prolog_size), epilog_size(set_epilog_size), parameter_size(set_parameter_size), @@ -140,7 +143,8 @@ struct WindowsFrameInfo { allocates_base_pointer = strtoul(tokens[10], NULL, 16); } - return new WindowsFrameInfo(prolog_size, + return new WindowsFrameInfo(static_cast(type), + prolog_size, epilog_size, parameter_size, saved_register_size, @@ -152,6 +156,7 @@ struct WindowsFrameInfo { // CopyFrom makes "this" WindowsFrameInfo object identical to "that". void CopyFrom(const WindowsFrameInfo &that) { + type_ = that.type_; valid = that.valid; prolog_size = that.prolog_size; epilog_size = that.epilog_size; @@ -166,10 +171,13 @@ struct WindowsFrameInfo { // Clears the WindowsFrameInfo object so that users will see it as though // it contains no information. void Clear() { + type_ = STACK_INFO_UNKNOWN; valid = VALID_NONE; program_string.erase(); } + StackInfoTypes type_; + // Identifies which fields in the structure are valid. This is of // type Validity, but it is defined as an int because it's not // possible to OR values into an enumerated type. Users must check diff --git a/thirdparty/breakpad/third_party/curl/COPYING b/thirdparty/breakpad/third_party/curl/COPYING new file mode 100644 index 000000000..610fbdb07 --- /dev/null +++ b/thirdparty/breakpad/third_party/curl/COPYING @@ -0,0 +1,22 @@ +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2011, Daniel Stenberg, . + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + diff --git a/thirdparty/breakpad/third_party/libdisasm/.dirstamp b/thirdparty/breakpad/third_party/libdisasm/.dirstamp deleted file mode 100644 index e69de29bb..000000000 diff --git a/thirdparty/breakpad/third_party/lss/linux_syscall_support.h b/thirdparty/breakpad/third_party/lss/linux_syscall_support.h index 4f2ce3c91..604649e5b 100644 --- a/thirdparty/breakpad/third_party/lss/linux_syscall_support.h +++ b/thirdparty/breakpad/third_party/lss/linux_syscall_support.h @@ -87,7 +87,7 @@ */ #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \ - && defined(__linux) + && (defined(__linux) || defined(__ANDROID__)) #ifndef SYS_CPLUSPLUS #ifdef __cplusplus @@ -99,6 +99,7 @@ extern "C" { #endif #include +#include #include #include #include @@ -107,7 +108,7 @@ extern "C" { #include #include #include -#include +#include #include #include #include @@ -1844,8 +1845,6 @@ struct kernel_statfs { void *newtls, int *child_tidptr) { long __res; { - register void *__tls __asm__("r8") = newtls; - register int *__ctid __asm__("r10") = child_tidptr; __asm__ __volatile__(/* if (fn == NULL) * return -EINVAL; */ @@ -1876,6 +1875,8 @@ struct kernel_statfs { * %r10 = child_tidptr) */ "movq %2,%%rax\n" + "movq %9,%%r8\n" + "movq %10,%%r10\n" LSS_ENTRYPOINT /* if (%rax != 0) @@ -1906,8 +1907,9 @@ struct kernel_statfs { : "=a" (__res) : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), "r"(fn), "S"(child_stack), "D"(flags), "r"(arg), - "d"(parent_tidptr), "r"(__tls), "r"(__ctid) - : "rsp", "memory", "r11", "rcx"); + "d"(parent_tidptr), "r"(newtls), + "r"(child_tidptr) + : "rsp", "memory", "r8", "r10", "r11", "rcx"); } LSS_RETURN(int, __res); } @@ -2138,7 +2140,9 @@ struct kernel_statfs { * return -EINVAL; */ "cmp %2,#0\n" + "it ne\n" "cmpne %3,#0\n" + "it eq\n" "moveq %0,%1\n" "beq 1f\n" @@ -2166,8 +2170,24 @@ struct kernel_statfs { /* In the child, now. Call "fn(arg)". */ "ldr r0,[sp, #4]\n" + + /* When compiling for Thumb-2 the "MOV LR,PC" here + * won't work because it loads PC+4 into LR, + * whereas the LDR is a 4-byte instruction. + * This results in the child thread always + * crashing with an "Illegal Instruction" when it + * returned into the middle of the LDR instruction + * The instruction sequence used instead was + * recommended by + * "https://wiki.edubuntu.org/ARM/Thumb2PortingHowto#Quick_Reference". + */ + #ifdef __thumb2__ + "ldr r7,[sp]\n" + "blx r7\n" + #else "mov lr,pc\n" "ldr pc,[sp]\n" + #endif /* Call _exit(%r0). */ @@ -2724,7 +2744,7 @@ struct kernel_statfs { LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, const stack_t*, o) #if defined(__NR_sigreturn) - LSS_INLINE _syscall1(int, sigreturn, unsigned long, u); + LSS_INLINE _syscall1(int, sigreturn, unsigned long, u) #endif LSS_INLINE _syscall2(int, stat, const char*, f, struct kernel_stat*, b) @@ -2741,7 +2761,7 @@ struct kernel_statfs { const struct kernel_iovec*, v, size_t, c) #if defined(__NR_getcpu) LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu, - unsigned *, node, void *, unused); + unsigned *, node, void *, unused) #endif #if defined(__x86_64__) || \ (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) @@ -3012,9 +3032,13 @@ struct kernel_statfs { #define __NR__sigprocmask __NR_sigprocmask #define __NR__sigsuspend __NR_sigsuspend #define __NR__socketcall __NR_socketcall +#if ! defined(__ANDROID__) + /* The Android NDK #defines stat64 stat, so avoid multiple-definition */ LSS_INLINE _syscall2(int, fstat64, int, f, struct kernel_stat64 *, b) - LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, +#endif + LSS_INLINE _syscall5(int, _llseek, uint, fd, + unsigned long, hi, unsigned long, lo, loff_t *, res, uint, wh) #if !defined(__ARM_EABI__) LSS_INLINE _syscall1(void*, mmap, void*, a) @@ -3022,7 +3046,7 @@ struct kernel_statfs { LSS_INLINE _syscall6(void*, mmap2, void*, s, size_t, l, int, p, int, f, int, d, - __off64_t, o) + off_t, o) LSS_INLINE _syscall3(int, _sigaction, int, s, const struct kernel_old_sigaction*, a, struct kernel_old_sigaction*, o) @@ -3037,8 +3061,11 @@ struct kernel_statfs { int, b, unsigned long, s) #endif +#if ! defined(__ANDROID__) + /* The Android NDK #defines stat64 stat, so avoid multiple-definition */ LSS_INLINE _syscall2(int, stat64, const char *, p, struct kernel_stat64 *, b) +#endif LSS_INLINE int LSS_NAME(sigaction)(int signum, const struct kernel_sigaction *act,