LEFT | RIGHT |
1 // Copyright (c) 2014 Google Inc. | 1 // Copyright (c) 2014 Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 15 matching lines...) Expand all Loading... |
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | 29 |
30 // microdump.cc: A microdump reader. | 30 // microdump.cc: A microdump reader. |
31 // | 31 // |
32 // See microdump.h for documentation. | 32 // See microdump.h for documentation. |
33 | 33 |
34 #include "google_breakpad/processor/microdump.h" | 34 #include "google_breakpad/processor/microdump.h" |
35 | 35 |
36 #include <memory> | |
37 #include <stdio.h> | 36 #include <stdio.h> |
38 #include <string.h> | 37 #include <string.h> |
| 38 |
| 39 #include <memory> |
| 40 #include <sstream> |
39 #include <string> | 41 #include <string> |
40 #include <vector> | 42 #include <vector> |
41 #include <sstream> | |
42 | 43 |
43 #include "google_breakpad/common/minidump_cpu_arm.h" | 44 #include "google_breakpad/common/minidump_cpu_arm.h" |
44 | 45 #include "google_breakpad/processor/code_module.h" |
45 #include "processor/basic_code_module.h" | 46 #include "processor/basic_code_module.h" |
46 #include "processor/basic_code_modules.h" | 47 #include "processor/linked_ptr.h" |
47 #include "processor/logging.h" | 48 #include "processor/logging.h" |
48 | 49 #include "processor/range_map-inl.h" |
49 namespace google_breakpad { | 50 |
50 | 51 namespace { |
51 uint64_t HexStrToUL(const string& str) { | 52 static const char kGoogleBreakpadKey[] = "/google-breakpad("; |
52 return strtoul(str.c_str(), NULL, 16); | 53 static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----"; |
53 } | 54 static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----"; |
54 | 55 static const char kOsKey[] = ": O "; |
55 long HexStrToL(const string& str) { | 56 static const char kCpuKey[] = ": C "; |
56 return strtol(str.c_str(), NULL, 16); | 57 static const char kMmapKey[] = ": M "; |
| 58 static const char kStackKey[] = ": S "; |
| 59 static const char kStackFirstLineKey[] = ": S 0 "; |
| 60 static const char kArmArchitecture[] = "armv7l"; |
| 61 |
| 62 template<typename T> |
| 63 T HexStrToL(const string& str) { |
| 64 uint64_t res = 0; |
| 65 std::istringstream ss(str); |
| 66 ss >> std::hex >> res; |
| 67 return static_cast<T>(res); |
57 } | 68 } |
58 | 69 |
59 std::vector<uint8_t> ParseHexBuf(const string& str) { | 70 std::vector<uint8_t> ParseHexBuf(const string& str) { |
60 std::vector<uint8_t> buf; | 71 std::vector<uint8_t> buf; |
61 for (size_t i = 0; i < str.length(); i += 2) { | 72 for (size_t i = 0; i < str.length(); i += 2) { |
62 buf.push_back(HexStrToL(str.substr(i, 2))); | 73 buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2))); |
63 } | 74 } |
64 return buf; | 75 return buf; |
65 } | 76 } |
66 | 77 |
67 | 78 } // namespace |
68 // | 79 |
69 // MicrodumpModuleList | 80 namespace google_breakpad { |
70 // | 81 |
71 | 82 // |
72 void MicrodumpModuleList::Add(const CodeModule* module) { | 83 // MicrodumpModules |
73 modules_.push_back(module); | 84 // |
74 } | 85 |
75 | 86 void MicrodumpModules::Add(const CodeModule* module) { |
76 unsigned int MicrodumpModuleList::module_count() const { | 87 linked_ptr<const CodeModule> module_ptr(module); |
77 return modules_.size(); | 88 if (!map_->StoreRange(module->base_address(), module->size(), module_ptr)) { |
78 } | 89 BPLOG(ERROR) << "Module " << module->code_file() << |
79 | 90 " could not be stored"; |
80 const CodeModule* | |
81 MicrodumpModuleList::GetModuleForAddress(uint64_t address) const { | |
82 for (MicrodumpModules::const_iterator i = modules_.begin(); | |
83 i != modules_.end(); | |
84 ++i) { | |
85 const CodeModule* module = *i; | |
86 if (module->base_address() <= address && | |
87 address - module->base_address() < module->size()) | |
88 return module; | |
89 } | 91 } |
90 return NULL; | |
91 } | |
92 | |
93 const CodeModule* MicrodumpModuleList::GetMainModule() const { | |
94 return module_count() > 0 ? modules_[0] : NULL; | |
95 } | |
96 | |
97 const CodeModule* | |
98 MicrodumpModuleList::GetModuleAtSequence(unsigned int sequence) const { | |
99 return (module_count() > sequence) ? modules_.at(sequence) : NULL; | |
100 } | |
101 | |
102 const CodeModule* | |
103 MicrodumpModuleList::GetModuleAtIndex(unsigned int index) const { | |
104 return (module_count() > index) ? modules_.at(index) : NULL; | |
105 } | |
106 | |
107 const CodeModules* MicrodumpModuleList::Copy() const { | |
108 return new BasicCodeModules(this); | |
109 } | 92 } |
110 | 93 |
111 | 94 |
112 // | 95 // |
113 // MicrodumpContext | 96 // MicrodumpContext |
114 // | 97 // |
115 | |
116 MicrodumpContext::MicrodumpContext() { | |
117 } | |
118 | |
119 MicrodumpContext::~MicrodumpContext() { | |
120 } | |
121 | |
122 void MicrodumpContext::SetContextX86(MDRawContextX86* x86) { | |
123 DumpContext::SetContextFlags(MD_CONTEXT_X86); | |
124 DumpContext::SetContextX86(x86); | |
125 valid_ = true; | |
126 } | |
127 | |
128 void MicrodumpContext::SetContextPPC(MDRawContextPPC* ppc) { | |
129 DumpContext::SetContextFlags(MD_CONTEXT_PPC); | |
130 DumpContext::SetContextPPC(ppc); | |
131 valid_ = true; | |
132 } | |
133 | |
134 void MicrodumpContext::SetContextPPC64(MDRawContextPPC64* ppc64) { | |
135 DumpContext::SetContextFlags(MD_CONTEXT_PPC64); | |
136 DumpContext::SetContextPPC64(ppc64); | |
137 valid_ = true; | |
138 } | |
139 | |
140 void MicrodumpContext::SetContextAMD64(MDRawContextAMD64* amd64) { | |
141 DumpContext::SetContextFlags(MD_CONTEXT_AMD64); | |
142 DumpContext::SetContextAMD64(amd64); | |
143 valid_ = true; | |
144 } | |
145 | |
146 void MicrodumpContext::SetContextSPARC(MDRawContextSPARC* ctx_sparc) { | |
147 DumpContext::SetContextFlags(MD_CONTEXT_SPARC); | |
148 DumpContext::SetContextSPARC(ctx_sparc); | |
149 valid_ = true; | |
150 } | |
151 | 98 |
152 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) { | 99 void MicrodumpContext::SetContextARM(MDRawContextARM* arm) { |
153 DumpContext::SetContextFlags(MD_CONTEXT_ARM); | 100 DumpContext::SetContextFlags(MD_CONTEXT_ARM); |
154 DumpContext::SetContextARM(arm); | 101 DumpContext::SetContextARM(arm); |
155 valid_ = true; | 102 valid_ = true; |
156 } | 103 } |
157 | 104 |
158 void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) { | |
159 DumpContext::SetContextFlags(MD_CONTEXT_ARM64); | |
160 DumpContext::SetContextARM64(arm64); | |
161 valid_ = true; | |
162 } | |
163 | |
164 void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* ctx_mips) { | |
165 DumpContext::SetContextFlags(MD_CONTEXT_MIPS); | |
166 DumpContext::SetContextMIPS(ctx_mips); | |
167 valid_ = true; | |
168 } | |
169 | |
170 bool MicrodumpContext::SetContext(const std::vector<uint8_t>& cpu_state_raw) { | |
171 | |
172 if (cpu_state_raw.size() == sizeof(MDRawContextX86)) { | |
173 MDRawContextX86* x86 = new MDRawContextX86(); | |
174 memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size()); | |
175 SetContextX86(x86); | |
176 return true; | |
177 } | |
178 if (cpu_state_raw.size() == sizeof(MDRawContextPPC)) { | |
179 MDRawContextPPC* ppc = new MDRawContextPPC(); | |
180 memcpy(ppc, &cpu_state_raw[0], cpu_state_raw.size()); | |
181 SetContextPPC(ppc); | |
182 return true; | |
183 } | |
184 if (cpu_state_raw.size() == sizeof(MDRawContextPPC64)) { | |
185 MDRawContextPPC64* ppc64 = new MDRawContextPPC64(); | |
186 memcpy(ppc64, &cpu_state_raw[0], cpu_state_raw.size()); | |
187 SetContextPPC64(ppc64); | |
188 return true; | |
189 } | |
190 if (cpu_state_raw.size() == sizeof(MDRawContextAMD64)) { | |
191 MDRawContextAMD64* amd64 = new MDRawContextAMD64(); | |
192 memcpy(amd64, &cpu_state_raw[0], cpu_state_raw.size()); | |
193 SetContextAMD64(amd64); | |
194 return true; | |
195 } | |
196 if (cpu_state_raw.size() == sizeof(MDRawContextSPARC)) { | |
197 MDRawContextSPARC* sparc = new MDRawContextSPARC(); | |
198 memcpy(sparc, &cpu_state_raw[0], cpu_state_raw.size()); | |
199 SetContextSPARC(sparc); | |
200 return true; | |
201 } | |
202 if (cpu_state_raw.size() == sizeof(MDRawContextARM)) { | |
203 MDRawContextARM* arm = new MDRawContextARM(); | |
204 memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); | |
205 SetContextARM(arm); | |
206 return true; | |
207 } | |
208 if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) { | |
209 MDRawContextARM64* arm64 = new MDRawContextARM64(); | |
210 memcpy(arm64, &cpu_state_raw[0], cpu_state_raw.size()); | |
211 SetContextARM64(arm64); | |
212 return true; | |
213 } | |
214 if (cpu_state_raw.size() == sizeof(MDRawContextMIPS)) { | |
215 MDRawContextMIPS* ctx_mips = new MDRawContextMIPS(); | |
216 memcpy(ctx_mips, &cpu_state_raw[0], cpu_state_raw.size()); | |
217 SetContextMIPS(ctx_mips); | |
218 return true; | |
219 } | |
220 return false; | |
221 } | |
222 | 105 |
223 // | 106 // |
224 // MicrodumpMemoryRegion | 107 // MicrodumpMemoryRegion |
225 // | 108 // |
226 | 109 |
227 MicrodumpMemoryRegion::MicrodumpMemoryRegion(): base_address_(0) { } | 110 MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { } |
228 | 111 |
229 void MicrodumpMemoryRegion::Init(uint64_t base_address, | 112 void MicrodumpMemoryRegion::Init(uint64_t base_address, |
230 const string &contents) { | 113 const std::vector<uint8_t>& contents) { |
231 base_address_ = base_address; | 114 base_address_ = base_address; |
232 contents_ = contents; | 115 contents_ = contents; |
233 } | 116 } |
234 | 117 |
235 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; } | 118 uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; } |
| 119 |
236 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); } | 120 uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); } |
237 | 121 |
238 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 122 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
239 uint8_t *value) const { | 123 uint8_t* value) const { |
240 return GetMemoryLittleEndian(address, value); | 124 return GetMemoryLittleEndian(address, value); |
241 } | 125 } |
242 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 126 |
243 uint16_t *value) const { | 127 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
244 return GetMemoryLittleEndian(address, value); | 128 uint16_t* value) const { |
245 } | 129 return GetMemoryLittleEndian(address, value); |
246 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 130 } |
247 uint32_t *value) const { | 131 |
248 return GetMemoryLittleEndian(address, value); | 132 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
249 } | 133 uint32_t* value) const { |
250 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, | 134 return GetMemoryLittleEndian(address, value); |
251 uint64_t *value) const { | 135 } |
| 136 |
| 137 bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
| 138 uint64_t* value) const { |
252 return GetMemoryLittleEndian(address, value); | 139 return GetMemoryLittleEndian(address, value); |
253 } | 140 } |
254 | 141 |
255 template<typename ValueType> | 142 template<typename ValueType> |
256 bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address, | 143 bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address, |
257 ValueType *value) const { | 144 ValueType* value) const { |
258 if (address < base_address_ || | 145 if (address < base_address_ || |
259 address - base_address_ + sizeof(ValueType) > contents_.size()) | 146 address - base_address_ + sizeof(ValueType) > contents_.size()) |
260 return false; | 147 return false; |
261 ValueType v = 0; | 148 ValueType v = 0; |
262 int start = address - base_address_; | 149 uint64_t start = address - base_address_; |
263 // The loop condition is odd, but it's correct for size_t. | 150 // The loop condition is odd, but it's correct for size_t. |
264 for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) | 151 for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) |
265 v = (v << 8) | static_cast<unsigned char>(contents_[start + i]); | 152 v = (v << 8) | static_cast<uint8_t>(contents_[start + i]); |
266 *value = v; | 153 *value = v; |
267 return true; | 154 return true; |
268 } | 155 } |
269 | 156 |
270 void MicrodumpMemoryRegion::Print() const { | 157 void MicrodumpMemoryRegion::Print() const { |
271 printf("%s\n", contents_.c_str()); | 158 // Not reached, just needed to honor the base class contract. |
| 159 assert(false); |
272 } | 160 } |
273 | 161 |
274 // | 162 // |
275 // Microdump | 163 // Microdump |
276 // | 164 // |
277 const char Microdump::kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----"; | |
278 const char Microdump::kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----"; | |
279 const char Microdump::kOsKey[] = ": O "; | |
280 const char Microdump::kCpuKey[] = ": C "; | |
281 const char Microdump::kMmapKey[] = ": M "; | |
282 const char Microdump::kStackKey[] = ": S "; | |
283 | |
284 Microdump::Microdump(const string& contents) | 165 Microdump::Microdump(const string& contents) |
285 : context_(new MicrodumpContext()), | 166 : context_(new MicrodumpContext()), |
286 stack_region_(new MicrodumpMemoryRegion()), | 167 stack_region_(new MicrodumpMemoryRegion()), |
287 modules_(new MicrodumpModuleList()) { | 168 modules_(new MicrodumpModules()) { |
288 assert(!contents.empty()); | 169 assert(!contents.empty()); |
289 | 170 |
290 bool in_microdump = false; | 171 bool in_microdump = false; |
291 string line; | 172 string line; |
292 uintptr_t stack_start = 0; | 173 uint64_t stack_start = 0; |
293 std::vector<uint8_t> stack_content; | 174 std::vector<uint8_t> stack_content; |
| 175 string arch; |
294 | 176 |
295 std::istringstream stream(contents); | 177 std::istringstream stream(contents); |
296 while (std::getline(stream, line)) { | 178 while (std::getline(stream, line)) { |
297 size_t found; | 179 if (line.find(kGoogleBreakpadKey) == string::npos) { |
298 if (line.find(Microdump::kMicrodumpBegin) != string::npos) { | 180 continue; |
| 181 } |
| 182 if (line.find(kMicrodumpBegin) != string::npos) { |
299 in_microdump = true; | 183 in_microdump = true; |
300 continue; | 184 continue; |
301 } | 185 } |
302 if (line.find(Microdump::kMicrodumpEnd) != string::npos) { | 186 if (line.find(kMicrodumpEnd) != string::npos) { |
303 break; | 187 break; |
304 } | 188 } |
305 | 189 |
306 if (!in_microdump) { | 190 if (!in_microdump) { |
307 continue; | 191 continue; |
308 } | 192 } |
309 | 193 |
310 if ((found = line.find(kOsKey)) != string::npos) { | 194 size_t pos; |
311 // This is for future use. | 195 if ((pos = line.find(kOsKey)) != string::npos) { |
312 } else if ((found = line.find(kStackKey)) != string::npos) { | 196 string os_str(line, pos + strlen(kOsKey)); |
313 string stack_str = line; | 197 std::istringstream os_tokens(os_str); |
314 stack_str.erase(0, found + strlen(kStackKey)); | 198 string unused_id; |
| 199 os_tokens >> unused_id; |
| 200 os_tokens >> arch; |
| 201 arch = arch.substr(1, arch.length() - 2); // remove quotes |
| 202 // OS line also contains release and version for future use. |
| 203 } else if ((pos = line.find(kStackKey)) != string::npos) { |
| 204 if (line.find(kStackFirstLineKey) != string::npos) { |
| 205 // The first line of the stack (S 0 stack header) provides the value of |
| 206 // the stack pointer, the start address of the stack being dumped and |
| 207 // the length of the stack. We could use it in future to double check |
| 208 // that we received all the stack as expected. |
| 209 continue; |
| 210 } |
| 211 string stack_str(line, pos + strlen(kStackKey)); |
315 std::istringstream stack_tokens(stack_str); | 212 std::istringstream stack_tokens(stack_str); |
316 uintptr_t start_addr; | 213 string start_addr_str; |
317 string raw_content; | 214 string raw_content; |
318 stack_tokens >> std::hex >> start_addr; | 215 stack_tokens >> start_addr_str; |
319 stack_tokens >> raw_content; | 216 stack_tokens >> raw_content; |
| 217 uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str); |
320 | 218 |
321 if (stack_start != 0) { | 219 if (stack_start != 0) { |
| 220 // Verify that the stack chunks in the microdump are contiguous. |
322 assert(start_addr == stack_start + stack_content.size()); | 221 assert(start_addr == stack_start + stack_content.size()); |
323 } else { | 222 } else { |
324 stack_start = start_addr; | 223 stack_start = start_addr; |
325 } | 224 } |
326 std::vector<uint8_t> chunk = ParseHexBuf(raw_content); | 225 std::vector<uint8_t> chunk = ParseHexBuf(raw_content); |
327 stack_content.insert(stack_content.end(), chunk.begin(), chunk.end()); | 226 stack_content.insert(stack_content.end(), chunk.begin(), chunk.end()); |
328 | 227 |
329 stack_region_->Init(stack_start, | 228 } else if ((pos = line.find(kCpuKey)) != string::npos) { |
330 string(stack_content.begin(), stack_content.end())); | 229 string cpu_state_str(line, pos + strlen(kCpuKey)); |
331 } else if ((found = line.find(kCpuKey)) != string::npos) { | |
332 string cpu_state_str = line; | |
333 cpu_state_str.erase(0, found + strlen(kCpuKey)); | |
334 std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str); | 230 std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str); |
335 assert(context_->SetContext(cpu_state_raw)); | 231 if (strcmp(arch.c_str(), kArmArchitecture) == 0) { |
336 } else if ((found = line.find(kMmapKey)) != string::npos) { | 232 MDRawContextARM* arm = new MDRawContextARM(); |
337 string mmap_line = line; | 233 memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size()); |
338 mmap_line.erase(0, found + strlen(kMmapKey)); | 234 context_->SetContextARM(arm); |
| 235 } else { |
| 236 std::cerr << "Unsupported architecture: " << arch << std::endl; |
| 237 } |
| 238 } else if ((pos = line.find(kMmapKey)) != string::npos) { |
| 239 string mmap_line(line, pos + strlen(kMmapKey)); |
339 std::istringstream mmap_tokens(mmap_line); | 240 std::istringstream mmap_tokens(mmap_line); |
340 string addr, offset, size, what, identifier, filename; | 241 string addr, offset, size, identifier, filename; |
341 mmap_tokens >> addr; | 242 mmap_tokens >> addr; |
342 mmap_tokens >> offset; | 243 mmap_tokens >> offset; |
343 mmap_tokens >> size; | 244 mmap_tokens >> size; |
344 mmap_tokens >> identifier; | 245 mmap_tokens >> identifier; |
345 mmap_tokens >> filename; | 246 mmap_tokens >> filename; |
346 | 247 |
347 modules_->Add(new BasicCodeModule(HexStrToUL(addr), // base_address | 248 modules_->Add(new BasicCodeModule( |
348 HexStrToUL(size), // size | 249 HexStrToL<uint64_t>(addr), // base_address |
349 filename, // code_file | 250 HexStrToL<uint64_t>(size), // size |
350 identifier, // code_identifier | 251 filename, // code_file |
351 filename, // debug_file | 252 identifier, // code_identifier |
352 identifier, // debug_identifier | 253 filename, // debug_file |
353 "")); // version | 254 identifier, // debug_identifier |
| 255 "")); // version |
354 } | 256 } |
355 } | 257 } |
| 258 stack_region_->Init(stack_start, stack_content); |
356 } | 259 } |
357 | 260 |
358 } // namespace google_breakpad | 261 } // namespace google_breakpad |
359 | 262 |
LEFT | RIGHT |