aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--Makefile.in21
-rw-r--r--src/client/linux/dump_writer_common/mapping_info.h12
-rw-r--r--src/client/linux/handler/exception_handler.cc13
-rw-r--r--src/client/linux/handler/minidump_descriptor.cc11
-rw-r--r--src/client/linux/handler/minidump_descriptor.h45
-rw-r--r--src/client/linux/microdump_writer/microdump_writer.cc364
-rw-r--r--src/client/linux/microdump_writer/microdump_writer.h58
-rw-r--r--src/client/linux/minidump_writer/minidump_writer.h8
9 files changed, 511 insertions, 23 deletions
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 <limits.h>
+#include <list>
#include <stdint.h>
+#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 <MappingInfo, GUID>
+typedef std::list<MappingEntry> 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 <sys/utsname.h>
+
+#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<const char*>(str));
+ }
+
+ // Stages the hex repr. of the given int type in the current line buffer.
+ template<typename T>
+ 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<uint8_t>(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<const uint8_t*>(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<uintptr_t>(stack));
+ LogAppend(" ");
+ LogAppend(stack_len);
+ LogCommitLine();
+
+ if (max_stack_len >= 0 &&
+ stack_len > static_cast<unsigned int>(max_stack_len)) {
+ stack_len = max_stack_len;
+ }
+
+ *stack_copy = reinterpret_cast<uint8_t*>(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<uintptr_t>(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<pid_t>(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<uint8_t*>(&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<uintptr_t>(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<const ExceptionHandler::CrashContext*>(blob);
+ dumper.set_crash_address(
+ reinterpret_cast<uintptr_t>(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 <stdint.h>
+#include <sys/types.h>
+
+#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 <MappingInfo, GUID>
-typedef std::list<MappingEntry> MappingList;
-
#if defined(__aarch64__)
typedef struct fpsimd_context fpstate_t;
#elif !defined(__ARM_EABI__) && !defined(__mips__)