Home | History | Annotate | Download | only in processor
      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 // microdump.cc: A microdump reader.
     31 //
     32 // See microdump.h for documentation.
     33 
     34 #include "google_breakpad/processor/microdump.h"
     35 
     36 #include <stdio.h>
     37 #include <string.h>
     38 
     39 #include <memory>
     40 #include <sstream>
     41 #include <string>
     42 #include <vector>
     43 
     44 #include "google_breakpad/common/minidump_cpu_arm.h"
     45 #include "google_breakpad/processor/code_module.h"
     46 #include "processor/basic_code_module.h"
     47 #include "processor/linked_ptr.h"
     48 #include "processor/logging.h"
     49 #include "processor/range_map-inl.h"
     50 
     51 namespace {
     52 static const char kGoogleBreakpadKey[] = "google-breakpad";
     53 static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----";
     54 static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----";
     55 static const char kOsKey[] = ": O ";
     56 static const char kCpuKey[] = ": C ";
     57 static const char kMmapKey[] = ": M ";
     58 static const char kStackKey[] = ": S ";
     59 static const char kStackFirstLineKey[] = ": S 0 ";
     60 static const char kArmArchitecture[] = "arm";
     61 static const char kArm64Architecture[] = "arm64";
     62 
     63 template<typename T>
     64 T HexStrToL(const string& str) {
     65   uint64_t res = 0;
     66   std::istringstream ss(str);
     67   ss >> std::hex >> res;
     68   return static_cast<T>(res);
     69 }
     70 
     71 std::vector<uint8_t> ParseHexBuf(const string& str) {
     72   std::vector<uint8_t> buf;
     73   for (size_t i = 0; i < str.length(); i += 2) {
     74     buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2)));
     75   }
     76   return buf;
     77 }
     78 
     79 }  // namespace
     80 
     81 namespace google_breakpad {
     82 
     83 //
     84 // MicrodumpModules
     85 //
     86 
     87 void MicrodumpModules::Add(const CodeModule* module) {
     88   linked_ptr<const CodeModule> module_ptr(module);
     89   if (!map_->StoreRange(module->base_address(), module->size(), module_ptr)) {
     90     BPLOG(ERROR) << "Module " << module->code_file() <<
     91                     " could not be stored";
     92   }
     93 }
     94 
     95 
     96 //
     97 // MicrodumpContext
     98 //
     99 
    100 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
    101   DumpContext::SetContextFlags(MD_CONTEXT_ARM);
    102   DumpContext::SetContextARM(arm);
    103   valid_ = true;
    104 }
    105 
    106 void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) {
    107   DumpContext::SetContextFlags(MD_CONTEXT_ARM64);
    108   DumpContext::SetContextARM64(arm64);
    109   valid_ = true;
    110 }
    111 
    112 
    113 //
    114 // MicrodumpMemoryRegion
    115 //
    116 
    117 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
    118 
    119 void MicrodumpMemoryRegion::Init(uint64_t base_address,
    120                                  const std::vector<uint8_t>& contents) {
    121   base_address_ = base_address;
    122   contents_ = contents;
    123 }
    124 
    125 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
    126 
    127 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
    128 
    129 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
    130                                                uint8_t* value) const {
    131   return GetMemoryLittleEndian(address, value);
    132 }
    133 
    134 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
    135                                                uint16_t* value) const {
    136   return GetMemoryLittleEndian(address, value);
    137 }
    138 
    139 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
    140                                                uint32_t* value) const {
    141   return GetMemoryLittleEndian(address, value);
    142 }
    143 
    144 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
    145                                                uint64_t* value) const {
    146   return GetMemoryLittleEndian(address, value);
    147 }
    148 
    149 template<typename ValueType>
    150 bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address,
    151                                                   ValueType* value) const {
    152   if (address < base_address_ ||
    153       address - base_address_ + sizeof(ValueType) > contents_.size())
    154     return false;
    155   ValueType v = 0;
    156   uint64_t start = address - base_address_;
    157   // The loop condition is odd, but it's correct for size_t.
    158   for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--)
    159     v = (v << 8) | static_cast<uint8_t>(contents_[start + i]);
    160   *value = v;
    161   return true;
    162 }
    163 
    164 void MicrodumpMemoryRegion::Print() const {
    165   // Not reached, just needed to honor the base class contract.
    166   assert(false);
    167 }
    168 
    169 //
    170 // Microdump
    171 //
    172 Microdump::Microdump(const string& contents)
    173   : context_(new MicrodumpContext()),
    174     stack_region_(new MicrodumpMemoryRegion()),
    175     modules_(new MicrodumpModules()),
    176     system_info_(new SystemInfo()) {
    177   assert(!contents.empty());
    178 
    179   bool in_microdump = false;
    180   string line;
    181   uint64_t stack_start = 0;
    182   std::vector<uint8_t> stack_content;
    183   string arch;
    184 
    185   std::istringstream stream(contents);
    186   while (std::getline(stream, line)) {
    187     if (line.find(kGoogleBreakpadKey) == string::npos) {
    188       continue;
    189     }
    190     if (line.find(kMicrodumpBegin) != string::npos) {
    191       in_microdump = true;
    192       continue;
    193     }
    194     if (line.find(kMicrodumpEnd) != string::npos) {
    195       break;
    196     }
    197 
    198     if (!in_microdump) {
    199       continue;
    200     }
    201 
    202     size_t pos;
    203     if ((pos = line.find(kOsKey)) != string::npos) {
    204       string os_str(line, pos + strlen(kOsKey));
    205       std::istringstream os_tokens(os_str);
    206       string os_id;
    207       string num_cpus;
    208       string os_version;
    209       // This reflect the actual HW arch and might not match the arch emulated
    210       // for the execution (e.g., running a 32-bit binary on a 64-bit cpu).
    211       string hw_arch;
    212 
    213       os_tokens >> os_id;
    214       os_tokens >> arch;
    215       os_tokens >> num_cpus;
    216       os_tokens >> hw_arch;
    217       std::getline(os_tokens, os_version);
    218       os_version.erase(0, 1);  // remove leading space.
    219 
    220       system_info_->cpu = hw_arch;
    221       system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus);
    222       system_info_->os_version = os_version;
    223 
    224       if (os_id == "L") {
    225         system_info_->os = "Linux";
    226         system_info_->os_short = "linux";
    227       } else if (os_id == "A") {
    228         system_info_->os = "Android";
    229         system_info_->os_short = "android";
    230       }
    231 
    232       // OS line also contains release and version for future use.
    233     } else if ((pos = line.find(kStackKey)) != string::npos) {
    234       if (line.find(kStackFirstLineKey) != string::npos) {
    235         // The first line of the stack (S 0 stack header) provides the value of
    236         // the stack pointer, the start address of the stack being dumped and
    237         // the length of the stack. We could use it in future to double check
    238         // that we received all the stack as expected.
    239         continue;
    240       }
    241       string stack_str(line, pos + strlen(kStackKey));
    242       std::istringstream stack_tokens(stack_str);
    243       string start_addr_str;
    244       string raw_content;
    245       stack_tokens >> start_addr_str;
    246       stack_tokens >> raw_content;
    247       uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str);
    248 
    249       if (stack_start != 0) {
    250         // Verify that the stack chunks in the microdump are contiguous.
    251         assert(start_addr == stack_start + stack_content.size());
    252       } else {
    253         stack_start = start_addr;
    254       }
    255       std::vector<uint8_t> chunk = ParseHexBuf(raw_content);
    256       stack_content.insert(stack_content.end(), chunk.begin(), chunk.end());
    257 
    258     } else if ((pos = line.find(kCpuKey)) != string::npos) {
    259       string cpu_state_str(line, pos + strlen(kCpuKey));
    260       std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str);
    261       if (strcmp(arch.c_str(), kArmArchitecture) == 0) {
    262         if (cpu_state_raw.size() != sizeof(MDRawContextARM)) {
    263           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() <<
    264               " bytes instead of " << sizeof(MDRawContextARM) << std::endl;
    265           continue;
    266         }
    267         MDRawContextARM* arm = new MDRawContextARM();
    268         memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
    269         context_->SetContextARM(arm);
    270       } else if (strcmp(arch.c_str(), kArm64Architecture) == 0) {
    271         if (cpu_state_raw.size() != sizeof(MDRawContextARM64)) {
    272           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() <<
    273               " bytes instead of " << sizeof(MDRawContextARM64) << std::endl;
    274           continue;
    275         }
    276         MDRawContextARM64* arm = new MDRawContextARM64();
    277         memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
    278         context_->SetContextARM64(arm);
    279       } else {
    280         std::cerr << "Unsupported architecture: " << arch << std::endl;
    281       }
    282     } else if ((pos = line.find(kMmapKey)) != string::npos) {
    283       string mmap_line(line, pos + strlen(kMmapKey));
    284       std::istringstream mmap_tokens(mmap_line);
    285       string addr, offset, size, identifier, filename;
    286       mmap_tokens >> addr;
    287       mmap_tokens >> offset;
    288       mmap_tokens >> size;
    289       mmap_tokens >> identifier;
    290       mmap_tokens >> filename;
    291 
    292       modules_->Add(new BasicCodeModule(
    293           HexStrToL<uint64_t>(addr),  // base_address
    294           HexStrToL<uint64_t>(size),  // size
    295           filename,                   // code_file
    296           identifier,                 // code_identifier
    297           filename,                   // debug_file
    298           identifier,                 // debug_identifier
    299           ""));                       // version
    300     }
    301   }
    302   stack_region_->Init(stack_start, stack_content);
    303 }
    304 
    305 }  // namespace google_breakpad
    306 
    307