summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc609
1 files changed, 0 insertions, 609 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc b/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc
deleted file mode 100644
index a508667a0..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/microdump_writer/microdump_writer.cc
+++ /dev/null
@@ -1,609 +0,0 @@
-// 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 <limits>
-
-#include <sys/utsname.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/handler/microdump_extra_info.h"
-#include "client/linux/log/log.h"
-#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
-#include "common/linux/file_id.h"
-#include "common/linux/linux_libc_support.h"
-#include "common/memory.h"
-
-namespace {
-
-using google_breakpad::auto_wasteful_vector;
-using google_breakpad::ExceptionHandler;
-using google_breakpad::kDefaultBuildIdSize;
-using google_breakpad::LinuxDumper;
-using google_breakpad::LinuxPtraceDumper;
-using google_breakpad::MappingInfo;
-using google_breakpad::MappingList;
-using google_breakpad::MicrodumpExtraInfo;
-using google_breakpad::RawContextCPU;
-using google_breakpad::ThreadInfo;
-using google_breakpad::UContextReader;
-
-const size_t kLineBufferSize = 2048;
-
-#if !defined(__LP64__)
-// The following are only used by DumpFreeSpace, so need to be compiled
-// in conditionally in the same way.
-
-template <typename Dst, typename Src>
-Dst saturated_cast(Src src) {
- if (src >= std::numeric_limits<Dst>::max())
- return std::numeric_limits<Dst>::max();
- if (src <= std::numeric_limits<Dst>::min())
- return std::numeric_limits<Dst>::min();
- return static_cast<Dst>(src);
-}
-
-int Log2Floor(uint64_t n) {
- // Copied from chromium src/base/bits.h
- if (n == 0)
- return -1;
- int log = 0;
- uint64_t value = n;
- for (int i = 5; i >= 0; --i) {
- int shift = (1 << i);
- uint64_t x = value >> shift;
- if (x != 0) {
- value = x;
- log += shift;
- }
- }
- assert(value == 1u);
- return log;
-}
-
-bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) {
- // Because of load biasing, we can end up with a situation where two
- // mappings actually overlap. So we will define adjacency to also include a
- // b start address that lies within a's address range (including starting
- // immediately after a).
- // Because load biasing only ever moves the start address backwards, the end
- // address should still increase.
- return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr;
-}
-
-bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) {
- // Return true if mapping a is before mapping b.
- // For the same reason (load biasing) we compare end addresses, which - unlike
- // start addresses - will not have been modified.
- return a->start_addr + a->size < b->start_addr + b->size;
-}
-
-size_t NextOrderedMapping(
- const google_breakpad::wasteful_vector<MappingInfo*>& mappings,
- size_t curr) {
- // Find the mapping that directly follows mappings[curr].
- // If no such mapping exists, return |invalid| to indicate this.
- const size_t invalid = std::numeric_limits<size_t>::max();
- size_t best = invalid;
- for (size_t next = 0; next < mappings.size(); ++next) {
- if (MappingLessThan(mappings[curr], mappings[next]) &&
- (best == invalid || MappingLessThan(mappings[next], mappings[best]))) {
- best = next;
- }
- }
- return best;
-}
-
-#endif // !__LP64__
-
-class MicrodumpWriter {
- public:
- MicrodumpWriter(const ExceptionHandler::CrashContext* context,
- const MappingList& mappings,
- const MicrodumpExtraInfo& microdump_extra_info,
- 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),
- microdump_extra_info_(microdump_extra_info),
- log_line_(NULL) {
- log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
- if (log_line_)
- log_line_[0] = '\0'; // Clear out the log line buffer.
- }
-
- ~MicrodumpWriter() { dumper_->ThreadsResume(); }
-
- bool Init() {
- // In the exceptional case where the system was out of memory and there
- // wasn't even room to allocate the line buffer, bail out. There is nothing
- // useful we can possibly achieve without the ability to Log. At least let's
- // try to not crash.
- if (!dumper_->Init() || !log_line_)
- return false;
- return dumper_->ThreadsSuspend() && dumper_->LateInit();
- }
-
- bool Dump() {
- bool success;
- LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
- DumpProductInformation();
- DumpOSInformation();
- DumpProcessType();
- DumpGPUInformation();
-#if !defined(__LP64__)
- DumpFreeSpace();
-#endif
- 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) {
-#if defined(__ANDROID__)
- logger::writeToCrashLog(msg);
-#else
- logger::write(msg, my_strlen(msg));
- logger::write("\n", 1);
-#endif
- }
-
- // Stages the given string in the current line buffer.
- void LogAppend(const char* str) {
- my_strlcat(log_line_, str, kLineBufferSize);
- }
-
- // 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() {
- LogLine(log_line_);
- my_strlcpy(log_line_, "", kLineBufferSize);
- }
-
- void DumpProductInformation() {
- LogAppend("V ");
- if (microdump_extra_info_.product_info) {
- LogAppend(microdump_extra_info_.product_info);
- } else {
- LogAppend("UNKNOWN:0.0.0.0");
- }
- LogCommitLine();
- }
-
- void DumpProcessType() {
- LogAppend("P ");
- if (microdump_extra_info_.process_type) {
- LogAppend(microdump_extra_info_.process_type);
- } else {
- LogAppend("UNKNOWN");
- }
- LogCommitLine();
- }
-
- void DumpOSInformation() {
- const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
-
-#if defined(__ANDROID__)
- const char kOSId[] = "A";
-#else
- const char kOSId[] = "L";
-#endif
-
-// Dump the runtime architecture. On multiarch devices it might not match the
-// hw architecture (the one returned by uname()), for instance in the case of
-// a 32-bit app running on a aarch64 device.
-#if defined(__aarch64__)
- const char kArch[] = "arm64";
-#elif defined(__ARMEL__)
- const char kArch[] = "arm";
-#elif defined(__x86_64__)
- const char kArch[] = "x86_64";
-#elif defined(__i386__)
- const char kArch[] = "x86";
-#elif defined(__mips__)
-# if _MIPS_SIM == _ABIO32
- const char kArch[] = "mips";
-# elif _MIPS_SIM == _ABI64
- const char kArch[] = "mips64";
-# else
-# error "This mips ABI is currently not supported (n32)"
-#endif
-#else
-#error "This code has not been ported to your platform yet"
-#endif
-
- LogAppend("O ");
- LogAppend(kOSId);
- LogAppend(" ");
- LogAppend(kArch);
- LogAppend(" ");
- LogAppend(n_cpus);
- LogAppend(" ");
-
- // Dump the HW architecture (e.g., armv7l, aarch64).
- struct utsname uts;
- const bool has_uts_info = (uname(&uts) == 0);
- const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
- LogAppend(hwArch);
- LogAppend(" ");
-
- // If the client has attached a build fingerprint to the MinidumpDescriptor
- // use that one. Otherwise try to get some basic info from uname().
- if (microdump_extra_info_.build_fingerprint) {
- LogAppend(microdump_extra_info_.build_fingerprint);
- } else if (has_uts_info) {
- LogAppend(uts.release);
- LogAppend(" ");
- LogAppend(uts.version);
- } else {
- LogAppend("no build fingerprint available");
- }
- LogCommitLine();
- }
-
- void DumpGPUInformation() {
- LogAppend("G ");
- if (microdump_extra_info_.gpu_fingerprint) {
- LogAppend(microdump_extra_info_.gpu_fingerprint);
- } else {
- LogAppend("UNKNOWN");
- }
- LogCommitLine();
- }
-
- 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)) {
- // The stack pointer might not be available. In this case we don't hard
- // fail, just produce a (almost useless) microdump w/o a stack section.
- return true;
- }
-
- 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
- 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) {
-
- auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
- dumper_->allocator());
-
- if (identifier) {
- // GUID was provided by caller.
- identifier_bytes.insert(identifier_bytes.end(),
- identifier,
- identifier + sizeof(MDGUID));
- } else {
- dumper_->ElfFileIdentifierForMapping(
- mapping,
- member,
- mapping_id,
- identifier_bytes);
- }
-
- // Copy as many bytes of |identifier| as will fit into a MDGUID
- MDGUID module_identifier = {0};
- memcpy(&module_identifier, &identifier_bytes[0],
- std::min(sizeof(MDGUID), identifier_bytes.size()));
-
- char file_name[NAME_MAX];
- char file_path[NAME_MAX];
- dumper_->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("0 "); // Age is always 0 on Linux.
- LogAppend(file_name);
- LogCommitLine();
- }
-
-#if !defined(__LP64__)
- void DumpFreeSpace() {
- const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
- dumper_->mappings();
- if (mappings.size() == 0) return;
-
- // This is complicated by the fact that mappings is not in order. It should
- // be mostly in order, however the mapping that contains the entry point for
- // the process is always at the front of the vector.
-
- static const int HBITS = sizeof(size_t) * 8;
- size_t hole_histogram[HBITS];
- my_memset(hole_histogram, 0, sizeof(hole_histogram));
-
- // Find the lowest address mapping.
- size_t curr = 0;
- for (size_t i = 1; i < mappings.size(); ++i) {
- if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
- }
-
- uintptr_t lo_addr = mappings[curr]->start_addr;
-
- size_t hole_cnt = 0;
- size_t hole_max = 0;
- size_t hole_sum = 0;
-
- while (true) {
- // Skip to the end of an adjacent run of mappings. This is an optimization
- // for the fact that mappings is mostly sorted.
- while (curr != mappings.size() - 1 &&
- MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
- ++curr;
- }
-
- size_t next = NextOrderedMapping(mappings, curr);
- if (next == std::numeric_limits<size_t>::max())
- break;
-
- uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
- uintptr_t hole_hi = mappings[next]->start_addr;
-
- if (hole_hi > hole_lo) {
- size_t hole_sz = hole_hi - hole_lo;
- hole_sum += hole_sz;
- hole_max = std::max(hole_sz, hole_max);
- ++hole_cnt;
- ++hole_histogram[Log2Floor(hole_sz)];
- }
- curr = next;
- }
-
- uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
-
- LogAppend("H ");
- LogAppend(lo_addr);
- LogAppend(" ");
- LogAppend(hi_addr);
- LogAppend(" ");
- LogAppend(saturated_cast<uint16_t>(hole_cnt));
- LogAppend(" ");
- LogAppend(hole_max);
- LogAppend(" ");
- LogAppend(hole_sum);
- for (unsigned int i = 0; i < HBITS; ++i) {
- if (!hole_histogram[i]) continue;
- LogAppend(" ");
- LogAppend(saturated_cast<uint8_t>(i));
- LogAppend(":");
- LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
- }
- LogCommitLine();
- }
-#endif
-
- // 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];
- if (mapping.name[0] == 0 || // only want modules with filenames.
- !mapping.exec || // only want executable mappings.
- mapping.size < 4096 || // too small to get a signature for.
- 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 ucontext_t* const ucontext_;
-#if !defined(__ARM_EABI__) && !defined(__mips__)
- const google_breakpad::fpstate_t* const float_state_;
-#endif
- LinuxDumper* dumper_;
- const MappingList& mapping_list_;
- const MicrodumpExtraInfo microdump_extra_info_;
- char* log_line_;
-};
-} // namespace
-
-namespace google_breakpad {
-
-bool WriteMicrodump(pid_t crashing_process,
- const void* blob,
- size_t blob_size,
- const MappingList& mappings,
- const MicrodumpExtraInfo& microdump_extra_info) {
- 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, microdump_extra_info, &dumper);
- if (!writer.Init())
- return false;
- return writer.Dump();
-}
-
-} // namespace google_breakpad