diff --git a/Makefile.am b/Makefile.am index 3da90508..1e3279ff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -134,6 +134,7 @@ src_client_linux_libbreakpad_client_a_SOURCES = \ src/client/linux/handler/exception_handler.cc \ src/client/linux/handler/minidump_descriptor.cc \ src/client/linux/log/log.cc \ + src/client/linux/microdump_writer/microdump_writer.cc \ src/client/linux/minidump_writer/linux_dumper.cc \ src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ src/client/linux/minidump_writer/minidump_writer.cc \ @@ -450,6 +451,7 @@ src_client_linux_linux_client_unittest_shlib_LDADD = \ src/client/linux/handler/exception_handler.o \ src/client/linux/handler/minidump_descriptor.o \ src/client/linux/log/log.o \ + src/client/linux/microdump_writer/microdump_writer.o \ src/client/linux/minidump_writer/linux_dumper.o \ src/client/linux/minidump_writer/linux_ptrace_dumper.o \ src/client/linux/minidump_writer/minidump_writer.o \ diff --git a/Makefile.in b/Makefile.in index 0db0d7a6..d15881f1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -288,6 +288,7 @@ am__src_client_linux_libbreakpad_client_a_SOURCES_DIST = \ src/client/linux/handler/exception_handler.cc \ src/client/linux/handler/minidump_descriptor.cc \ src/client/linux/log/log.cc \ + src/client/linux/microdump_writer/microdump_writer.cc \ src/client/linux/minidump_writer/linux_dumper.cc \ src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ src/client/linux/minidump_writer/minidump_writer.cc \ @@ -309,6 +310,7 @@ am__dirstamp = $(am__leading_dot)dirstamp @LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.$(OBJEXT) \ @LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.$(OBJEXT) \ @LINUX_HOST_TRUE@ src/client/linux/log/log.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.$(OBJEXT) \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.$(OBJEXT) \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.$(OBJEXT) \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.$(OBJEXT) \ @@ -1859,6 +1861,7 @@ lib_LIBRARIES = $(am__append_5) $(am__append_8) @LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.cc \ @LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.cc \ @LINUX_HOST_TRUE@ src/client/linux/log/log.cc \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.cc \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.cc \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.cc \ @@ -2079,6 +2082,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.o \ @LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.o \ @LINUX_HOST_TRUE@ src/client/linux/log/log.o \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.o \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.o \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.o \ @LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.o \ @@ -3074,6 +3078,15 @@ src/client/linux/log/$(DEPDIR)/$(am__dirstamp): src/client/linux/log/log.$(OBJEXT): \ src/client/linux/log/$(am__dirstamp) \ src/client/linux/log/$(DEPDIR)/$(am__dirstamp) +src/client/linux/microdump_writer/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/microdump_writer + @: > src/client/linux/microdump_writer/$(am__dirstamp) +src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/microdump_writer/$(DEPDIR) + @: > src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/microdump_writer/microdump_writer.$(OBJEXT): \ + src/client/linux/microdump_writer/$(am__dirstamp) \ + src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) src/client/linux/minidump_writer/$(am__dirstamp): @$(MKDIR_P) src/client/linux/minidump_writer @: > src/client/linux/minidump_writer/$(am__dirstamp) @@ -4155,6 +4168,7 @@ mostlyclean-compile: -rm -f src/client/linux/dump_writer_common/*.$(OBJEXT) -rm -f src/client/linux/handler/*.$(OBJEXT) -rm -f src/client/linux/log/*.$(OBJEXT) + -rm -f src/client/linux/microdump_writer/*.$(OBJEXT) -rm -f src/client/linux/minidump_writer/*.$(OBJEXT) -rm -f src/common/*.$(OBJEXT) -rm -f src/common/android/*.$(OBJEXT) @@ -4184,6 +4198,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/handler/$(DEPDIR)/minidump_descriptor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/handler/$(DEPDIR)/src_client_linux_linux_client_unittest_shlib-exception_handler_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/log/$(DEPDIR)/log.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/microdump_writer/$(DEPDIR)/microdump_writer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_core_dumper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po@am__quote@ @@ -7690,6 +7705,8 @@ distclean-generic: -rm -f src/client/linux/handler/$(am__dirstamp) -rm -f src/client/linux/log/$(DEPDIR)/$(am__dirstamp) -rm -f src/client/linux/log/$(am__dirstamp) + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/microdump_writer/$(am__dirstamp) -rm -f src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) -rm -f src/client/linux/minidump_writer/$(am__dirstamp) -rm -f src/common/$(DEPDIR)/$(am__dirstamp) @@ -7732,7 +7749,7 @@ clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf src/client/$(DEPDIR) src/client/linux/crash_generation/$(DEPDIR) src/client/linux/dump_writer_common/$(DEPDIR) src/client/linux/handler/$(DEPDIR) src/client/linux/log/$(DEPDIR) src/client/linux/minidump_writer/$(DEPDIR) src/common/$(DEPDIR) src/common/android/$(DEPDIR) src/common/dwarf/$(DEPDIR) src/common/linux/$(DEPDIR) src/common/linux/tests/$(DEPDIR) src/common/tests/$(DEPDIR) src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) src/third_party/libdisasm/$(DEPDIR) src/tools/linux/core2md/$(DEPDIR) src/tools/linux/dump_syms/$(DEPDIR) src/tools/linux/md2core/$(DEPDIR) src/tools/linux/symupload/$(DEPDIR) + -rm -rf src/client/$(DEPDIR) src/client/linux/crash_generation/$(DEPDIR) src/client/linux/dump_writer_common/$(DEPDIR) src/client/linux/handler/$(DEPDIR) src/client/linux/log/$(DEPDIR) src/client/linux/microdump_writer/$(DEPDIR) src/client/linux/minidump_writer/$(DEPDIR) src/common/$(DEPDIR) src/common/android/$(DEPDIR) src/common/dwarf/$(DEPDIR) src/common/linux/$(DEPDIR) src/common/linux/tests/$(DEPDIR) src/common/tests/$(DEPDIR) src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) src/third_party/libdisasm/$(DEPDIR) src/tools/linux/core2md/$(DEPDIR) src/tools/linux/dump_syms/$(DEPDIR) src/tools/linux/md2core/$(DEPDIR) src/tools/linux/symupload/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags @@ -7784,7 +7801,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf src/client/$(DEPDIR) src/client/linux/crash_generation/$(DEPDIR) src/client/linux/dump_writer_common/$(DEPDIR) src/client/linux/handler/$(DEPDIR) src/client/linux/log/$(DEPDIR) src/client/linux/minidump_writer/$(DEPDIR) src/common/$(DEPDIR) src/common/android/$(DEPDIR) src/common/dwarf/$(DEPDIR) src/common/linux/$(DEPDIR) src/common/linux/tests/$(DEPDIR) src/common/tests/$(DEPDIR) src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) src/third_party/libdisasm/$(DEPDIR) src/tools/linux/core2md/$(DEPDIR) src/tools/linux/dump_syms/$(DEPDIR) src/tools/linux/md2core/$(DEPDIR) src/tools/linux/symupload/$(DEPDIR) + -rm -rf src/client/$(DEPDIR) src/client/linux/crash_generation/$(DEPDIR) src/client/linux/dump_writer_common/$(DEPDIR) src/client/linux/handler/$(DEPDIR) src/client/linux/log/$(DEPDIR) src/client/linux/microdump_writer/$(DEPDIR) src/client/linux/minidump_writer/$(DEPDIR) src/common/$(DEPDIR) src/common/android/$(DEPDIR) src/common/dwarf/$(DEPDIR) src/common/linux/$(DEPDIR) src/common/linux/tests/$(DEPDIR) src/common/tests/$(DEPDIR) src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) src/third_party/libdisasm/$(DEPDIR) src/tools/linux/core2md/$(DEPDIR) src/tools/linux/dump_syms/$(DEPDIR) src/tools/linux/md2core/$(DEPDIR) src/tools/linux/symupload/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/src/client/linux/dump_writer_common/mapping_info.h b/src/client/linux/dump_writer_common/mapping_info.h index c206b504..5f247cfd 100644 --- a/src/client/linux/dump_writer_common/mapping_info.h +++ b/src/client/linux/dump_writer_common/mapping_info.h @@ -30,8 +30,12 @@ #ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ #define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ +#include +#include #include +#include "google_breakpad/common/minidump_format.h" + namespace google_breakpad { // One of these is produced for each mapping in the process (i.e. line in @@ -44,6 +48,14 @@ struct MappingInfo { char name[NAME_MAX]; }; +struct MappingEntry { + MappingInfo first; + uint8_t second[sizeof(MDGUID)]; +}; + +// A list of +typedef std::list MappingList; + } // namespace google_breakpad #endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 7cad7756..3e2d1962 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -91,6 +91,7 @@ #include "common/linux/linux_libc_support.h" #include "common/memory.h" #include "client/linux/log/log.h" +#include "client/linux/microdump_writer/microdump_writer.h" #include "client/linux/minidump_writer/linux_dumper.h" #include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/eintr_wrapper.h" @@ -210,7 +211,8 @@ ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, if (server_fd >= 0) crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); - if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && + !minidump_descriptor_.IsMicrodumpOnConsole()) minidump_descriptor_.UpdatePath(); pthread_mutex_lock(&g_handler_stack_mutex_); @@ -548,6 +550,12 @@ void ExceptionHandler::WaitForContinueSignal() { // Runs on the cloned process. bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, size_t context_size) { + if (minidump_descriptor_.IsMicrodumpOnConsole()) { + return google_breakpad::WriteMicrodump(crashing_process, + context, + context_size, + mapping_list_); + } if (minidump_descriptor_.IsFD()) { return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), minidump_descriptor_.size_limit(), @@ -583,7 +591,8 @@ bool ExceptionHandler::WriteMinidump(const string& dump_path, __attribute__((optimize("no-omit-frame-pointer"))) #endif bool ExceptionHandler::WriteMinidump() { - if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && + !minidump_descriptor_.IsMicrodumpOnConsole()) { // Update the path of the minidump so that this can be called multiple times // and new files are created for each minidump. This is done before the // generation happens, as clients may want to access the MinidumpDescriptor diff --git a/src/client/linux/handler/minidump_descriptor.cc b/src/client/linux/handler/minidump_descriptor.cc index c4618adc..029f8926 100644 --- a/src/client/linux/handler/minidump_descriptor.cc +++ b/src/client/linux/handler/minidump_descriptor.cc @@ -35,8 +35,12 @@ namespace google_breakpad { +//static +const MinidumpDescriptor::MicrodumpOnConsole kMicrodumpOnConsole = {}; + MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) - : fd_(descriptor.fd_), + : mode_(descriptor.mode_), + fd_(descriptor.fd_), directory_(descriptor.directory_), c_path_(NULL), size_limit_(descriptor.size_limit_) { @@ -50,6 +54,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=( const MinidumpDescriptor& descriptor) { assert(descriptor.path_.empty()); + mode_ = descriptor.mode_; fd_ = descriptor.fd_; directory_ = descriptor.directory_; path_.clear(); @@ -63,7 +68,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=( } void MinidumpDescriptor::UpdatePath() { - assert(fd_ == -1 && !directory_.empty()); + assert(mode_ == kWriteMinidumpToFile && !directory_.empty()); GUID guid; char guid_str[kGUIDStringLength + 1]; @@ -72,7 +77,7 @@ void MinidumpDescriptor::UpdatePath() { } path_.clear(); - path_ = directory_ + "/" + guid_str + ".dmp"; + path_ = directory_ + "/" + guid_str + ".dmp"; c_path_ = path_.c_str(); } diff --git a/src/client/linux/handler/minidump_descriptor.h b/src/client/linux/handler/minidump_descriptor.h index 9ffe622b..ed656cee 100644 --- a/src/client/linux/handler/minidump_descriptor.h +++ b/src/client/linux/handler/minidump_descriptor.h @@ -37,18 +37,25 @@ #include "common/using_std_string.h" -// The MinidumpDescriptor describes how to access a minidump: it can contain -// either a file descriptor or a path. -// Note that when using files, it is created with the path to a directory. -// The actual path where the minidump is generated is created by this class. +// This class describes how a crash dump should be generated, either: +// - Writing a full minidump to a file in a given directory (the actual path, +// inside the directory, is determined by this class). +// - Writing a full minidump to a given fd. +// - Writing a reduced microdump to the console (logcat on Android). namespace google_breakpad { class MinidumpDescriptor { public: - MinidumpDescriptor() : fd_(-1), size_limit_(-1) {} + struct MicrodumpOnConsole {}; + static const MicrodumpOnConsole kMicrodumpOnConsole; + + MinidumpDescriptor() : mode_(kUninitialized), + fd_(-1), + size_limit_(-1) {} explicit MinidumpDescriptor(const string& directory) - : fd_(-1), + : mode_(kWriteMinidumpToFile), + fd_(-1), directory_(directory), c_path_(NULL), size_limit_(-1) { @@ -56,16 +63,24 @@ class MinidumpDescriptor { } explicit MinidumpDescriptor(int fd) - : fd_(fd), + : mode_(kWriteMinidumpToFd), + fd_(fd), c_path_(NULL), size_limit_(-1) { assert(fd != -1); } + explicit MinidumpDescriptor(const MicrodumpOnConsole&) + : mode_(kWriteMicrodumpToConsole), + fd_(-1), + size_limit_(-1) {} + explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor); - bool IsFD() const { return fd_ != -1; } + static MinidumpDescriptor getMicrodumpDescriptor(); + + bool IsFD() const { return mode_ == kWriteMinidumpToFd; } int fd() const { return fd_; } @@ -73,6 +88,10 @@ class MinidumpDescriptor { const char* path() const { return c_path_; } + bool IsMicrodumpOnConsole() const { + return mode_ == kWriteMicrodumpToConsole; + } + // Updates the path so it is unique. // Should be called from a normal context: this methods uses the heap. void UpdatePath(); @@ -81,6 +100,16 @@ class MinidumpDescriptor { void set_size_limit(off_t limit) { size_limit_ = limit; } private: + enum DumpMode { + kUninitialized = 0, + kWriteMinidumpToFile, + kWriteMinidumpToFd, + kWriteMicrodumpToConsole + }; + + // Specifies the dump mode (see DumpMode). + DumpMode mode_; + // The file descriptor where the minidump is generated. int fd_; diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc new file mode 100644 index 00000000..494e2a20 --- /dev/null +++ b/src/client/linux/microdump_writer/microdump_writer.cc @@ -0,0 +1,364 @@ +// Copyright (c) 2014, 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 translation unit generates microdumps into the console (logcat on +// Android). See crbug.com/410294 for more info and design docs. + +#include "client/linux/microdump_writer/microdump_writer.h" + +#include + +#include "client/linux/dump_writer_common/seccomp_unwinder.h" +#include "client/linux/dump_writer_common/thread_info.h" +#include "client/linux/dump_writer_common/ucontext_reader.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "common/linux/linux_libc_support.h" +#include "client/linux/log/log.h" + +namespace { + +using google_breakpad::ExceptionHandler; +using google_breakpad::LinuxDumper; +using google_breakpad::LinuxPtraceDumper; +using google_breakpad::MappingInfo; +using google_breakpad::MappingList; +using google_breakpad::RawContextCPU; +using google_breakpad::SeccompUnwinder; +using google_breakpad::ThreadInfo; +using google_breakpad::UContextReader; + +class MicrodumpWriter { + public: + MicrodumpWriter(const ExceptionHandler::CrashContext* context, + const MappingList& mappings, + LinuxDumper* dumper) + : ucontext_(context ? &context->context : NULL), +#if !defined(__ARM_EABI__) && !defined(__mips__) + float_state_(context ? &context->float_state : NULL), +#endif + dumper_(dumper), + mapping_list_(mappings) { } + + ~MicrodumpWriter() { dumper_->ThreadsResume(); } + + bool Init() { + if (!dumper_->Init()) + return false; + return dumper_->ThreadsSuspend(); + } + + bool Dump() { + bool success; + LogLine("-----BEGIN BREAKPAD MICRODUMP-----"); + success = DumpOSInformation(); + if (success) + success = DumpCrashingThread(); + if (success) + success = DumpMappings(); + LogLine("-----END BREAKPAD MICRODUMP-----"); + dumper_->ThreadsResume(); + return success; + } + + private: + // Writes one line to the system log. + void LogLine(const char* msg) { + logger::write(msg, my_strlen(msg)); + } + + // Stages the given string in the current line buffer. + void LogAppend(const char* str) { + my_strlcat(log_line_, str, sizeof(log_line_)); + } + + // As above (required to take precedence over template specialization below). + void LogAppend(char* str) { + LogAppend(const_cast(str)); + } + + // Stages the hex repr. of the given int type in the current line buffer. + template + void LogAppend(T value) { + // Make enough room to hex encode the largest int type + NUL. + static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + char hexstr[sizeof(T) * 2 + 1]; + for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4) + hexstr[i] = HEX[static_cast(value) & 0x0F]; + hexstr[sizeof(T) * 2] = '\0'; + LogAppend(hexstr); + } + + // Stages the buffer content hex-encoded in the current line buffer. + void LogAppend(const void* buf, size_t length) { + const uint8_t* ptr = reinterpret_cast(buf); + for (size_t i = 0; i < length; ++i, ++ptr) + LogAppend(*ptr); + } + + // Writes out the current line buffer on the system log. + void LogCommitLine() { + logger::write(log_line_, my_strlen(log_line_)); + my_strlcpy(log_line_, "", sizeof(log_line_)); + } + + bool DumpOSInformation() { + struct utsname uts; + if (uname(&uts)) + return false; + +#if defined(__ANDROID__) + const char kOSId[] = "A"; +#else + const char kOSId[] = "L"; +#endif + + LogAppend("O "); + LogAppend(kOSId); + LogAppend(" \""); + LogAppend(uts.machine); + LogAppend("\" \""); + LogAppend(uts.release); + LogAppend(" \""); + LogAppend(uts.version); + LogAppend("\""); + LogCommitLine(); + return true; + } + + bool DumpThreadStack(uint32_t thread_id, + uintptr_t stack_pointer, + int max_stack_len, + uint8_t** stack_copy) { + *stack_copy = NULL; + const void* stack; + size_t stack_len; + + if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) { + assert(false); + return false; + } + + LogAppend("S 0 "); + LogAppend(stack_pointer); + LogAppend(" "); + LogAppend(reinterpret_cast(stack)); + LogAppend(" "); + LogAppend(stack_len); + LogCommitLine(); + + if (max_stack_len >= 0 && + stack_len > static_cast(max_stack_len)) { + stack_len = max_stack_len; + } + + *stack_copy = reinterpret_cast(Alloc(stack_len)); + dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len); + + // Dump the content of the stack, splicing it into chunks which size is + // compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD). + const size_t STACK_DUMP_CHUNK_SIZE = 384; + for (size_t stack_off = 0; stack_off < stack_len; + stack_off += STACK_DUMP_CHUNK_SIZE) { + LogAppend("S "); + LogAppend(reinterpret_cast(stack) + stack_off); + LogAppend(" "); + LogAppend(*stack_copy + stack_off, + std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off)); + LogCommitLine(); + } + return true; + } + + // Write information about the crashing thread. + bool DumpCrashingThread() { + const unsigned num_threads = dumper_->threads().size(); + + for (unsigned i = 0; i < num_threads; ++i) { + MDRawThread thread; + my_memset(&thread, 0, sizeof(thread)); + thread.thread_id = dumper_->threads()[i]; + + // Dump only the crashing thread. + if (static_cast(thread.thread_id) != dumper_->crash_thread()) + continue; + + assert(ucontext_); + assert(!dumper_->IsPostMortem()); + + uint8_t* stack_copy; + const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_); + if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy)) + return false; + + RawContextCPU cpu; + my_memset(&cpu, 0, sizeof(RawContextCPU)); +#if !defined(__ARM_EABI__) && !defined(__mips__) + UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); +#else + UContextReader::FillCPUContext(&cpu, ucontext_); +#endif + if (stack_copy) + SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy); + DumpCPUState(&cpu); + } + return true; + } + + void DumpCPUState(RawContextCPU* cpu) { + LogAppend("C "); + LogAppend(cpu, sizeof(*cpu)); + LogCommitLine(); + } + + // If there is caller-provided information about this mapping + // in the mapping_list_ list, return true. Otherwise, return false. + bool HaveMappingInfo(const MappingInfo& mapping) { + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + // Ignore any mappings that are wholly contained within + // mappings in the mapping_info_ list. + if (mapping.start_addr >= iter->first.start_addr && + (mapping.start_addr + mapping.size) <= + (iter->first.start_addr + iter->first.size)) { + return true; + } + } + return false; + } + + // Dump information about the provided |mapping|. If |identifier| is non-NULL, + // use it instead of calculating a file ID from the mapping. + void DumpModule(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + const uint8_t* identifier) { + MDGUID module_identifier; + if (identifier) { + // GUID was provided by caller. + my_memcpy(&module_identifier, identifier, sizeof(MDGUID)); + } else { + dumper_->ElfFileIdentifierForMapping( + mapping, + member, + mapping_id, + reinterpret_cast(&module_identifier)); + } + + char file_name[NAME_MAX]; + char file_path[NAME_MAX]; + LinuxDumper::GetMappingEffectiveNameAndPath( + mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); + + LogAppend("M "); + LogAppend(static_cast(mapping.start_addr)); + LogAppend(" "); + LogAppend(mapping.offset); + LogAppend(" "); + LogAppend(mapping.size); + LogAppend(" "); + LogAppend(module_identifier.data1); + LogAppend(module_identifier.data2); + LogAppend(module_identifier.data3); + LogAppend(module_identifier.data4[0]); + LogAppend(module_identifier.data4[1]); + LogAppend(module_identifier.data4[2]); + LogAppend(module_identifier.data4[3]); + LogAppend(module_identifier.data4[4]); + LogAppend(module_identifier.data4[5]); + LogAppend(module_identifier.data4[6]); + LogAppend(module_identifier.data4[7]); + LogAppend(" "); + LogAppend(file_name); + LogCommitLine(); + } + + // Write information about the mappings in effect. + bool DumpMappings() { + // First write all the mappings from the dumper + for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + // Skip mappings which don't look like libraries. + if (!strstr(mapping.name, ".so") || // dump only libs (skip fonts, apks). + mapping.size < 4096) { // too small to get a signature for. + continue; + } + + if (HaveMappingInfo(mapping)) + continue; + + DumpModule(mapping, true, i, NULL); + } + // Next write all the mappings provided by the caller + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + DumpModule(iter->first, false, 0, iter->second); + } + return true; + } + + void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } + + const struct ucontext* const ucontext_; +#if !defined(__ARM_EABI__) && !defined(__mips__) + const google_breakpad::fpstate_t* const float_state_; +#endif + LinuxDumper* dumper_; + const MappingList& mapping_list_; + char log_line_[512]; +}; +} // namespace + +namespace google_breakpad { + +bool WriteMicrodump(pid_t crashing_process, + const void* blob, + size_t blob_size, + const MappingList& mappings) { + LinuxPtraceDumper dumper(crashing_process); + const ExceptionHandler::CrashContext* context = NULL; + if (blob) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + context = reinterpret_cast(blob); + dumper.set_crash_address( + reinterpret_cast(context->siginfo.si_addr)); + dumper.set_crash_signal(context->siginfo.si_signo); + dumper.set_crash_thread(context->tid); + } + MicrodumpWriter writer(context, mappings, &dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace google_breakpad diff --git a/src/client/linux/microdump_writer/microdump_writer.h b/src/client/linux/microdump_writer/microdump_writer.h new file mode 100644 index 00000000..3c19f3d0 --- /dev/null +++ b/src/client/linux/microdump_writer/microdump_writer.h @@ -0,0 +1,58 @@ +// Copyright (c) 2014, 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_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ + +#include +#include + +#include "client/linux/dump_writer_common/mapping_info.h" + +namespace google_breakpad { + +// Writes a microdump (a reduced dump containing only the state of the crashing +// thread) on the console (logcat on Android). These functions do not malloc nor +// use libc functions which may. Thus, it can be used in contexts where the +// state of the heap may be corrupt. +// Args: +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob| in bytes. +// mappings: a list of additional mappings provided by the application. +// +// Returns true iff successful. +bool WriteMicrodump(pid_t crashing_process, + const void* blob, + size_t blob_size, + const MappingList& mappings); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ diff --git a/src/client/linux/minidump_writer/minidump_writer.h b/src/client/linux/minidump_writer/minidump_writer.h index e1afe69b..d13fb120 100644 --- a/src/client/linux/minidump_writer/minidump_writer.h +++ b/src/client/linux/minidump_writer/minidump_writer.h @@ -45,14 +45,6 @@ namespace google_breakpad { class ExceptionHandler; -struct MappingEntry { - MappingInfo first; - uint8_t second[sizeof(MDGUID)]; -}; - -// A list of -typedef std::list MappingList; - #if defined(__aarch64__) typedef struct fpsimd_context fpstate_t; #elif !defined(__ARM_EABI__) && !defined(__mips__)