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