microdump.cc
1 // Copyright 2014 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // microdump.cc: A microdump reader. 30 // 31 // See microdump.h for documentation. 32 33 #ifdef HAVE_CONFIG_H 34 #include <config.h> // Must come first 35 #endif 36 37 #include "google_breakpad/processor/microdump.h" 38 39 #include <stdio.h> 40 #include <string.h> 41 42 #include <memory> 43 #include <sstream> 44 #include <string> 45 #include <vector> 46 47 #include "google_breakpad/common/minidump_cpu_arm.h" 48 #include "google_breakpad/processor/code_module.h" 49 #include "processor/basic_code_module.h" 50 #include "processor/convert_old_arm64_context.h" 51 #include "processor/linked_ptr.h" 52 #include "processor/logging.h" 53 #include "processor/range_map-inl.h" 54 55 namespace { 56 static const char kGoogleBreakpadKey[] = "google-breakpad"; 57 static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----"; 58 static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----"; 59 static const char kOsKey[] = ": O "; 60 static const char kCpuKey[] = ": C "; 61 static const char kCrashReasonKey[] = ": R "; 62 static const char kGpuKey[] = ": G "; 63 static const char kMmapKey[] = ": M "; 64 static const char kStackKey[] = ": S "; 65 static const char kStackFirstLineKey[] = ": S 0 "; 66 static const char kArmArchitecture[] = "arm"; 67 static const char kArm64Architecture[] = "arm64"; 68 static const char kX86Architecture[] = "x86"; 69 static const char kMipsArchitecture[] = "mips"; 70 static const char kMips64Architecture[] = "mips64"; 71 static const char kGpuUnknown[] = "UNKNOWN"; 72 73 template<typename T> 74 T HexStrToL(const string& str) { 75 uint64_t res = 0; 76 std::istringstream ss(str); 77 ss >> std::hex >> res; 78 return static_cast<T>(res); 79 } 80 81 std::vector<uint8_t> ParseHexBuf(const string& str) { 82 std::vector<uint8_t> buf; 83 for (size_t i = 0; i < str.length(); i += 2) { 84 buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2))); 85 } 86 return buf; 87 } 88 89 bool GetLine(std::istringstream* istream, string* str) { 90 if (std::getline(*istream, *str)) { 91 // Trim any trailing newline from the end of the line. Allows us 92 // to seamlessly handle both Windows/DOS and Unix formatted input. The 93 // adb tool generally writes logcat dumps in Windows/DOS format. 94 if (!str->empty() && str->at(str->size() - 1) == '\r') { 95 str->erase(str->size() - 1); 96 } 97 return true; 98 } 99 return false; 100 } 101 102 } // namespace 103 104 namespace google_breakpad { 105 106 // 107 // MicrodumpModules 108 // 109 110 void MicrodumpModules::Add(const CodeModule* module) { 111 linked_ptr<const CodeModule> module_ptr(module); 112 if (!map_.StoreRange(module->base_address(), module->size(), module_ptr)) { 113 BPLOG(ERROR) << "Module " << module->code_file() << 114 " could not be stored"; 115 } 116 } 117 118 void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) { 119 map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper 120 : MergeRangeStrategy::kExclusiveRanges); 121 } 122 123 // 124 // MicrodumpContext 125 // 126 127 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) { 128 DumpContext::SetContextFlags(MD_CONTEXT_ARM); 129 DumpContext::SetContextARM(arm); 130 valid_ = true; 131 } 132 133 void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) { 134 DumpContext::SetContextFlags(MD_CONTEXT_ARM64); 135 DumpContext::SetContextARM64(arm64); 136 valid_ = true; 137 } 138 139 void MicrodumpContext::SetContextX86(MDRawContextX86* x86) { 140 DumpContext::SetContextFlags(MD_CONTEXT_X86); 141 DumpContext::SetContextX86(x86); 142 valid_ = true; 143 } 144 145 void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) { 146 DumpContext::SetContextFlags(MD_CONTEXT_MIPS); 147 DumpContext::SetContextMIPS(mips32); 148 valid_ = true; 149 } 150 151 void MicrodumpContext::SetContextMIPS64(MDRawContextMIPS* mips64) { 152 DumpContext::SetContextFlags(MD_CONTEXT_MIPS64); 153 DumpContext::SetContextMIPS(mips64); 154 valid_ = true; 155 } 156 157 158 // 159 // MicrodumpMemoryRegion 160 // 161 162 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { } 163 164 void MicrodumpMemoryRegion::Init(uint64_t base_address, 165 const std::vector<uint8_t>& contents) { 166 base_address_ = base_address; 167 contents_ = contents; 168 } 169 170 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; } 171 172 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); } 173 174 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, 175 uint8_t* value) const { 176 return GetMemoryLittleEndian(address, value); 177 } 178 179 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, 180 uint16_t* value) const { 181 return GetMemoryLittleEndian(address, value); 182 } 183 184 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, 185 uint32_t* value) const { 186 return GetMemoryLittleEndian(address, value); 187 } 188 189 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, 190 uint64_t* value) const { 191 return GetMemoryLittleEndian(address, value); 192 } 193 194 template<typename ValueType> 195 bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address, 196 ValueType* value) const { 197 if (address < base_address_ || 198 address - base_address_ + sizeof(ValueType) > contents_.size()) 199 return false; 200 ValueType v = 0; 201 uint64_t start = address - base_address_; 202 // The loop condition is odd, but it's correct for size_t. 203 for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) 204 v = (v << 8) | static_cast<uint8_t>(contents_[start + i]); 205 *value = v; 206 return true; 207 } 208 209 void MicrodumpMemoryRegion::Print() const { 210 // Not reached, just needed to honor the base class contract. 211 assert(false); 212 } 213 214 // 215 // Microdump 216 // 217 Microdump::Microdump(const string& contents) 218 : context_(new MicrodumpContext()), 219 stack_region_(new MicrodumpMemoryRegion()), 220 modules_(new MicrodumpModules()), 221 system_info_(new SystemInfo()), 222 crash_reason_(), 223 crash_address_(0u) { 224 assert(!contents.empty()); 225 226 bool in_microdump = false; 227 string line; 228 uint64_t stack_start = 0; 229 std::vector<uint8_t> stack_content; 230 string arch; 231 232 std::istringstream stream(contents); 233 while (GetLine(&stream, &line)) { 234 if (line.find(kGoogleBreakpadKey) == string::npos) { 235 continue; 236 } 237 if (line.find(kMicrodumpBegin) != string::npos) { 238 in_microdump = true; 239 continue; 240 } 241 if (!in_microdump) { 242 continue; 243 } 244 if (line.find(kMicrodumpEnd) != string::npos) { 245 break; 246 } 247 248 size_t pos; 249 if ((pos = line.find(kOsKey)) != string::npos) { 250 string os_str(line, pos + strlen(kOsKey)); 251 std::istringstream os_tokens(os_str); 252 string os_id; 253 string num_cpus; 254 string os_version; 255 // This reflect the actual HW arch and might not match the arch emulated 256 // for the execution (e.g., running a 32-bit binary on a 64-bit cpu). 257 string hw_arch; 258 259 os_tokens >> os_id; 260 os_tokens >> arch; 261 os_tokens >> num_cpus; 262 os_tokens >> hw_arch; 263 GetLine(&os_tokens, &os_version); 264 os_version.erase(0, 1); // remove leading space. 265 266 system_info_->cpu = arch; 267 system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus); 268 system_info_->os_version = os_version; 269 270 if (os_id == "L") { 271 system_info_->os = "Linux"; 272 system_info_->os_short = "linux"; 273 } else if (os_id == "A") { 274 system_info_->os = "Android"; 275 system_info_->os_short = "android"; 276 modules_->SetEnableModuleShrink(true); 277 } 278 279 // OS line also contains release and version for future use. 280 } else if ((pos = line.find(kStackKey)) != string::npos) { 281 if (line.find(kStackFirstLineKey) != string::npos) { 282 // The first line of the stack (S 0 stack header) provides the value of 283 // the stack pointer, the start address of the stack being dumped and 284 // the length of the stack. We could use it in future to double check 285 // that we received all the stack as expected. 286 continue; 287 } 288 string stack_str(line, pos + strlen(kStackKey)); 289 std::istringstream stack_tokens(stack_str); 290 string start_addr_str; 291 string raw_content; 292 stack_tokens >> start_addr_str; 293 stack_tokens >> raw_content; 294 uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str); 295 296 if (stack_start != 0) { 297 // Verify that the stack chunks in the microdump are contiguous. 298 assert(start_addr == stack_start + stack_content.size()); 299 } else { 300 stack_start = start_addr; 301 } 302 std::vector<uint8_t> chunk = ParseHexBuf(raw_content); 303 stack_content.insert(stack_content.end(), chunk.begin(), chunk.end()); 304 305 } else if ((pos = line.find(kCpuKey)) != string::npos) { 306 string cpu_state_str(line, pos + strlen(kCpuKey)); 307 std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str); 308 if (strcmp(arch.c_str(), kArmArchitecture) == 0) { 309 if (cpu_state_raw.size() != sizeof(MDRawContextARM)) { 310 std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() 311 << " bytes instead of " << sizeof(MDRawContextARM) 312 << std::endl; 313 continue; 314 } 315 MDRawContextARM* arm = new MDRawContextARM(); 316 memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); 317 context_->SetContextARM(arm); 318 } else if (strcmp(arch.c_str(), kArm64Architecture) == 0) { 319 if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) { 320 MDRawContextARM64* arm = new MDRawContextARM64(); 321 memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); 322 context_->SetContextARM64(arm); 323 } else if (cpu_state_raw.size() == sizeof(MDRawContextARM64_Old)) { 324 MDRawContextARM64_Old old_arm; 325 memcpy(&old_arm, &cpu_state_raw[0], cpu_state_raw.size()); 326 MDRawContextARM64* new_arm = new MDRawContextARM64(); 327 ConvertOldARM64Context(old_arm, new_arm); 328 context_->SetContextARM64(new_arm); 329 } else { 330 std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() 331 << " bytes instead of " << sizeof(MDRawContextARM64) 332 << std::endl; 333 continue; 334 } 335 } else if (strcmp(arch.c_str(), kX86Architecture) == 0) { 336 if (cpu_state_raw.size() != sizeof(MDRawContextX86)) { 337 std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() 338 << " bytes instead of " << sizeof(MDRawContextX86) 339 << std::endl; 340 continue; 341 } 342 MDRawContextX86* x86 = new MDRawContextX86(); 343 memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size()); 344 context_->SetContextX86(x86); 345 } else if (strcmp(arch.c_str(), kMipsArchitecture) == 0) { 346 if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { 347 std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() 348 << " bytes instead of " << sizeof(MDRawContextMIPS) 349 << std::endl; 350 continue; 351 } 352 MDRawContextMIPS* mips32 = new MDRawContextMIPS(); 353 memcpy(mips32, &cpu_state_raw[0], cpu_state_raw.size()); 354 context_->SetContextMIPS(mips32); 355 } else if (strcmp(arch.c_str(), kMips64Architecture) == 0) { 356 if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) { 357 std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size() 358 << " bytes instead of " << sizeof(MDRawContextMIPS) 359 << std::endl; 360 continue; 361 } 362 MDRawContextMIPS* mips64 = new MDRawContextMIPS(); 363 memcpy(mips64, &cpu_state_raw[0], cpu_state_raw.size()); 364 context_->SetContextMIPS64(mips64); 365 } else { 366 std::cerr << "Unsupported architecture: " << arch << std::endl; 367 } 368 } else if ((pos = line.find(kCrashReasonKey)) != string::npos) { 369 string crash_reason_str(line, pos + strlen(kCrashReasonKey)); 370 std::istringstream crash_reason_tokens(crash_reason_str); 371 string signal; 372 string address; 373 crash_reason_tokens >> signal; 374 crash_reason_tokens >> crash_reason_; 375 crash_reason_tokens >> address; 376 crash_address_ = HexStrToL<uint64_t>(address); 377 } else if ((pos = line.find(kGpuKey)) != string::npos) { 378 string gpu_str(line, pos + strlen(kGpuKey)); 379 if (strcmp(gpu_str.c_str(), kGpuUnknown) != 0) { 380 std::istringstream gpu_tokens(gpu_str); 381 std::getline(gpu_tokens, system_info_->gl_version, '|'); 382 std::getline(gpu_tokens, system_info_->gl_vendor, '|'); 383 std::getline(gpu_tokens, system_info_->gl_renderer, '|'); 384 } 385 } else if ((pos = line.find(kMmapKey)) != string::npos) { 386 string mmap_line(line, pos + strlen(kMmapKey)); 387 std::istringstream mmap_tokens(mmap_line); 388 string addr, offset, size, identifier, filename; 389 mmap_tokens >> addr; 390 mmap_tokens >> offset; 391 mmap_tokens >> size; 392 mmap_tokens >> identifier; 393 mmap_tokens >> filename; 394 395 modules_->Add(new BasicCodeModule( 396 HexStrToL<uint64_t>(addr), // base_address 397 HexStrToL<uint64_t>(size), // size 398 filename, // code_file 399 identifier, // code_identifier 400 filename, // debug_file 401 identifier, // debug_identifier 402 "")); // version 403 } 404 } 405 stack_region_->Init(stack_start, stack_content); 406 } 407 408 } // namespace google_breakpad