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>
HexStrToL(const string & str)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 
ParseHexBuf(const string & str)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 
Add(const CodeModule * module)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 
SetContextARM(MDRawContextARM * arm)100 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
101   DumpContext::SetContextFlags(MD_CONTEXT_ARM);
102   DumpContext::SetContextARM(arm);
103   valid_ = true;
104 }
105 
SetContextARM64(MDRawContextARM64 * arm64)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 
MicrodumpMemoryRegion()117 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
118 
Init(uint64_t base_address,const std::vector<uint8_t> & contents)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 
GetBase() const125 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
126 
GetSize() const127 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
128 
GetMemoryAtAddress(uint64_t address,uint8_t * value) const129 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
130                                                uint8_t* value) const {
131   return GetMemoryLittleEndian(address, value);
132 }
133 
GetMemoryAtAddress(uint64_t address,uint16_t * value) const134 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
135                                                uint16_t* value) const {
136   return GetMemoryLittleEndian(address, value);
137 }
138 
GetMemoryAtAddress(uint64_t address,uint32_t * value) const139 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
140                                                uint32_t* value) const {
141   return GetMemoryLittleEndian(address, value);
142 }
143 
GetMemoryAtAddress(uint64_t address,uint64_t * value) const144 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
145                                                uint64_t* value) const {
146   return GetMemoryLittleEndian(address, value);
147 }
148 
149 template<typename ValueType>
GetMemoryLittleEndian(uint64_t address,ValueType * value) const150 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 
Print() const164 void MicrodumpMemoryRegion::Print() const {
165   // Not reached, just needed to honor the base class contract.
166   assert(false);
167 }
168 
169 //
170 // Microdump
171 //
Microdump(const string & contents)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