|  | // 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. | 
|  |  | 
|  | // microdump.cc: A microdump reader. | 
|  | // | 
|  | // See microdump.h for documentation. | 
|  |  | 
|  | #include "google_breakpad/processor/microdump.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "google_breakpad/common/minidump_cpu_arm.h" | 
|  | #include "google_breakpad/processor/code_module.h" | 
|  | #include "processor/basic_code_module.h" | 
|  | #include "processor/convert_old_arm64_context.h" | 
|  | #include "processor/linked_ptr.h" | 
|  | #include "processor/logging.h" | 
|  | #include "processor/range_map-inl.h" | 
|  |  | 
|  | namespace { | 
|  | static const char kGoogleBreakpadKey[] = "google-breakpad"; | 
|  | static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----"; | 
|  | static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----"; | 
|  | static const char kOsKey[] = ": O "; | 
|  | static const char kCpuKey[] = ": C "; | 
|  | static const char kCrashReasonKey[] = ": R "; | 
|  | static const char kGpuKey[] = ": G "; | 
|  | static const char kMmapKey[] = ": M "; | 
|  | static const char kStackKey[] = ": S "; | 
|  | static const char kStackFirstLineKey[] = ": S 0 "; | 
|  | static const char kArmArchitecture[] = "arm"; | 
|  | static const char kArm64Architecture[] = "arm64"; | 
|  | static const char kX86Architecture[] = "x86"; | 
|  | static const char kMipsArchitecture[] = "mips"; | 
|  | static const char kMips64Architecture[] = "mips64"; | 
|  | static const char kGpuUnknown[] = "UNKNOWN"; | 
|  |  | 
|  | template<typename T> | 
|  | T HexStrToL(const string& str) { | 
|  | uint64_t res = 0; | 
|  | std::istringstream ss(str); | 
|  | ss >> std::hex >> res; | 
|  | return static_cast<T>(res); | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> ParseHexBuf(const string& str) { | 
|  | std::vector<uint8_t> buf; | 
|  | for (size_t i = 0; i < str.length(); i += 2) { | 
|  | buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2))); | 
|  | } | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | bool GetLine(std::istringstream* istream, string* str) { | 
|  | if (std::getline(*istream, *str)) { | 
|  | // Trim any trailing newline from the end of the line. Allows us | 
|  | // to seamlessly handle both Windows/DOS and Unix formatted input. The | 
|  | // adb tool generally writes logcat dumps in Windows/DOS format. | 
|  | if (!str->empty() && str->at(str->size() - 1) == '\r') { | 
|  | str->erase(str->size() - 1); | 
|  | } | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace google_breakpad { | 
|  |  | 
|  | // | 
|  | // MicrodumpModules | 
|  | // | 
|  |  | 
|  | void MicrodumpModules::Add(const CodeModule* module) { | 
|  | linked_ptr<const CodeModule> module_ptr(module); | 
|  | if (!map_.StoreRange(module->base_address(), module->size(), module_ptr)) { | 
|  | BPLOG(ERROR) << "Module " << module->code_file() << | 
|  | " could not be stored"; | 
|  | } | 
|  | } | 
|  |  | 
|  | void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) { | 
|  | map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper | 
|  | : MergeRangeStrategy::kExclusiveRanges); | 
|  | } | 
|  |  | 
|  | // | 
|  | // MicrodumpContext | 
|  | // | 
|  |  | 
|  | void MicrodumpContext::SetContextARM(MDRawContextARM* arm) { | 
|  | DumpContext::SetContextFlags(MD_CONTEXT_ARM); | 
|  | DumpContext::SetContextARM(arm); | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) { | 
|  | DumpContext::SetContextFlags(MD_CONTEXT_ARM64); | 
|  | DumpContext::SetContextARM64(arm64); | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | void MicrodumpContext::SetContextX86(MDRawContextX86* x86) { | 
|  | DumpContext::SetContextFlags(MD_CONTEXT_X86); | 
|  | DumpContext::SetContextX86(x86); | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) { | 
|  | DumpContext::SetContextFlags(MD_CONTEXT_MIPS); | 
|  | DumpContext::SetContextMIPS(mips32); | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  | void MicrodumpContext::SetContextMIPS64(MDRawContextMIPS* mips64) { | 
|  | DumpContext::SetContextFlags(MD_CONTEXT_MIPS64); | 
|  | DumpContext::SetContextMIPS(mips64); | 
|  | valid_ = true; | 
|  | } | 
|  |  | 
|  |  | 
|  | // | 
|  | // MicrodumpMemoryRegion | 
|  | // | 
|  |  | 
|  | MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { } | 
|  |  | 
|  | void MicrodumpMemoryRegion::Init(uint64_t base_address, | 
|  | const std::vector<uint8_t>& contents) { | 
|  | base_address_ = base_address; | 
|  | contents_ = contents; | 
|  | } | 
|  |  | 
|  | uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; } | 
|  |  | 
|  | uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); } | 
|  |  | 
|  | bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 
|  | uint8_t* value) const { | 
|  | return GetMemoryLittleEndian(address, value); | 
|  | } | 
|  |  | 
|  | bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 
|  | uint16_t* value) const { | 
|  | return GetMemoryLittleEndian(address, value); | 
|  | } | 
|  |  | 
|  | bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 
|  | uint32_t* value) const { | 
|  | return GetMemoryLittleEndian(address, value); | 
|  | } | 
|  |  | 
|  | bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 
|  | uint64_t* value) const { | 
|  | return GetMemoryLittleEndian(address, value); | 
|  | } | 
|  |  | 
|  | template<typename ValueType> | 
|  | bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address, | 
|  | ValueType* value) const { | 
|  | if (address < base_address_ || | 
|  | address - base_address_ + sizeof(ValueType) > contents_.size()) | 
|  | return false; | 
|  | ValueType v = 0; | 
|  | uint64_t start = address - base_address_; | 
|  | // The loop condition is odd, but it's correct for size_t. | 
|  | for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) | 
|  | v = (v << 8) | static_cast<uint8_t>(contents_[start + i]); | 
|  | *value = v; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void MicrodumpMemoryRegion::Print() const { | 
|  | // Not reached, just needed to honor the base class contract. | 
|  | assert(false); | 
|  | } | 
|  |  | 
|  | // | 
|  | // Microdump | 
|  | // | 
|  | Microdump::Microdump(const string& contents) | 
|  | : context_(new MicrodumpContext()), | 
|  | stack_region_(new MicrodumpMemoryRegion()), | 
|  | modules_(new MicrodumpModules()), | 
|  | system_info_(new SystemInfo()), | 
|  | crash_reason_(), | 
|  | crash_address_(0u) { | 
|  | assert(!contents.empty()); | 
|  |  | 
|  | bool in_microdump = false; | 
|  | string line; | 
|  | uint64_t stack_start = 0; | 
|  | std::vector<uint8_t> stack_content; | 
|  | string arch; | 
|  |  | 
|  | std::istringstream stream(contents); | 
|  | while (GetLine(&stream, &line)) { | 
|  | if (line.find(kGoogleBreakpadKey) == string::npos) { | 
|  | continue; | 
|  | } | 
|  | if (line.find(kMicrodumpBegin) != string::npos) { | 
|  | in_microdump = true; | 
|  | continue; | 
|  | } | 
|  | if (!in_microdump) { | 
|  | continue; | 
|  | } | 
|  | if (line.find(kMicrodumpEnd) != string::npos) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | size_t pos; | 
|  | if ((pos = line.find(kOsKey)) != string::npos) { | 
|  | string os_str(line, pos + strlen(kOsKey)); | 
|  | std::istringstream os_tokens(os_str); | 
|  | string os_id; | 
|  | string num_cpus; | 
|  | string os_version; | 
|  | // This reflect the actual HW arch and might not match the arch emulated | 
|  | // for the execution (e.g., running a 32-bit binary on a 64-bit cpu). | 
|  | string hw_arch; | 
|  |  | 
|  | os_tokens >> os_id; | 
|  | os_tokens >> arch; | 
|  | os_tokens >> num_cpus; | 
|  | os_tokens >> hw_arch; | 
|  | GetLine(&os_tokens, &os_version); | 
|  | os_version.erase(0, 1);  // remove leading space. | 
|  |  | 
|  | system_info_->cpu = arch; | 
|  | system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus); | 
|  | system_info_->os_version = os_version; | 
|  |  | 
|  | if (os_id == "L") { | 
|  | system_info_->os = "Linux"; | 
|  | system_info_->os_short = "linux"; | 
|  | } else if (os_id == "A") { | 
|  | system_info_->os = "Android"; | 
|  | system_info_->os_short = "android"; | 
|  | modules_->SetEnableModuleShrink(true); | 
|  | } | 
|  |  | 
|  | // OS line also contains release and version for future use. | 
|  | } else if ((pos = line.find(kStackKey)) != string::npos) { | 
|  | if (line.find(kStackFirstLineKey) != string::npos) { | 
|  | // The first line of the stack (S 0 stack header) provides the value of | 
|  | // the stack pointer, the start address of the stack being dumped and | 
|  | // the length of the stack. We could use it in future to double check | 
|  | // that we received all the stack as expected. | 
|  | continue; | 
|  | } | 
|  | string stack_str(line, pos + strlen(kStackKey)); | 
|  | std::istringstream stack_tokens(stack_str); | 
|  | string start_addr_str; | 
|  | string raw_content; | 
|  | stack_tokens >> start_addr_str; | 
|  | stack_tokens >> raw_content; | 
|  | uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str); | 
|  |  | 
|  | if (stack_start != 0) { | 
|  | // Verify that the stack chunks in the microdump are contiguous. | 
|  | assert(start_addr == stack_start + stack_content.size()); | 
|  | } else { | 
|  | stack_start = start_addr; | 
|  | } | 
|  | std::vector<uint8_t> chunk = ParseHexBuf(raw_content); | 
|  | stack_content.insert(stack_content.end(), chunk.begin(), chunk.end()); | 
|  |  | 
|  | } else if ((pos = line.find(kCpuKey)) != string::npos) { | 
|  | string cpu_state_str(line, pos + strlen(kCpuKey)); | 
|  | std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str); | 
|  | if (strcmp(arch.c_str(), kArmArchitecture) == 0) { | 
|  | if (cpu_state_raw.size() != sizeof(MDRawContextARM)) { | 
|  | std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() | 
|  | << " bytes instead of " << sizeof(MDRawContextARM) | 
|  | << std::endl; | 
|  | continue; | 
|  | } | 
|  | MDRawContextARM* arm = new MDRawContextARM(); | 
|  | memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | context_->SetContextARM(arm); | 
|  | } else if (strcmp(arch.c_str(), kArm64Architecture) == 0) { | 
|  | if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) { | 
|  | MDRawContextARM64* arm = new MDRawContextARM64(); | 
|  | memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | context_->SetContextARM64(arm); | 
|  | } else if (cpu_state_raw.size() == sizeof(MDRawContextARM64_Old)) { | 
|  | MDRawContextARM64_Old old_arm; | 
|  | memcpy(&old_arm, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | MDRawContextARM64* new_arm = new MDRawContextARM64(); | 
|  | ConvertOldARM64Context(old_arm, new_arm); | 
|  | context_->SetContextARM64(new_arm); | 
|  | } else { | 
|  | std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() | 
|  | << " bytes instead of " << sizeof(MDRawContextARM64) | 
|  | << std::endl; | 
|  | continue; | 
|  | } | 
|  | } else if (strcmp(arch.c_str(), kX86Architecture) == 0) { | 
|  | if (cpu_state_raw.size() != sizeof(MDRawContextX86)) { | 
|  | std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() | 
|  | << " bytes instead of " << sizeof(MDRawContextX86) | 
|  | << std::endl; | 
|  | continue; | 
|  | } | 
|  | MDRawContextX86* x86 = new MDRawContextX86(); | 
|  | memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | context_->SetContextX86(x86); | 
|  | } else if (strcmp(arch.c_str(), kMipsArchitecture) == 0) { | 
|  | if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { | 
|  | std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() | 
|  | << " bytes instead of " << sizeof(MDRawContextMIPS) | 
|  | << std::endl; | 
|  | continue; | 
|  | } | 
|  | MDRawContextMIPS* mips32 = new MDRawContextMIPS(); | 
|  | memcpy(mips32, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | context_->SetContextMIPS(mips32); | 
|  | } else if (strcmp(arch.c_str(), kMips64Architecture) == 0) { | 
|  | if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { | 
|  | std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() | 
|  | << " bytes instead of " << sizeof(MDRawContextMIPS) | 
|  | << std::endl; | 
|  | continue; | 
|  | } | 
|  | MDRawContextMIPS* mips64 = new MDRawContextMIPS(); | 
|  | memcpy(mips64, &cpu_state_raw[0], cpu_state_raw.size()); | 
|  | context_->SetContextMIPS64(mips64); | 
|  | } else { | 
|  | std::cerr << "Unsupported architecture: " << arch << std::endl; | 
|  | } | 
|  | } else if ((pos = line.find(kCrashReasonKey)) != string::npos) { | 
|  | string crash_reason_str(line, pos + strlen(kCrashReasonKey)); | 
|  | std::istringstream crash_reason_tokens(crash_reason_str); | 
|  | string signal; | 
|  | string address; | 
|  | crash_reason_tokens >> signal; | 
|  | crash_reason_tokens >> crash_reason_; | 
|  | crash_reason_tokens >> address; | 
|  | crash_address_ = HexStrToL<uint64_t>(address); | 
|  | } else if ((pos = line.find(kGpuKey)) != string::npos) { | 
|  | string gpu_str(line, pos + strlen(kGpuKey)); | 
|  | if (strcmp(gpu_str.c_str(), kGpuUnknown) != 0) { | 
|  | std::istringstream gpu_tokens(gpu_str); | 
|  | std::getline(gpu_tokens, system_info_->gl_version, '|'); | 
|  | std::getline(gpu_tokens, system_info_->gl_vendor, '|'); | 
|  | std::getline(gpu_tokens, system_info_->gl_renderer, '|'); | 
|  | } | 
|  | } else if ((pos = line.find(kMmapKey)) != string::npos) { | 
|  | string mmap_line(line, pos + strlen(kMmapKey)); | 
|  | std::istringstream mmap_tokens(mmap_line); | 
|  | string addr, offset, size, identifier, filename; | 
|  | mmap_tokens >> addr; | 
|  | mmap_tokens >> offset; | 
|  | mmap_tokens >> size; | 
|  | mmap_tokens >> identifier; | 
|  | mmap_tokens >> filename; | 
|  |  | 
|  | modules_->Add(new BasicCodeModule( | 
|  | HexStrToL<uint64_t>(addr),  // base_address | 
|  | HexStrToL<uint64_t>(size),  // size | 
|  | filename,                   // code_file | 
|  | identifier,                 // code_identifier | 
|  | filename,                   // debug_file | 
|  | identifier,                 // debug_identifier | 
|  | ""));                       // version | 
|  | } | 
|  | } | 
|  | stack_region_->Init(stack_start, stack_content); | 
|  | } | 
|  |  | 
|  | }  // namespace google_breakpad |