xref: /aosp_15_r20/external/google-breakpad/src/processor/microdump.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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>
HexStrToL(const string & str)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 
ParseHexBuf(const string & str)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 
GetLine(std::istringstream * istream,string * str)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 
Add(const CodeModule * module)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 
SetEnableModuleShrink(bool is_enabled)118 void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) {
119   map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper
120                                    : MergeRangeStrategy::kExclusiveRanges);
121 }
122 
123 //
124 // MicrodumpContext
125 //
126 
SetContextARM(MDRawContextARM * arm)127 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
128   DumpContext::SetContextFlags(MD_CONTEXT_ARM);
129   DumpContext::SetContextARM(arm);
130   valid_ = true;
131 }
132 
SetContextARM64(MDRawContextARM64 * arm64)133 void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) {
134   DumpContext::SetContextFlags(MD_CONTEXT_ARM64);
135   DumpContext::SetContextARM64(arm64);
136   valid_ = true;
137 }
138 
SetContextX86(MDRawContextX86 * x86)139 void MicrodumpContext::SetContextX86(MDRawContextX86* x86) {
140   DumpContext::SetContextFlags(MD_CONTEXT_X86);
141   DumpContext::SetContextX86(x86);
142   valid_ = true;
143 }
144 
SetContextMIPS(MDRawContextMIPS * mips32)145 void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) {
146   DumpContext::SetContextFlags(MD_CONTEXT_MIPS);
147   DumpContext::SetContextMIPS(mips32);
148   valid_ = true;
149 }
150 
SetContextMIPS64(MDRawContextMIPS * mips64)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 
MicrodumpMemoryRegion()162 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
163 
Init(uint64_t base_address,const std::vector<uint8_t> & contents)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 
GetBase() const170 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
171 
GetSize() const172 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
173 
GetMemoryAtAddress(uint64_t address,uint8_t * value) const174 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
175                                                uint8_t* value) const {
176   return GetMemoryLittleEndian(address, value);
177 }
178 
GetMemoryAtAddress(uint64_t address,uint16_t * value) const179 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
180                                                uint16_t* value) const {
181   return GetMemoryLittleEndian(address, value);
182 }
183 
GetMemoryAtAddress(uint64_t address,uint32_t * value) const184 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
185                                                uint32_t* value) const {
186   return GetMemoryLittleEndian(address, value);
187 }
188 
GetMemoryAtAddress(uint64_t address,uint64_t * value) const189 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
190                                                uint64_t* value) const {
191   return GetMemoryLittleEndian(address, value);
192 }
193 
194 template<typename ValueType>
GetMemoryLittleEndian(uint64_t address,ValueType * value) const195 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 
Print() const209 void MicrodumpMemoryRegion::Print() const {
210   // Not reached, just needed to honor the base class contract.
211   assert(false);
212 }
213 
214 //
215 // Microdump
216 //
Microdump(const string & contents)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
409