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/convert_old_arm64_context.h"
48 #include "processor/linked_ptr.h"
49 #include "processor/logging.h"
50 #include "processor/range_map-inl.h"
51 
52 namespace {
53 static const char kGoogleBreakpadKey[] = "google-breakpad";
54 static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----";
55 static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----";
56 static const char kOsKey[] = ": O ";
57 static const char kCpuKey[] = ": C ";
58 static const char kCrashReasonKey[] = ": R ";
59 static const char kGpuKey[] = ": G ";
60 static const char kMmapKey[] = ": M ";
61 static const char kStackKey[] = ": S ";
62 static const char kStackFirstLineKey[] = ": S 0 ";
63 static const char kArmArchitecture[] = "arm";
64 static const char kArm64Architecture[] = "arm64";
65 static const char kX86Architecture[] = "x86";
66 static const char kMipsArchitecture[] = "mips";
67 static const char kMips64Architecture[] = "mips64";
68 static const char kGpuUnknown[] = "UNKNOWN";
69 
70 template<typename T>
HexStrToL(const string & str)71 T HexStrToL(const string& str) {
72   uint64_t res = 0;
73   std::istringstream ss(str);
74   ss >> std::hex >> res;
75   return static_cast<T>(res);
76 }
77 
ParseHexBuf(const string & str)78 std::vector<uint8_t> ParseHexBuf(const string& str) {
79   std::vector<uint8_t> buf;
80   for (size_t i = 0; i < str.length(); i += 2) {
81     buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2)));
82   }
83   return buf;
84 }
85 
GetLine(std::istringstream * istream,string * str)86 bool GetLine(std::istringstream* istream, string* str) {
87   if (std::getline(*istream, *str)) {
88     // Trim any trailing newline from the end of the line. Allows us
89     // to seamlessly handle both Windows/DOS and Unix formatted input. The
90     // adb tool generally writes logcat dumps in Windows/DOS format.
91     if (!str->empty() && str->at(str->size() - 1) == '\r') {
92       str->erase(str->size() - 1);
93     }
94     return true;
95   }
96   return false;
97 }
98 
99 }  // namespace
100 
101 namespace google_breakpad {
102 
103 //
104 // MicrodumpModules
105 //
106 
Add(const CodeModule * module)107 void MicrodumpModules::Add(const CodeModule* module) {
108   linked_ptr<const CodeModule> module_ptr(module);
109   if (!map_.StoreRange(module->base_address(), module->size(), module_ptr)) {
110     BPLOG(ERROR) << "Module " << module->code_file() <<
111                     " could not be stored";
112   }
113 }
114 
SetEnableModuleShrink(bool is_enabled)115 void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) {
116   map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper
117                                    : MergeRangeStrategy::kExclusiveRanges);
118 }
119 
120 //
121 // MicrodumpContext
122 //
123 
SetContextARM(MDRawContextARM * arm)124 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
125   DumpContext::SetContextFlags(MD_CONTEXT_ARM);
126   DumpContext::SetContextARM(arm);
127   valid_ = true;
128 }
129 
SetContextARM64(MDRawContextARM64 * arm64)130 void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) {
131   DumpContext::SetContextFlags(MD_CONTEXT_ARM64);
132   DumpContext::SetContextARM64(arm64);
133   valid_ = true;
134 }
135 
SetContextX86(MDRawContextX86 * x86)136 void MicrodumpContext::SetContextX86(MDRawContextX86* x86) {
137   DumpContext::SetContextFlags(MD_CONTEXT_X86);
138   DumpContext::SetContextX86(x86);
139   valid_ = true;
140 }
141 
SetContextMIPS(MDRawContextMIPS * mips32)142 void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) {
143   DumpContext::SetContextFlags(MD_CONTEXT_MIPS);
144   DumpContext::SetContextMIPS(mips32);
145   valid_ = true;
146 }
147 
SetContextMIPS64(MDRawContextMIPS * mips64)148 void MicrodumpContext::SetContextMIPS64(MDRawContextMIPS* mips64) {
149   DumpContext::SetContextFlags(MD_CONTEXT_MIPS64);
150   DumpContext::SetContextMIPS(mips64);
151   valid_ = true;
152 }
153 
154 
155 //
156 // MicrodumpMemoryRegion
157 //
158 
MicrodumpMemoryRegion()159 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
160 
Init(uint64_t base_address,const std::vector<uint8_t> & contents)161 void MicrodumpMemoryRegion::Init(uint64_t base_address,
162                                  const std::vector<uint8_t>& contents) {
163   base_address_ = base_address;
164   contents_ = contents;
165 }
166 
GetBase() const167 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
168 
GetSize() const169 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
170 
GetMemoryAtAddress(uint64_t address,uint8_t * value) const171 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
172                                                uint8_t* value) const {
173   return GetMemoryLittleEndian(address, value);
174 }
175 
GetMemoryAtAddress(uint64_t address,uint16_t * value) const176 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
177                                                uint16_t* value) const {
178   return GetMemoryLittleEndian(address, value);
179 }
180 
GetMemoryAtAddress(uint64_t address,uint32_t * value) const181 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
182                                                uint32_t* value) const {
183   return GetMemoryLittleEndian(address, value);
184 }
185 
GetMemoryAtAddress(uint64_t address,uint64_t * value) const186 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
187                                                uint64_t* value) const {
188   return GetMemoryLittleEndian(address, value);
189 }
190 
191 template<typename ValueType>
GetMemoryLittleEndian(uint64_t address,ValueType * value) const192 bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address,
193                                                   ValueType* value) const {
194   if (address < base_address_ ||
195       address - base_address_ + sizeof(ValueType) > contents_.size())
196     return false;
197   ValueType v = 0;
198   uint64_t start = address - base_address_;
199   // The loop condition is odd, but it's correct for size_t.
200   for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--)
201     v = (v << 8) | static_cast<uint8_t>(contents_[start + i]);
202   *value = v;
203   return true;
204 }
205 
Print() const206 void MicrodumpMemoryRegion::Print() const {
207   // Not reached, just needed to honor the base class contract.
208   assert(false);
209 }
210 
211 //
212 // Microdump
213 //
Microdump(const string & contents)214 Microdump::Microdump(const string& contents)
215   : context_(new MicrodumpContext()),
216     stack_region_(new MicrodumpMemoryRegion()),
217     modules_(new MicrodumpModules()),
218     system_info_(new SystemInfo()),
219     crash_reason_(),
220     crash_address_(0u) {
221   assert(!contents.empty());
222 
223   bool in_microdump = false;
224   string line;
225   uint64_t stack_start = 0;
226   std::vector<uint8_t> stack_content;
227   string arch;
228 
229   std::istringstream stream(contents);
230   while (GetLine(&stream, &line)) {
231     if (line.find(kGoogleBreakpadKey) == string::npos) {
232       continue;
233     }
234     if (line.find(kMicrodumpBegin) != string::npos) {
235       in_microdump = true;
236       continue;
237     }
238     if (!in_microdump) {
239       continue;
240     }
241     if (line.find(kMicrodumpEnd) != string::npos) {
242       break;
243     }
244 
245     size_t pos;
246     if ((pos = line.find(kOsKey)) != string::npos) {
247       string os_str(line, pos + strlen(kOsKey));
248       std::istringstream os_tokens(os_str);
249       string os_id;
250       string num_cpus;
251       string os_version;
252       // This reflect the actual HW arch and might not match the arch emulated
253       // for the execution (e.g., running a 32-bit binary on a 64-bit cpu).
254       string hw_arch;
255 
256       os_tokens >> os_id;
257       os_tokens >> arch;
258       os_tokens >> num_cpus;
259       os_tokens >> hw_arch;
260       GetLine(&os_tokens, &os_version);
261       os_version.erase(0, 1);  // remove leading space.
262 
263       system_info_->cpu = arch;
264       system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus);
265       system_info_->os_version = os_version;
266 
267       if (os_id == "L") {
268         system_info_->os = "Linux";
269         system_info_->os_short = "linux";
270       } else if (os_id == "A") {
271         system_info_->os = "Android";
272         system_info_->os_short = "android";
273         modules_->SetEnableModuleShrink(true);
274       }
275 
276       // OS line also contains release and version for future use.
277     } else if ((pos = line.find(kStackKey)) != string::npos) {
278       if (line.find(kStackFirstLineKey) != string::npos) {
279         // The first line of the stack (S 0 stack header) provides the value of
280         // the stack pointer, the start address of the stack being dumped and
281         // the length of the stack. We could use it in future to double check
282         // that we received all the stack as expected.
283         continue;
284       }
285       string stack_str(line, pos + strlen(kStackKey));
286       std::istringstream stack_tokens(stack_str);
287       string start_addr_str;
288       string raw_content;
289       stack_tokens >> start_addr_str;
290       stack_tokens >> raw_content;
291       uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str);
292 
293       if (stack_start != 0) {
294         // Verify that the stack chunks in the microdump are contiguous.
295         assert(start_addr == stack_start + stack_content.size());
296       } else {
297         stack_start = start_addr;
298       }
299       std::vector<uint8_t> chunk = ParseHexBuf(raw_content);
300       stack_content.insert(stack_content.end(), chunk.begin(), chunk.end());
301 
302     } else if ((pos = line.find(kCpuKey)) != string::npos) {
303       string cpu_state_str(line, pos + strlen(kCpuKey));
304       std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str);
305       if (strcmp(arch.c_str(), kArmArchitecture) == 0) {
306         if (cpu_state_raw.size() != sizeof(MDRawContextARM)) {
307           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
308                     << " bytes instead of " << sizeof(MDRawContextARM)
309                     << std::endl;
310           continue;
311         }
312         MDRawContextARM* arm = new MDRawContextARM();
313         memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
314         context_->SetContextARM(arm);
315       } else if (strcmp(arch.c_str(), kArm64Architecture) == 0) {
316         if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) {
317           MDRawContextARM64* arm = new MDRawContextARM64();
318           memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
319           context_->SetContextARM64(arm);
320         } else if (cpu_state_raw.size() == sizeof(MDRawContextARM64_Old)) {
321           MDRawContextARM64_Old old_arm;
322           memcpy(&old_arm, &cpu_state_raw[0], cpu_state_raw.size());
323           MDRawContextARM64* new_arm = new MDRawContextARM64();
324           ConvertOldARM64Context(old_arm, new_arm);
325           context_->SetContextARM64(new_arm);
326         } else {
327           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
328                     << " bytes instead of " << sizeof(MDRawContextARM64)
329                     << std::endl;
330           continue;
331         }
332       } else if (strcmp(arch.c_str(), kX86Architecture) == 0) {
333         if (cpu_state_raw.size() != sizeof(MDRawContextX86)) {
334           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
335                     << " bytes instead of " << sizeof(MDRawContextX86)
336                     << std::endl;
337           continue;
338         }
339         MDRawContextX86* x86 = new MDRawContextX86();
340         memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size());
341         context_->SetContextX86(x86);
342       } else if (strcmp(arch.c_str(), kMipsArchitecture) == 0) {
343         if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) {
344           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
345                     << " bytes instead of " << sizeof(MDRawContextMIPS)
346                     << std::endl;
347           continue;
348         }
349         MDRawContextMIPS* mips32 = new MDRawContextMIPS();
350         memcpy(mips32, &cpu_state_raw[0], cpu_state_raw.size());
351         context_->SetContextMIPS(mips32);
352       } else if (strcmp(arch.c_str(), kMips64Architecture) == 0) {
353         if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) {
354           std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
355                     << " bytes instead of " << sizeof(MDRawContextMIPS)
356                     << std::endl;
357           continue;
358         }
359         MDRawContextMIPS* mips64 = new MDRawContextMIPS();
360         memcpy(mips64, &cpu_state_raw[0], cpu_state_raw.size());
361         context_->SetContextMIPS64(mips64);
362       } else {
363         std::cerr << "Unsupported architecture: " << arch << std::endl;
364       }
365     } else if ((pos = line.find(kCrashReasonKey)) != string::npos) {
366       string crash_reason_str(line, pos + strlen(kCrashReasonKey));
367       std::istringstream crash_reason_tokens(crash_reason_str);
368       string signal;
369       string address;
370       crash_reason_tokens >> signal;
371       crash_reason_tokens >> crash_reason_;
372       crash_reason_tokens >> address;
373       crash_address_ = HexStrToL<uint64_t>(address);
374     } else if ((pos = line.find(kGpuKey)) != string::npos) {
375       string gpu_str(line, pos + strlen(kGpuKey));
376       if (strcmp(gpu_str.c_str(), kGpuUnknown) != 0) {
377         std::istringstream gpu_tokens(gpu_str);
378         std::getline(gpu_tokens, system_info_->gl_version, '|');
379         std::getline(gpu_tokens, system_info_->gl_vendor, '|');
380         std::getline(gpu_tokens, system_info_->gl_renderer, '|');
381       }
382     } else if ((pos = line.find(kMmapKey)) != string::npos) {
383       string mmap_line(line, pos + strlen(kMmapKey));
384       std::istringstream mmap_tokens(mmap_line);
385       string addr, offset, size, identifier, filename;
386       mmap_tokens >> addr;
387       mmap_tokens >> offset;
388       mmap_tokens >> size;
389       mmap_tokens >> identifier;
390       mmap_tokens >> filename;
391 
392       modules_->Add(new BasicCodeModule(
393           HexStrToL<uint64_t>(addr),  // base_address
394           HexStrToL<uint64_t>(size),  // size
395           filename,                   // code_file
396           identifier,                 // code_identifier
397           filename,                   // debug_file
398           identifier,                 // debug_identifier
399           ""));                       // version
400     }
401   }
402   stack_region_->Init(stack_start, stack_content);
403 }
404 
405 }  // namespace google_breakpad
406