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
52namespace {
53static const char kGoogleBreakpadKey[] = "google-breakpad";
54static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----";
55static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----";
56static const char kOsKey[] = ": O ";
57static const char kCpuKey[] = ": C ";
58static const char kCrashReasonKey[] = ": R ";
59static const char kGpuKey[] = ": G ";
60static const char kMmapKey[] = ": M ";
61static const char kStackKey[] = ": S ";
62static const char kStackFirstLineKey[] = ": S 0 ";
63static const char kArmArchitecture[] = "arm";
64static const char kArm64Architecture[] = "arm64";
65static const char kX86Architecture[] = "x86";
66static const char kMipsArchitecture[] = "mips";
67static const char kMips64Architecture[] = "mips64";
68static const char kGpuUnknown[] = "UNKNOWN";
69
70template<typename T>
71T 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
78std::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
86bool 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
101namespace google_breakpad {
102
103//
104// MicrodumpModules
105//
106
107void 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
115void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) {
116 map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper
117 : MergeRangeStrategy::kExclusiveRanges);
118}
119
120//
121// MicrodumpContext
122//
123
124void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
125 DumpContext::SetContextFlags(MD_CONTEXT_ARM);
126 DumpContext::SetContextARM(arm);
127 valid_ = true;
128}
129
130void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) {
131 DumpContext::SetContextFlags(MD_CONTEXT_ARM64);
132 DumpContext::SetContextARM64(arm64);
133 valid_ = true;
134}
135
136void MicrodumpContext::SetContextX86(MDRawContextX86* x86) {
137 DumpContext::SetContextFlags(MD_CONTEXT_X86);
138 DumpContext::SetContextX86(x86);
139 valid_ = true;
140}
141
142void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) {
143 DumpContext::SetContextFlags(MD_CONTEXT_MIPS);
144 DumpContext::SetContextMIPS(mips32);
145 valid_ = true;
146}
147
148void 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
159MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
160
161void MicrodumpMemoryRegion::Init(uint64_t base_address,
162 const std::vector<uint8_t>& contents) {
163 base_address_ = base_address;
164 contents_ = contents;
165}
166
167uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
168
169uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
170
171bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
172 uint8_t* value) const {
173 return GetMemoryLittleEndian(address, value);
174}
175
176bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
177 uint16_t* value) const {
178 return GetMemoryLittleEndian(address, value);
179}
180
181bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
182 uint32_t* value) const {
183 return GetMemoryLittleEndian(address, value);
184}
185
186bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
187 uint64_t* value) const {
188 return GetMemoryLittleEndian(address, value);
189}
190
191template<typename ValueType>
192bool 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
206void MicrodumpMemoryRegion::Print() const {
207 // Not reached, just needed to honor the base class contract.
208 assert(false);
209}
210
211//
212// Microdump
213//
214Microdump::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