/ src / processor / microdump.cc
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