Home | History | Annotate | Download | only in microdump_writer
      1 // Copyright (c) 2014, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // This translation unit generates microdumps into the console (logcat on
     31 // Android). See crbug.com/410294 for more info and design docs.
     32 
     33 #include "client/linux/microdump_writer/microdump_writer.h"
     34 
     35 #include <sys/utsname.h>
     36 
     37 #include "client/linux/dump_writer_common/seccomp_unwinder.h"
     38 #include "client/linux/dump_writer_common/thread_info.h"
     39 #include "client/linux/dump_writer_common/ucontext_reader.h"
     40 #include "client/linux/handler/exception_handler.h"
     41 #include "client/linux/log/log.h"
     42 #include "client/linux/minidump_writer/linux_ptrace_dumper.h"
     43 #include "common/linux/linux_libc_support.h"
     44 
     45 namespace {
     46 
     47 using google_breakpad::ExceptionHandler;
     48 using google_breakpad::LinuxDumper;
     49 using google_breakpad::LinuxPtraceDumper;
     50 using google_breakpad::MappingInfo;
     51 using google_breakpad::MappingList;
     52 using google_breakpad::RawContextCPU;
     53 using google_breakpad::SeccompUnwinder;
     54 using google_breakpad::ThreadInfo;
     55 using google_breakpad::UContextReader;
     56 
     57 const size_t kLineBufferSize = 2048;
     58 
     59 class MicrodumpWriter {
     60  public:
     61   MicrodumpWriter(const ExceptionHandler::CrashContext* context,
     62                   const MappingList& mappings,
     63                   LinuxDumper* dumper)
     64       : ucontext_(context ? &context->context : NULL),
     65 #if !defined(__ARM_EABI__) && !defined(__mips__)
     66         float_state_(context ? &context->float_state : NULL),
     67 #endif
     68         dumper_(dumper),
     69         mapping_list_(mappings),
     70         log_line_(NULL) {
     71     log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
     72     if (log_line_)
     73       log_line_[0] = '\0';  // Clear out the log line buffer.
     74   }
     75 
     76   ~MicrodumpWriter() { dumper_->ThreadsResume(); }
     77 
     78   bool Init() {
     79     // In the exceptional case where the system was out of memory and there
     80     // wasn't even room to allocate the line buffer, bail out. There is nothing
     81     // useful we can possibly achieve without the ability to Log. At least let's
     82     // try to not crash.
     83     if (!dumper_->Init() || !log_line_)
     84       return false;
     85     return dumper_->ThreadsSuspend();
     86   }
     87 
     88   bool Dump() {
     89     bool success;
     90     LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
     91     success = DumpOSInformation();
     92     if (success)
     93       success = DumpCrashingThread();
     94     if (success)
     95       success = DumpMappings();
     96     LogLine("-----END BREAKPAD MICRODUMP-----");
     97     dumper_->ThreadsResume();
     98     return success;
     99   }
    100 
    101  private:
    102   // Writes one line to the system log.
    103   void LogLine(const char* msg) {
    104     logger::write(msg, my_strlen(msg));
    105 #if !defined(__ANDROID__)
    106     logger::write("\n", 1);  // Android logger appends the \n. Linux's doesn't.
    107 #endif
    108   }
    109 
    110   // Stages the given string in the current line buffer.
    111   void LogAppend(const char* str) {
    112     my_strlcat(log_line_, str, kLineBufferSize);
    113   }
    114 
    115   // As above (required to take precedence over template specialization below).
    116   void LogAppend(char* str) {
    117     LogAppend(const_cast<const char*>(str));
    118   }
    119 
    120   // Stages the hex repr. of the given int type in the current line buffer.
    121   template<typename T>
    122   void LogAppend(T value) {
    123     // Make enough room to hex encode the largest int type + NUL.
    124     static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    125                                'A', 'B', 'C', 'D', 'E', 'F'};
    126     char hexstr[sizeof(T) * 2 + 1];
    127     for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
    128       hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
    129     hexstr[sizeof(T) * 2] = '\0';
    130     LogAppend(hexstr);
    131   }
    132 
    133   // Stages the buffer content hex-encoded in the current line buffer.
    134   void LogAppend(const void* buf, size_t length) {
    135     const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
    136     for (size_t i = 0; i < length; ++i, ++ptr)
    137       LogAppend(*ptr);
    138   }
    139 
    140   // Writes out the current line buffer on the system log.
    141   void LogCommitLine() {
    142     LogLine(log_line_);
    143     my_strlcpy(log_line_, "", kLineBufferSize);
    144   }
    145 
    146   bool DumpOSInformation() {
    147     struct utsname uts;
    148     if (uname(&uts))
    149       return false;
    150     const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
    151 
    152 #if defined(__ANDROID__)
    153     const char kOSId[] = "A";
    154 #else
    155     const char kOSId[] = "L";
    156 #endif
    157 
    158 // We cannot depend on uts.machine. On multiarch devices it always returns the
    159 // primary arch, not the one that match the executable being run.
    160 #if defined(__aarch64__)
    161     const char kArch[] = "arm64";
    162 #elif defined(__ARMEL__)
    163     const char kArch[] = "arm";
    164 #elif defined(__x86_64__)
    165     const char kArch[] = "x86_64";
    166 #elif defined(__i386__)
    167     const char kArch[] = "x86";
    168 #elif defined(__mips__)
    169     const char kArch[] = "mips";
    170 #else
    171 #error "This code has not been ported to your platform yet"
    172 #endif
    173 
    174     LogAppend("O ");
    175     LogAppend(kOSId);
    176     LogAppend(" ");
    177     LogAppend(kArch);
    178     LogAppend(" ");
    179     LogAppend(n_cpus);
    180     LogAppend(" ");
    181     LogAppend(uts.machine);
    182     LogAppend(" ");
    183     LogAppend(uts.release);
    184     LogAppend(" ");
    185     LogAppend(uts.version);
    186     LogCommitLine();
    187     return true;
    188   }
    189 
    190   bool DumpThreadStack(uint32_t thread_id,
    191                        uintptr_t stack_pointer,
    192                        int max_stack_len,
    193                        uint8_t** stack_copy) {
    194     *stack_copy = NULL;
    195     const void* stack;
    196     size_t stack_len;
    197 
    198     if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
    199       // The stack pointer might not be available. In this case we don't hard
    200       // fail, just produce a (almost useless) microdump w/o a stack section.
    201       return true;
    202     }
    203 
    204     LogAppend("S 0 ");
    205     LogAppend(stack_pointer);
    206     LogAppend(" ");
    207     LogAppend(reinterpret_cast<uintptr_t>(stack));
    208     LogAppend(" ");
    209     LogAppend(stack_len);
    210     LogCommitLine();
    211 
    212     if (max_stack_len >= 0 &&
    213         stack_len > static_cast<unsigned int>(max_stack_len)) {
    214       stack_len = max_stack_len;
    215     }
    216 
    217     *stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
    218     dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
    219 
    220     // Dump the content of the stack, splicing it into chunks which size is
    221     // compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
    222     const size_t STACK_DUMP_CHUNK_SIZE = 384;
    223     for (size_t stack_off = 0; stack_off < stack_len;
    224          stack_off += STACK_DUMP_CHUNK_SIZE) {
    225       LogAppend("S ");
    226       LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
    227       LogAppend(" ");
    228       LogAppend(*stack_copy + stack_off,
    229                 std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
    230       LogCommitLine();
    231     }
    232     return true;
    233   }
    234 
    235   // Write information about the crashing thread.
    236   bool DumpCrashingThread() {
    237     const unsigned num_threads = dumper_->threads().size();
    238 
    239     for (unsigned i = 0; i < num_threads; ++i) {
    240       MDRawThread thread;
    241       my_memset(&thread, 0, sizeof(thread));
    242       thread.thread_id = dumper_->threads()[i];
    243 
    244       // Dump only the crashing thread.
    245       if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
    246         continue;
    247 
    248       assert(ucontext_);
    249       assert(!dumper_->IsPostMortem());
    250 
    251       uint8_t* stack_copy;
    252       const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
    253       if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
    254         return false;
    255 
    256       RawContextCPU cpu;
    257       my_memset(&cpu, 0, sizeof(RawContextCPU));
    258 #if !defined(__ARM_EABI__) && !defined(__mips__)
    259       UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
    260 #else
    261       UContextReader::FillCPUContext(&cpu, ucontext_);
    262 #endif
    263       if (stack_copy)
    264         SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
    265       DumpCPUState(&cpu);
    266     }
    267     return true;
    268   }
    269 
    270   void DumpCPUState(RawContextCPU* cpu) {
    271     LogAppend("C ");
    272     LogAppend(cpu, sizeof(*cpu));
    273     LogCommitLine();
    274   }
    275 
    276   // If there is caller-provided information about this mapping
    277   // in the mapping_list_ list, return true. Otherwise, return false.
    278   bool HaveMappingInfo(const MappingInfo& mapping) {
    279     for (MappingList::const_iterator iter = mapping_list_.begin();
    280          iter != mapping_list_.end();
    281          ++iter) {
    282       // Ignore any mappings that are wholly contained within
    283       // mappings in the mapping_info_ list.
    284       if (mapping.start_addr >= iter->first.start_addr &&
    285           (mapping.start_addr + mapping.size) <=
    286               (iter->first.start_addr + iter->first.size)) {
    287         return true;
    288       }
    289     }
    290     return false;
    291   }
    292 
    293   // Dump information about the provided |mapping|. If |identifier| is non-NULL,
    294   // use it instead of calculating a file ID from the mapping.
    295   void DumpModule(const MappingInfo& mapping,
    296                   bool member,
    297                   unsigned int mapping_id,
    298                   const uint8_t* identifier) {
    299     MDGUID module_identifier;
    300     if (identifier) {
    301       // GUID was provided by caller.
    302       my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
    303     } else {
    304       dumper_->ElfFileIdentifierForMapping(
    305           mapping,
    306           member,
    307           mapping_id,
    308           reinterpret_cast<uint8_t*>(&module_identifier));
    309     }
    310 
    311     char file_name[NAME_MAX];
    312     char file_path[NAME_MAX];
    313     LinuxDumper::GetMappingEffectiveNameAndPath(
    314         mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
    315 
    316     LogAppend("M ");
    317     LogAppend(static_cast<uintptr_t>(mapping.start_addr));
    318     LogAppend(" ");
    319     LogAppend(mapping.offset);
    320     LogAppend(" ");
    321     LogAppend(mapping.size);
    322     LogAppend(" ");
    323     LogAppend(module_identifier.data1);
    324     LogAppend(module_identifier.data2);
    325     LogAppend(module_identifier.data3);
    326     LogAppend(module_identifier.data4[0]);
    327     LogAppend(module_identifier.data4[1]);
    328     LogAppend(module_identifier.data4[2]);
    329     LogAppend(module_identifier.data4[3]);
    330     LogAppend(module_identifier.data4[4]);
    331     LogAppend(module_identifier.data4[5]);
    332     LogAppend(module_identifier.data4[6]);
    333     LogAppend(module_identifier.data4[7]);
    334     LogAppend("0 ");  // Age is always 0 on Linux.
    335     LogAppend(file_name);
    336     LogCommitLine();
    337   }
    338 
    339   // Write information about the mappings in effect.
    340   bool DumpMappings() {
    341     // First write all the mappings from the dumper
    342     for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
    343       const MappingInfo& mapping = *dumper_->mappings()[i];
    344       if (mapping.name[0] == 0 ||  // only want modules with filenames.
    345           !mapping.exec ||  // only want executable mappings.
    346           mapping.size < 4096 || // too small to get a signature for.
    347           HaveMappingInfo(mapping)) {
    348         continue;
    349       }
    350 
    351       DumpModule(mapping, true, i, NULL);
    352     }
    353     // Next write all the mappings provided by the caller
    354     for (MappingList::const_iterator iter = mapping_list_.begin();
    355          iter != mapping_list_.end();
    356          ++iter) {
    357       DumpModule(iter->first, false, 0, iter->second);
    358     }
    359     return true;
    360   }
    361 
    362   void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
    363 
    364   const struct ucontext* const ucontext_;
    365 #if !defined(__ARM_EABI__) && !defined(__mips__)
    366   const google_breakpad::fpstate_t* const float_state_;
    367 #endif
    368   LinuxDumper* dumper_;
    369   const MappingList& mapping_list_;
    370   char* log_line_;
    371 };
    372 }  // namespace
    373 
    374 namespace google_breakpad {
    375 
    376 bool WriteMicrodump(pid_t crashing_process,
    377                     const void* blob,
    378                     size_t blob_size,
    379                     const MappingList& mappings) {
    380   LinuxPtraceDumper dumper(crashing_process);
    381   const ExceptionHandler::CrashContext* context = NULL;
    382   if (blob) {
    383     if (blob_size != sizeof(ExceptionHandler::CrashContext))
    384       return false;
    385     context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
    386     dumper.set_crash_address(
    387         reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
    388     dumper.set_crash_signal(context->siginfo.si_signo);
    389     dumper.set_crash_thread(context->tid);
    390   }
    391   MicrodumpWriter writer(context, mappings, &dumper);
    392   if (!writer.Init())
    393     return false;
    394   return writer.Dump();
    395 }
    396 
    397 }  // namespace google_breakpad
    398