1 | // Copyright (c) 2010 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 | // minidump.cc: A minidump reader. |
31 | // |
32 | // See minidump.h for documentation. |
33 | // |
34 | // Author: Mark Mentovai |
35 | |
36 | #include "google_breakpad/processor/minidump.h" |
37 | |
38 | #include <assert.h> |
39 | #include <fcntl.h> |
40 | #include <stddef.h> |
41 | #include <string.h> |
42 | #include <time.h> |
43 | |
44 | #ifdef _WIN32 |
45 | #include <io.h> |
46 | #else // _WIN32 |
47 | #include <unistd.h> |
48 | #endif // _WIN32 |
49 | |
50 | #include <algorithm> |
51 | #include <fstream> |
52 | #include <limits> |
53 | #include <utility> |
54 | |
55 | #include "processor/range_map-inl.h" |
56 | |
57 | #include "common/macros.h" |
58 | #include "common/scoped_ptr.h" |
59 | #include "common/stdio_wrapper.h" |
60 | #include "google_breakpad/processor/dump_context.h" |
61 | #include "processor/basic_code_module.h" |
62 | #include "processor/basic_code_modules.h" |
63 | #include "processor/convert_old_arm64_context.h" |
64 | #include "processor/logging.h" |
65 | |
66 | namespace google_breakpad { |
67 | |
68 | using std::istream; |
69 | using std::ifstream; |
70 | using std::numeric_limits; |
71 | using std::vector; |
72 | |
73 | namespace { |
74 | |
75 | // Returns true iff |context_size| matches exactly one of the sizes of the |
76 | // various MDRawContext* types. |
77 | // TODO(blundell): This function can be removed once |
78 | // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 is fixed. |
79 | bool IsContextSizeUnique(uint32_t context_size) { |
80 | int num_matching_contexts = 0; |
81 | if (context_size == sizeof(MDRawContextX86)) |
82 | num_matching_contexts++; |
83 | if (context_size == sizeof(MDRawContextPPC)) |
84 | num_matching_contexts++; |
85 | if (context_size == sizeof(MDRawContextPPC64)) |
86 | num_matching_contexts++; |
87 | if (context_size == sizeof(MDRawContextAMD64)) |
88 | num_matching_contexts++; |
89 | if (context_size == sizeof(MDRawContextSPARC)) |
90 | num_matching_contexts++; |
91 | if (context_size == sizeof(MDRawContextARM)) |
92 | num_matching_contexts++; |
93 | if (context_size == sizeof(MDRawContextARM64)) |
94 | num_matching_contexts++; |
95 | if (context_size == sizeof(MDRawContextARM64_Old)) |
96 | num_matching_contexts++; |
97 | if (context_size == sizeof(MDRawContextMIPS)) |
98 | num_matching_contexts++; |
99 | return num_matching_contexts == 1; |
100 | } |
101 | |
102 | // |
103 | // Swapping routines |
104 | // |
105 | // Inlining these doesn't increase code size significantly, and it saves |
106 | // a whole lot of unnecessary jumping back and forth. |
107 | // |
108 | |
109 | |
110 | // Swapping an 8-bit quantity is a no-op. This function is only provided |
111 | // to account for certain templatized operations that require swapping for |
112 | // wider types but handle uint8_t too |
113 | // (MinidumpMemoryRegion::GetMemoryAtAddressInternal). |
114 | inline void Swap(uint8_t* value) {} |
115 | |
116 | // Optimization: don't need to AND the furthest right shift, because we're |
117 | // shifting an unsigned quantity. The standard requires zero-filling in this |
118 | // case. If the quantities were signed, a bitmask whould be needed for this |
119 | // right shift to avoid an arithmetic shift (which retains the sign bit). |
120 | // The furthest left shift never needs to be ANDed bitmask. |
121 | |
122 | inline void Swap(uint16_t* value) { |
123 | *value = (*value >> 8) | (*value << 8); |
124 | } |
125 | |
126 | inline void Swap(uint32_t* value) { |
127 | *value = (*value >> 24) | |
128 | ((*value >> 8) & 0x0000ff00) | |
129 | ((*value << 8) & 0x00ff0000) | |
130 | (*value << 24); |
131 | } |
132 | |
133 | inline void Swap(uint64_t* value) { |
134 | uint32_t* value32 = reinterpret_cast<uint32_t*>(value); |
135 | Swap(&value32[0]); |
136 | Swap(&value32[1]); |
137 | uint32_t temp = value32[0]; |
138 | value32[0] = value32[1]; |
139 | value32[1] = temp; |
140 | } |
141 | |
142 | |
143 | // Given a pointer to a 128-bit int in the minidump data, set the "low" |
144 | // and "high" fields appropriately. |
145 | void Normalize128(uint128_struct* value, bool is_big_endian) { |
146 | // The struct format is [high, low], so if the format is big-endian, |
147 | // the most significant bytes will already be in the high field. |
148 | if (!is_big_endian) { |
149 | uint64_t temp = value->low; |
150 | value->low = value->high; |
151 | value->high = temp; |
152 | } |
153 | } |
154 | |
155 | // This just swaps each int64 half of the 128-bit value. |
156 | // The value should also be normalized by calling Normalize128(). |
157 | void Swap(uint128_struct* value) { |
158 | Swap(&value->low); |
159 | Swap(&value->high); |
160 | } |
161 | |
162 | // Swapping signed integers |
163 | inline void Swap(int32_t* value) { |
164 | Swap(reinterpret_cast<uint32_t*>(value)); |
165 | } |
166 | |
167 | inline void Swap(MDLocationDescriptor* location_descriptor) { |
168 | Swap(&location_descriptor->data_size); |
169 | Swap(&location_descriptor->rva); |
170 | } |
171 | |
172 | inline void Swap(MDMemoryDescriptor* memory_descriptor) { |
173 | Swap(&memory_descriptor->start_of_memory_range); |
174 | Swap(&memory_descriptor->memory); |
175 | } |
176 | |
177 | inline void Swap(MDGUID* guid) { |
178 | Swap(&guid->data1); |
179 | Swap(&guid->data2); |
180 | Swap(&guid->data3); |
181 | // Don't swap guid->data4[] because it contains 8-bit quantities. |
182 | } |
183 | |
184 | inline void Swap(MDSystemTime* system_time) { |
185 | Swap(&system_time->year); |
186 | Swap(&system_time->month); |
187 | Swap(&system_time->day_of_week); |
188 | Swap(&system_time->day); |
189 | Swap(&system_time->hour); |
190 | Swap(&system_time->minute); |
191 | Swap(&system_time->second); |
192 | Swap(&system_time->milliseconds); |
193 | } |
194 | |
195 | inline void Swap(MDXStateFeature* xstate_feature) { |
196 | Swap(&xstate_feature->offset); |
197 | Swap(&xstate_feature->size); |
198 | } |
199 | |
200 | inline void Swap(MDXStateConfigFeatureMscInfo* xstate_feature_info) { |
201 | Swap(&xstate_feature_info->size_of_info); |
202 | Swap(&xstate_feature_info->context_size); |
203 | Swap(&xstate_feature_info->enabled_features); |
204 | |
205 | for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) { |
206 | Swap(&xstate_feature_info->features[i]); |
207 | } |
208 | } |
209 | |
210 | inline void Swap(MDRawSimpleStringDictionaryEntry* entry) { |
211 | Swap(&entry->key); |
212 | Swap(&entry->value); |
213 | } |
214 | |
215 | inline void Swap(uint16_t* data, size_t size_in_bytes) { |
216 | size_t data_length = size_in_bytes / sizeof(data[0]); |
217 | for (size_t i = 0; i < data_length; i++) { |
218 | Swap(&data[i]); |
219 | } |
220 | } |
221 | |
222 | // |
223 | // Character conversion routines |
224 | // |
225 | |
226 | |
227 | // Standard wide-character conversion routines depend on the system's own |
228 | // idea of what width a wide character should be: some use 16 bits, and |
229 | // some use 32 bits. For the purposes of a minidump, wide strings are |
230 | // always represented with 16-bit UTF-16 chracters. iconv isn't available |
231 | // everywhere, and its interface varies where it is available. iconv also |
232 | // deals purely with char* pointers, so in addition to considering the swap |
233 | // parameter, a converter that uses iconv would also need to take the host |
234 | // CPU's endianness into consideration. It doesn't seems worth the trouble |
235 | // of making it a dependency when we don't care about anything but UTF-16. |
236 | string* UTF16ToUTF8(const vector<uint16_t>& in, bool swap) { |
237 | scoped_ptr<string> out(new string()); |
238 | |
239 | // Set the string's initial capacity to the number of UTF-16 characters, |
240 | // because the UTF-8 representation will always be at least this long. |
241 | // If the UTF-8 representation is longer, the string will grow dynamically. |
242 | out->reserve(in.size()); |
243 | |
244 | for (vector<uint16_t>::const_iterator iterator = in.begin(); |
245 | iterator != in.end(); |
246 | ++iterator) { |
247 | // Get a 16-bit value from the input |
248 | uint16_t in_word = *iterator; |
249 | if (swap) |
250 | Swap(&in_word); |
251 | |
252 | // Convert the input value (in_word) into a Unicode code point (unichar). |
253 | uint32_t unichar; |
254 | if (in_word >= 0xdc00 && in_word <= 0xdcff) { |
255 | BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " << |
256 | HexString(in_word) << " without high" ; |
257 | return NULL; |
258 | } else if (in_word >= 0xd800 && in_word <= 0xdbff) { |
259 | // High surrogate. |
260 | unichar = (in_word - 0xd7c0) << 10; |
261 | if (++iterator == in.end()) { |
262 | BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << |
263 | HexString(in_word) << " at end of string" ; |
264 | return NULL; |
265 | } |
266 | uint32_t high_word = in_word; |
267 | in_word = *iterator; |
268 | if (in_word < 0xdc00 || in_word > 0xdcff) { |
269 | BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << |
270 | HexString(high_word) << " without low " << |
271 | HexString(in_word); |
272 | return NULL; |
273 | } |
274 | unichar |= in_word & 0x03ff; |
275 | } else { |
276 | // The ordinary case, a single non-surrogate Unicode character encoded |
277 | // as a single 16-bit value. |
278 | unichar = in_word; |
279 | } |
280 | |
281 | // Convert the Unicode code point (unichar) into its UTF-8 representation, |
282 | // appending it to the out string. |
283 | if (unichar < 0x80) { |
284 | (*out) += static_cast<char>(unichar); |
285 | } else if (unichar < 0x800) { |
286 | (*out) += 0xc0 | static_cast<char>(unichar >> 6); |
287 | (*out) += 0x80 | static_cast<char>(unichar & 0x3f); |
288 | } else if (unichar < 0x10000) { |
289 | (*out) += 0xe0 | static_cast<char>(unichar >> 12); |
290 | (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f); |
291 | (*out) += 0x80 | static_cast<char>(unichar & 0x3f); |
292 | } else if (unichar < 0x200000) { |
293 | (*out) += 0xf0 | static_cast<char>(unichar >> 18); |
294 | (*out) += 0x80 | static_cast<char>((unichar >> 12) & 0x3f); |
295 | (*out) += 0x80 | static_cast<char>((unichar >> 6) & 0x3f); |
296 | (*out) += 0x80 | static_cast<char>(unichar & 0x3f); |
297 | } else { |
298 | BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " << |
299 | HexString(unichar) << " in UTF-8" ; |
300 | return NULL; |
301 | } |
302 | } |
303 | |
304 | return out.release(); |
305 | } |
306 | |
307 | // Return the smaller of the number of code units in the UTF-16 string, |
308 | // not including the terminating null word, or maxlen. |
309 | size_t UTF16codeunits(const uint16_t* string, size_t maxlen) { |
310 | size_t count = 0; |
311 | while (count < maxlen && string[count] != 0) |
312 | count++; |
313 | return count; |
314 | } |
315 | |
316 | inline void Swap(MDTimeZoneInformation* time_zone) { |
317 | Swap(&time_zone->bias); |
318 | // Skip time_zone->standard_name. No need to swap UTF-16 fields. |
319 | // The swap will be done as part of the conversion to UTF-8. |
320 | Swap(&time_zone->standard_date); |
321 | Swap(&time_zone->standard_bias); |
322 | // Skip time_zone->daylight_name. No need to swap UTF-16 fields. |
323 | // The swap will be done as part of the conversion to UTF-8. |
324 | Swap(&time_zone->daylight_date); |
325 | Swap(&time_zone->daylight_bias); |
326 | } |
327 | |
328 | void ConvertUTF16BufferToUTF8String(const uint16_t* utf16_data, |
329 | size_t max_length_in_bytes, |
330 | string* utf8_result, |
331 | bool swap) { |
332 | // Since there is no explicit byte length for each string, use |
333 | // UTF16codeunits to calculate word length, then derive byte |
334 | // length from that. |
335 | size_t max_word_length = max_length_in_bytes / sizeof(utf16_data[0]); |
336 | size_t word_length = UTF16codeunits(utf16_data, max_word_length); |
337 | if (word_length > 0) { |
338 | size_t byte_length = word_length * sizeof(utf16_data[0]); |
339 | vector<uint16_t> utf16_vector(word_length); |
340 | memcpy(&utf16_vector[0], &utf16_data[0], byte_length); |
341 | scoped_ptr<string> temp(UTF16ToUTF8(utf16_vector, swap)); |
342 | if (temp.get()) { |
343 | utf8_result->assign(*temp); |
344 | } |
345 | } else { |
346 | utf8_result->clear(); |
347 | } |
348 | } |
349 | |
350 | |
351 | // For fields that may or may not be valid, PrintValueOrInvalid will print the |
352 | // string "(invalid)" if the field is not valid, and will print the value if |
353 | // the field is valid. The value is printed as hexadecimal or decimal. |
354 | |
355 | enum NumberFormat { |
356 | kNumberFormatDecimal, |
357 | kNumberFormatHexadecimal, |
358 | }; |
359 | |
360 | void PrintValueOrInvalid(bool valid, |
361 | NumberFormat number_format, |
362 | uint32_t value) { |
363 | if (!valid) { |
364 | printf("(invalid)\n" ); |
365 | } else if (number_format == kNumberFormatDecimal) { |
366 | printf("%d\n" , value); |
367 | } else { |
368 | printf("0x%x\n" , value); |
369 | } |
370 | } |
371 | |
372 | // Converts a time_t to a string showing the time in UTC. |
373 | string TimeTToUTCString(time_t tt) { |
374 | struct tm timestruct; |
375 | #ifdef _WIN32 |
376 | gmtime_s(×truct, &tt); |
377 | #else |
378 | gmtime_r(&tt, ×truct); |
379 | #endif |
380 | |
381 | char timestr[20]; |
382 | size_t rv = strftime(timestr, 20, "%Y-%m-%d %H:%M:%S" , ×truct); |
383 | if (rv == 0) { |
384 | return string(); |
385 | } |
386 | |
387 | return string(timestr); |
388 | } |
389 | |
390 | string MDGUIDToString(const MDGUID& uuid) { |
391 | char buf[37]; |
392 | snprintf(buf, sizeof(buf), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" , |
393 | uuid.data1, |
394 | uuid.data2, |
395 | uuid.data3, |
396 | uuid.data4[0], |
397 | uuid.data4[1], |
398 | uuid.data4[2], |
399 | uuid.data4[3], |
400 | uuid.data4[4], |
401 | uuid.data4[5], |
402 | uuid.data4[6], |
403 | uuid.data4[7]); |
404 | return std::string(buf); |
405 | } |
406 | |
407 | bool IsDevAshmem(const string& filename) { |
408 | const string kDevAshmem("/dev/ashmem/" ); |
409 | return filename.compare(0, kDevAshmem.length(), kDevAshmem) == 0; |
410 | } |
411 | |
412 | } // namespace |
413 | |
414 | // |
415 | // MinidumpObject |
416 | // |
417 | |
418 | |
419 | MinidumpObject::MinidumpObject(Minidump* minidump) |
420 | : DumpObject(), |
421 | minidump_(minidump) { |
422 | } |
423 | |
424 | |
425 | // |
426 | // MinidumpStream |
427 | // |
428 | |
429 | |
430 | MinidumpStream::MinidumpStream(Minidump* minidump) |
431 | : MinidumpObject(minidump) { |
432 | } |
433 | |
434 | |
435 | // |
436 | // MinidumpContext |
437 | // |
438 | |
439 | |
440 | MinidumpContext::MinidumpContext(Minidump* minidump) |
441 | : DumpContext(), |
442 | minidump_(minidump) { |
443 | } |
444 | |
445 | MinidumpContext::~MinidumpContext() { |
446 | } |
447 | |
448 | bool MinidumpContext::Read(uint32_t expected_size) { |
449 | valid_ = false; |
450 | |
451 | // Certain raw context types are currently assumed to have unique sizes. |
452 | if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) { |
453 | BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any " |
454 | << "other raw context" ; |
455 | return false; |
456 | } |
457 | if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) { |
458 | BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any " |
459 | << "other raw context" ; |
460 | return false; |
461 | } |
462 | if (!IsContextSizeUnique(sizeof(MDRawContextARM64_Old))) { |
463 | BPLOG(ERROR) << "sizeof(MDRawContextARM64_Old) cannot match the size of any " |
464 | << "other raw context" ; |
465 | return false; |
466 | } |
467 | |
468 | FreeContext(); |
469 | |
470 | // First, figure out what type of CPU this context structure is for. |
471 | // For some reason, the AMD64 Context doesn't have context_flags |
472 | // at the beginning of the structure, so special case it here. |
473 | |
474 | uint32_t sysinfo_cpu_type = 0; |
475 | if (!minidump_->GetContextCPUFlagsFromSystemInfo(&sysinfo_cpu_type)) { |
476 | BPLOG(ERROR) << "Failed to preserve the current stream position" ; |
477 | return false; |
478 | } |
479 | |
480 | if (expected_size == sizeof(MDRawContextAMD64) || |
481 | (sysinfo_cpu_type == MD_CONTEXT_AMD64 && |
482 | expected_size >= sizeof(MDRawContextAMD64))) { |
483 | BPLOG(INFO) << "MinidumpContext: looks like AMD64 context" ; |
484 | |
485 | scoped_ptr<MDRawContextAMD64> context_amd64(new MDRawContextAMD64()); |
486 | if (!minidump_->ReadBytes(context_amd64.get(), |
487 | sizeof(MDRawContextAMD64))) { |
488 | BPLOG(ERROR) << "MinidumpContext could not read amd64 context" ; |
489 | return false; |
490 | } |
491 | |
492 | // Context may include xsave registers and so be larger than |
493 | // sizeof(MDRawContextAMD64). For now we skip this extended data. |
494 | if (expected_size > sizeof(MDRawContextAMD64)) { |
495 | size_t bytes_left = expected_size - sizeof(MDRawContextAMD64); |
496 | std::vector<uint8_t> xstate(bytes_left); |
497 | if (!minidump_->ReadBytes(xstate.data(), |
498 | bytes_left)) { |
499 | BPLOG(ERROR) << "MinidumpContext could not skip amd64 xstate" ; |
500 | return false; |
501 | } |
502 | } |
503 | |
504 | if (minidump_->swap()) |
505 | Swap(&context_amd64->context_flags); |
506 | |
507 | uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK; |
508 | if (cpu_type == 0) { |
509 | context_amd64->context_flags |= sysinfo_cpu_type; |
510 | } |
511 | |
512 | if (cpu_type != MD_CONTEXT_AMD64) { |
513 | // TODO: Fall through to switch below. |
514 | // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 |
515 | BPLOG(ERROR) << "MinidumpContext not actually amd64 context" ; |
516 | return false; |
517 | } |
518 | |
519 | // Do this after reading the entire MDRawContext structure because |
520 | // GetSystemInfo may seek minidump to a new position. |
521 | if (!CheckAgainstSystemInfo(cpu_type)) { |
522 | BPLOG(ERROR) << "MinidumpContext amd64 does not match system info" ; |
523 | return false; |
524 | } |
525 | |
526 | // Normalize the 128-bit types in the dump. |
527 | // Since this is AMD64, by definition, the values are little-endian. |
528 | for (unsigned int vr_index = 0; |
529 | vr_index < MD_CONTEXT_AMD64_VR_COUNT; |
530 | ++vr_index) |
531 | Normalize128(&context_amd64->vector_register[vr_index], false); |
532 | |
533 | if (minidump_->swap()) { |
534 | Swap(&context_amd64->p1_home); |
535 | Swap(&context_amd64->p2_home); |
536 | Swap(&context_amd64->p3_home); |
537 | Swap(&context_amd64->p4_home); |
538 | Swap(&context_amd64->p5_home); |
539 | Swap(&context_amd64->p6_home); |
540 | // context_flags is already swapped |
541 | Swap(&context_amd64->mx_csr); |
542 | Swap(&context_amd64->cs); |
543 | Swap(&context_amd64->ds); |
544 | Swap(&context_amd64->es); |
545 | Swap(&context_amd64->fs); |
546 | Swap(&context_amd64->ss); |
547 | Swap(&context_amd64->eflags); |
548 | Swap(&context_amd64->dr0); |
549 | Swap(&context_amd64->dr1); |
550 | Swap(&context_amd64->dr2); |
551 | Swap(&context_amd64->dr3); |
552 | Swap(&context_amd64->dr6); |
553 | Swap(&context_amd64->dr7); |
554 | Swap(&context_amd64->rax); |
555 | Swap(&context_amd64->rcx); |
556 | Swap(&context_amd64->rdx); |
557 | Swap(&context_amd64->rbx); |
558 | Swap(&context_amd64->rsp); |
559 | Swap(&context_amd64->rbp); |
560 | Swap(&context_amd64->rsi); |
561 | Swap(&context_amd64->rdi); |
562 | Swap(&context_amd64->r8); |
563 | Swap(&context_amd64->r9); |
564 | Swap(&context_amd64->r10); |
565 | Swap(&context_amd64->r11); |
566 | Swap(&context_amd64->r12); |
567 | Swap(&context_amd64->r13); |
568 | Swap(&context_amd64->r14); |
569 | Swap(&context_amd64->r15); |
570 | Swap(&context_amd64->rip); |
571 | // FIXME: I'm not sure what actually determines |
572 | // which member of the union {flt_save, sse_registers} |
573 | // is valid. We're not currently using either, |
574 | // but it would be good to have them swapped properly. |
575 | |
576 | for (unsigned int vr_index = 0; |
577 | vr_index < MD_CONTEXT_AMD64_VR_COUNT; |
578 | ++vr_index) |
579 | Swap(&context_amd64->vector_register[vr_index]); |
580 | Swap(&context_amd64->vector_control); |
581 | Swap(&context_amd64->debug_control); |
582 | Swap(&context_amd64->last_branch_to_rip); |
583 | Swap(&context_amd64->last_branch_from_rip); |
584 | Swap(&context_amd64->last_exception_to_rip); |
585 | Swap(&context_amd64->last_exception_from_rip); |
586 | } |
587 | |
588 | SetContextFlags(context_amd64->context_flags); |
589 | |
590 | SetContextAMD64(context_amd64.release()); |
591 | } else if (expected_size == sizeof(MDRawContextPPC64)) { |
592 | // |context_flags| of MDRawContextPPC64 is 64 bits, but other MDRawContext |
593 | // in the else case have 32 bits |context_flags|, so special case it here. |
594 | uint64_t context_flags; |
595 | if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { |
596 | BPLOG(ERROR) << "MinidumpContext could not read context flags" ; |
597 | return false; |
598 | } |
599 | if (minidump_->swap()) |
600 | Swap(&context_flags); |
601 | |
602 | uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; |
603 | scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64()); |
604 | |
605 | if (cpu_type == 0) { |
606 | if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { |
607 | context_ppc64->context_flags |= cpu_type; |
608 | } else { |
609 | BPLOG(ERROR) << "Failed to preserve the current stream position" ; |
610 | return false; |
611 | } |
612 | } |
613 | |
614 | if (cpu_type != MD_CONTEXT_PPC64) { |
615 | // TODO: Fall through to switch below. |
616 | // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 |
617 | BPLOG(ERROR) << "MinidumpContext not actually ppc64 context" ; |
618 | return false; |
619 | } |
620 | |
621 | // Set the context_flags member, which has already been read, and |
622 | // read the rest of the structure beginning with the first member |
623 | // after context_flags. |
624 | context_ppc64->context_flags = context_flags; |
625 | |
626 | size_t flags_size = sizeof(context_ppc64->context_flags); |
627 | uint8_t* context_after_flags = |
628 | reinterpret_cast<uint8_t*>(context_ppc64.get()) + flags_size; |
629 | if (!minidump_->ReadBytes(context_after_flags, |
630 | sizeof(MDRawContextPPC64) - flags_size)) { |
631 | BPLOG(ERROR) << "MinidumpContext could not read ppc64 context" ; |
632 | return false; |
633 | } |
634 | |
635 | // Do this after reading the entire MDRawContext structure because |
636 | // GetSystemInfo may seek minidump to a new position. |
637 | if (!CheckAgainstSystemInfo(cpu_type)) { |
638 | BPLOG(ERROR) << "MinidumpContext ppc64 does not match system info" ; |
639 | return false; |
640 | } |
641 | if (minidump_->swap()) { |
642 | // context_ppc64->context_flags was already swapped. |
643 | Swap(&context_ppc64->srr0); |
644 | Swap(&context_ppc64->srr1); |
645 | for (unsigned int gpr_index = 0; |
646 | gpr_index < MD_CONTEXT_PPC64_GPR_COUNT; |
647 | ++gpr_index) { |
648 | Swap(&context_ppc64->gpr[gpr_index]); |
649 | } |
650 | Swap(&context_ppc64->cr); |
651 | Swap(&context_ppc64->xer); |
652 | Swap(&context_ppc64->lr); |
653 | Swap(&context_ppc64->ctr); |
654 | Swap(&context_ppc64->vrsave); |
655 | for (unsigned int fpr_index = 0; |
656 | fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; |
657 | ++fpr_index) { |
658 | Swap(&context_ppc64->float_save.fpregs[fpr_index]); |
659 | } |
660 | // Don't swap context_ppc64->float_save.fpscr_pad because it is only |
661 | // used for padding. |
662 | Swap(&context_ppc64->float_save.fpscr); |
663 | for (unsigned int vr_index = 0; |
664 | vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; |
665 | ++vr_index) { |
666 | Normalize128(&context_ppc64->vector_save.save_vr[vr_index], true); |
667 | Swap(&context_ppc64->vector_save.save_vr[vr_index]); |
668 | } |
669 | Swap(&context_ppc64->vector_save.save_vscr); |
670 | // Don't swap the padding fields in vector_save. |
671 | Swap(&context_ppc64->vector_save.save_vrvalid); |
672 | } |
673 | |
674 | SetContextFlags(static_cast<uint32_t>(context_ppc64->context_flags)); |
675 | |
676 | // Check for data loss when converting context flags from uint64_t into |
677 | // uint32_t |
678 | if (static_cast<uint64_t>(GetContextFlags()) != |
679 | context_ppc64->context_flags) { |
680 | BPLOG(ERROR) << "Data loss detected when converting PPC64 context_flags" ; |
681 | return false; |
682 | } |
683 | |
684 | SetContextPPC64(context_ppc64.release()); |
685 | } else if (expected_size == sizeof(MDRawContextARM64_Old)) { |
686 | // |context_flags| of MDRawContextARM64_Old is 64 bits, but other MDRawContext |
687 | // in the else case have 32 bits |context_flags|, so special case it here. |
688 | uint64_t context_flags; |
689 | |
690 | BPLOG(INFO) << "MinidumpContext: looks like ARM64 context" ; |
691 | |
692 | if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { |
693 | BPLOG(ERROR) << "MinidumpContext could not read context flags" ; |
694 | return false; |
695 | } |
696 | if (minidump_->swap()) |
697 | Swap(&context_flags); |
698 | |
699 | scoped_ptr<MDRawContextARM64_Old> context_arm64(new MDRawContextARM64_Old()); |
700 | |
701 | uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; |
702 | if (cpu_type == 0) { |
703 | if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { |
704 | context_arm64->context_flags |= cpu_type; |
705 | } else { |
706 | BPLOG(ERROR) << "Failed to preserve the current stream position" ; |
707 | return false; |
708 | } |
709 | } |
710 | |
711 | if (cpu_type != MD_CONTEXT_ARM64_OLD) { |
712 | // TODO: Fall through to switch below. |
713 | // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=550 |
714 | BPLOG(ERROR) << "MinidumpContext not actually arm64 context" ; |
715 | return false; |
716 | } |
717 | |
718 | // Set the context_flags member, which has already been read, and |
719 | // read the rest of the structure beginning with the first member |
720 | // after context_flags. |
721 | context_arm64->context_flags = context_flags; |
722 | |
723 | size_t flags_size = sizeof(context_arm64->context_flags); |
724 | uint8_t* context_after_flags = |
725 | reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size; |
726 | if (!minidump_->ReadBytes(context_after_flags, |
727 | sizeof(MDRawContextARM64_Old) - flags_size)) { |
728 | BPLOG(ERROR) << "MinidumpContext could not read arm64 context" ; |
729 | return false; |
730 | } |
731 | |
732 | // Do this after reading the entire MDRawContext structure because |
733 | // GetSystemInfo may seek minidump to a new position. |
734 | if (!CheckAgainstSystemInfo(cpu_type)) { |
735 | BPLOG(ERROR) << "MinidumpContext arm64 does not match system info" ; |
736 | return false; |
737 | } |
738 | |
739 | if (minidump_->swap()) { |
740 | // context_arm64->context_flags was already swapped. |
741 | for (unsigned int ireg_index = 0; |
742 | ireg_index < MD_CONTEXT_ARM64_GPR_COUNT; |
743 | ++ireg_index) { |
744 | Swap(&context_arm64->iregs[ireg_index]); |
745 | } |
746 | Swap(&context_arm64->cpsr); |
747 | Swap(&context_arm64->float_save.fpsr); |
748 | Swap(&context_arm64->float_save.fpcr); |
749 | for (unsigned int fpr_index = 0; |
750 | fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT; |
751 | ++fpr_index) { |
752 | Normalize128(&context_arm64->float_save.regs[fpr_index], |
753 | minidump_->is_big_endian()); |
754 | Swap(&context_arm64->float_save.regs[fpr_index]); |
755 | } |
756 | } |
757 | |
758 | scoped_ptr<MDRawContextARM64> new_context(new MDRawContextARM64()); |
759 | ConvertOldARM64Context(*context_arm64.get(), new_context.get()); |
760 | SetContextFlags(new_context->context_flags); |
761 | SetContextARM64(new_context.release()); |
762 | } else { |
763 | uint32_t context_flags; |
764 | if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { |
765 | BPLOG(ERROR) << "MinidumpContext could not read context flags" ; |
766 | return false; |
767 | } |
768 | if (minidump_->swap()) |
769 | Swap(&context_flags); |
770 | |
771 | uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; |
772 | if (cpu_type == 0) { |
773 | // Unfortunately the flag for MD_CONTEXT_ARM that was taken |
774 | // from a Windows CE SDK header conflicts in practice with |
775 | // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered, |
776 | // but handle dumps with the legacy value gracefully here. |
777 | if (context_flags & MD_CONTEXT_ARM_OLD) { |
778 | context_flags |= MD_CONTEXT_ARM; |
779 | context_flags &= ~MD_CONTEXT_ARM_OLD; |
780 | cpu_type = MD_CONTEXT_ARM; |
781 | } |
782 | } |
783 | |
784 | // Fixup if we were not provided a cpu type. |
785 | if (cpu_type == 0) { |
786 | cpu_type = sysinfo_cpu_type; |
787 | context_flags |= cpu_type; |
788 | } |
789 | |
790 | // Allocate the context structure for the correct CPU and fill it. The |
791 | // casts are slightly unorthodox, but it seems better to do that than to |
792 | // maintain a separate pointer for each type of CPU context structure |
793 | // when only one of them will be used. |
794 | switch (cpu_type) { |
795 | case MD_CONTEXT_X86: { |
796 | if (expected_size != sizeof(MDRawContextX86)) { |
797 | BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << |
798 | expected_size << " != " << sizeof(MDRawContextX86); |
799 | return false; |
800 | } |
801 | |
802 | scoped_ptr<MDRawContextX86> context_x86(new MDRawContextX86()); |
803 | |
804 | // Set the context_flags member, which has already been read, and |
805 | // read the rest of the structure beginning with the first member |
806 | // after context_flags. |
807 | context_x86->context_flags = context_flags; |
808 | |
809 | size_t flags_size = sizeof(context_x86->context_flags); |
810 | uint8_t* context_after_flags = |
811 | reinterpret_cast<uint8_t*>(context_x86.get()) + flags_size; |
812 | if (!minidump_->ReadBytes(context_after_flags, |
813 | sizeof(MDRawContextX86) - flags_size)) { |
814 | BPLOG(ERROR) << "MinidumpContext could not read x86 context" ; |
815 | return false; |
816 | } |
817 | |
818 | // Do this after reading the entire MDRawContext structure because |
819 | // GetSystemInfo may seek minidump to a new position. |
820 | if (!CheckAgainstSystemInfo(cpu_type)) { |
821 | BPLOG(ERROR) << "MinidumpContext x86 does not match system info" ; |
822 | return false; |
823 | } |
824 | |
825 | if (minidump_->swap()) { |
826 | // context_x86->context_flags was already swapped. |
827 | Swap(&context_x86->dr0); |
828 | Swap(&context_x86->dr1); |
829 | Swap(&context_x86->dr2); |
830 | Swap(&context_x86->dr3); |
831 | Swap(&context_x86->dr6); |
832 | Swap(&context_x86->dr7); |
833 | Swap(&context_x86->float_save.control_word); |
834 | Swap(&context_x86->float_save.status_word); |
835 | Swap(&context_x86->float_save.tag_word); |
836 | Swap(&context_x86->float_save.error_offset); |
837 | Swap(&context_x86->float_save.error_selector); |
838 | Swap(&context_x86->float_save.data_offset); |
839 | Swap(&context_x86->float_save.data_selector); |
840 | // context_x86->float_save.register_area[] contains 8-bit quantities |
841 | // and does not need to be swapped. |
842 | Swap(&context_x86->float_save.cr0_npx_state); |
843 | Swap(&context_x86->gs); |
844 | Swap(&context_x86->fs); |
845 | Swap(&context_x86->es); |
846 | Swap(&context_x86->ds); |
847 | Swap(&context_x86->edi); |
848 | Swap(&context_x86->esi); |
849 | Swap(&context_x86->ebx); |
850 | Swap(&context_x86->edx); |
851 | Swap(&context_x86->ecx); |
852 | Swap(&context_x86->eax); |
853 | Swap(&context_x86->ebp); |
854 | Swap(&context_x86->eip); |
855 | Swap(&context_x86->cs); |
856 | Swap(&context_x86->eflags); |
857 | Swap(&context_x86->esp); |
858 | Swap(&context_x86->ss); |
859 | // context_x86->extended_registers[] contains 8-bit quantities and |
860 | // does not need to be swapped. |
861 | } |
862 | |
863 | SetContextX86(context_x86.release()); |
864 | |
865 | break; |
866 | } |
867 | |
868 | case MD_CONTEXT_PPC: { |
869 | if (expected_size != sizeof(MDRawContextPPC)) { |
870 | BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << |
871 | expected_size << " != " << sizeof(MDRawContextPPC); |
872 | return false; |
873 | } |
874 | |
875 | scoped_ptr<MDRawContextPPC> context_ppc(new MDRawContextPPC()); |
876 | |
877 | // Set the context_flags member, which has already been read, and |
878 | // read the rest of the structure beginning with the first member |
879 | // after context_flags. |
880 | context_ppc->context_flags = context_flags; |
881 | |
882 | size_t flags_size = sizeof(context_ppc->context_flags); |
883 | uint8_t* context_after_flags = |
884 | reinterpret_cast<uint8_t*>(context_ppc.get()) + flags_size; |
885 | if (!minidump_->ReadBytes(context_after_flags, |
886 | sizeof(MDRawContextPPC) - flags_size)) { |
887 | BPLOG(ERROR) << "MinidumpContext could not read ppc context" ; |
888 | return false; |
889 | } |
890 | |
891 | // Do this after reading the entire MDRawContext structure because |
892 | // GetSystemInfo may seek minidump to a new position. |
893 | if (!CheckAgainstSystemInfo(cpu_type)) { |
894 | BPLOG(ERROR) << "MinidumpContext ppc does not match system info" ; |
895 | return false; |
896 | } |
897 | |
898 | // Normalize the 128-bit types in the dump. |
899 | // Since this is PowerPC, by definition, the values are big-endian. |
900 | for (unsigned int vr_index = 0; |
901 | vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; |
902 | ++vr_index) { |
903 | Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); |
904 | } |
905 | |
906 | if (minidump_->swap()) { |
907 | // context_ppc->context_flags was already swapped. |
908 | Swap(&context_ppc->srr0); |
909 | Swap(&context_ppc->srr1); |
910 | for (unsigned int gpr_index = 0; |
911 | gpr_index < MD_CONTEXT_PPC_GPR_COUNT; |
912 | ++gpr_index) { |
913 | Swap(&context_ppc->gpr[gpr_index]); |
914 | } |
915 | Swap(&context_ppc->cr); |
916 | Swap(&context_ppc->xer); |
917 | Swap(&context_ppc->lr); |
918 | Swap(&context_ppc->ctr); |
919 | Swap(&context_ppc->mq); |
920 | Swap(&context_ppc->vrsave); |
921 | for (unsigned int fpr_index = 0; |
922 | fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; |
923 | ++fpr_index) { |
924 | Swap(&context_ppc->float_save.fpregs[fpr_index]); |
925 | } |
926 | // Don't swap context_ppc->float_save.fpscr_pad because it is only |
927 | // used for padding. |
928 | Swap(&context_ppc->float_save.fpscr); |
929 | for (unsigned int vr_index = 0; |
930 | vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; |
931 | ++vr_index) { |
932 | Swap(&context_ppc->vector_save.save_vr[vr_index]); |
933 | } |
934 | Swap(&context_ppc->vector_save.save_vscr); |
935 | // Don't swap the padding fields in vector_save. |
936 | Swap(&context_ppc->vector_save.save_vrvalid); |
937 | } |
938 | |
939 | SetContextPPC(context_ppc.release()); |
940 | |
941 | break; |
942 | } |
943 | |
944 | case MD_CONTEXT_SPARC: { |
945 | if (expected_size != sizeof(MDRawContextSPARC)) { |
946 | BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << |
947 | expected_size << " != " << sizeof(MDRawContextSPARC); |
948 | return false; |
949 | } |
950 | |
951 | scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC()); |
952 | |
953 | // Set the context_flags member, which has already been read, and |
954 | // read the rest of the structure beginning with the first member |
955 | // after context_flags. |
956 | context_sparc->context_flags = context_flags; |
957 | |
958 | size_t flags_size = sizeof(context_sparc->context_flags); |
959 | uint8_t* context_after_flags = |
960 | reinterpret_cast<uint8_t*>(context_sparc.get()) + flags_size; |
961 | if (!minidump_->ReadBytes(context_after_flags, |
962 | sizeof(MDRawContextSPARC) - flags_size)) { |
963 | BPLOG(ERROR) << "MinidumpContext could not read sparc context" ; |
964 | return false; |
965 | } |
966 | |
967 | // Do this after reading the entire MDRawContext structure because |
968 | // GetSystemInfo may seek minidump to a new position. |
969 | if (!CheckAgainstSystemInfo(cpu_type)) { |
970 | BPLOG(ERROR) << "MinidumpContext sparc does not match system info" ; |
971 | return false; |
972 | } |
973 | |
974 | if (minidump_->swap()) { |
975 | // context_sparc->context_flags was already swapped. |
976 | for (unsigned int gpr_index = 0; |
977 | gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; |
978 | ++gpr_index) { |
979 | Swap(&context_sparc->g_r[gpr_index]); |
980 | } |
981 | Swap(&context_sparc->ccr); |
982 | Swap(&context_sparc->pc); |
983 | Swap(&context_sparc->npc); |
984 | Swap(&context_sparc->y); |
985 | Swap(&context_sparc->asi); |
986 | Swap(&context_sparc->fprs); |
987 | for (unsigned int fpr_index = 0; |
988 | fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; |
989 | ++fpr_index) { |
990 | Swap(&context_sparc->float_save.regs[fpr_index]); |
991 | } |
992 | Swap(&context_sparc->float_save.filler); |
993 | Swap(&context_sparc->float_save.fsr); |
994 | } |
995 | SetContextSPARC(context_sparc.release()); |
996 | |
997 | break; |
998 | } |
999 | |
1000 | case MD_CONTEXT_ARM: { |
1001 | if (expected_size != sizeof(MDRawContextARM)) { |
1002 | BPLOG(ERROR) << "MinidumpContext arm size mismatch, " << |
1003 | expected_size << " != " << sizeof(MDRawContextARM); |
1004 | return false; |
1005 | } |
1006 | |
1007 | scoped_ptr<MDRawContextARM> context_arm(new MDRawContextARM()); |
1008 | |
1009 | // Set the context_flags member, which has already been read, and |
1010 | // read the rest of the structure beginning with the first member |
1011 | // after context_flags. |
1012 | context_arm->context_flags = context_flags; |
1013 | |
1014 | size_t flags_size = sizeof(context_arm->context_flags); |
1015 | uint8_t* context_after_flags = |
1016 | reinterpret_cast<uint8_t*>(context_arm.get()) + flags_size; |
1017 | if (!minidump_->ReadBytes(context_after_flags, |
1018 | sizeof(MDRawContextARM) - flags_size)) { |
1019 | BPLOG(ERROR) << "MinidumpContext could not read arm context" ; |
1020 | return false; |
1021 | } |
1022 | |
1023 | // Do this after reading the entire MDRawContext structure because |
1024 | // GetSystemInfo may seek minidump to a new position. |
1025 | if (!CheckAgainstSystemInfo(cpu_type)) { |
1026 | BPLOG(ERROR) << "MinidumpContext arm does not match system info" ; |
1027 | return false; |
1028 | } |
1029 | |
1030 | if (minidump_->swap()) { |
1031 | // context_arm->context_flags was already swapped. |
1032 | for (unsigned int ireg_index = 0; |
1033 | ireg_index < MD_CONTEXT_ARM_GPR_COUNT; |
1034 | ++ireg_index) { |
1035 | Swap(&context_arm->iregs[ireg_index]); |
1036 | } |
1037 | Swap(&context_arm->cpsr); |
1038 | Swap(&context_arm->float_save.fpscr); |
1039 | for (unsigned int fpr_index = 0; |
1040 | fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; |
1041 | ++fpr_index) { |
1042 | Swap(&context_arm->float_save.regs[fpr_index]); |
1043 | } |
1044 | for (unsigned int fpe_index = 0; |
1045 | fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; |
1046 | ++fpe_index) { |
1047 | Swap(&context_arm->float_save.extra[fpe_index]); |
1048 | } |
1049 | } |
1050 | SetContextARM(context_arm.release()); |
1051 | |
1052 | break; |
1053 | } |
1054 | |
1055 | case MD_CONTEXT_ARM64: { |
1056 | if (expected_size != sizeof(MDRawContextARM64)) { |
1057 | BPLOG(ERROR) << "MinidumpContext arm64 size mismatch, " << |
1058 | expected_size << " != " << sizeof(MDRawContextARM64); |
1059 | return false; |
1060 | } |
1061 | |
1062 | scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64()); |
1063 | |
1064 | // Set the context_flags member, which has already been read, and |
1065 | // read the rest of the structure beginning with the first member |
1066 | // after context_flags. |
1067 | context_arm64->context_flags = context_flags; |
1068 | |
1069 | size_t flags_size = sizeof(context_arm64->context_flags); |
1070 | uint8_t* context_after_flags = |
1071 | reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size; |
1072 | if (!minidump_->ReadBytes(context_after_flags, |
1073 | sizeof(*context_arm64) - flags_size)) { |
1074 | BPLOG(ERROR) << "MinidumpContext could not read arm64 context" ; |
1075 | return false; |
1076 | } |
1077 | |
1078 | // Do this after reading the entire MDRawContext structure because |
1079 | // GetSystemInfo may seek minidump to a new position. |
1080 | if (!CheckAgainstSystemInfo(cpu_type)) { |
1081 | BPLOG(ERROR) << "MinidumpContext arm does not match system info" ; |
1082 | return false; |
1083 | } |
1084 | |
1085 | if (minidump_->swap()) { |
1086 | // context_arm64->context_flags was already swapped. |
1087 | for (unsigned int ireg_index = 0; |
1088 | ireg_index < MD_CONTEXT_ARM64_GPR_COUNT; |
1089 | ++ireg_index) { |
1090 | Swap(&context_arm64->iregs[ireg_index]); |
1091 | } |
1092 | Swap(&context_arm64->cpsr); |
1093 | Swap(&context_arm64->float_save.fpsr); |
1094 | Swap(&context_arm64->float_save.fpcr); |
1095 | for (unsigned int fpr_index = 0; |
1096 | fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT; |
1097 | ++fpr_index) { |
1098 | Normalize128(&context_arm64->float_save.regs[fpr_index], |
1099 | minidump_->is_big_endian()); |
1100 | Swap(&context_arm64->float_save.regs[fpr_index]); |
1101 | } |
1102 | } |
1103 | SetContextARM64(context_arm64.release()); |
1104 | break; |
1105 | } |
1106 | |
1107 | case MD_CONTEXT_MIPS: |
1108 | case MD_CONTEXT_MIPS64: { |
1109 | if (expected_size != sizeof(MDRawContextMIPS)) { |
1110 | BPLOG(ERROR) << "MinidumpContext MIPS size mismatch, " |
1111 | << expected_size |
1112 | << " != " |
1113 | << sizeof(MDRawContextMIPS); |
1114 | return false; |
1115 | } |
1116 | |
1117 | scoped_ptr<MDRawContextMIPS> context_mips(new MDRawContextMIPS()); |
1118 | |
1119 | // Set the context_flags member, which has already been read, and |
1120 | // read the rest of the structure beginning with the first member |
1121 | // after context_flags. |
1122 | context_mips->context_flags = context_flags; |
1123 | |
1124 | size_t flags_size = sizeof(context_mips->context_flags); |
1125 | uint8_t* context_after_flags = |
1126 | reinterpret_cast<uint8_t*>(context_mips.get()) + flags_size; |
1127 | if (!minidump_->ReadBytes(context_after_flags, |
1128 | sizeof(MDRawContextMIPS) - flags_size)) { |
1129 | BPLOG(ERROR) << "MinidumpContext could not read MIPS context" ; |
1130 | return false; |
1131 | } |
1132 | |
1133 | // Do this after reading the entire MDRawContext structure because |
1134 | // GetSystemInfo may seek minidump to a new position. |
1135 | if (!CheckAgainstSystemInfo(cpu_type)) { |
1136 | BPLOG(ERROR) << "MinidumpContext MIPS does not match system info" ; |
1137 | return false; |
1138 | } |
1139 | |
1140 | if (minidump_->swap()) { |
1141 | // context_mips->context_flags was already swapped. |
1142 | for (int ireg_index = 0; |
1143 | ireg_index < MD_CONTEXT_MIPS_GPR_COUNT; |
1144 | ++ireg_index) { |
1145 | Swap(&context_mips->iregs[ireg_index]); |
1146 | } |
1147 | Swap(&context_mips->mdhi); |
1148 | Swap(&context_mips->mdlo); |
1149 | for (int dsp_index = 0; |
1150 | dsp_index < MD_CONTEXT_MIPS_DSP_COUNT; |
1151 | ++dsp_index) { |
1152 | Swap(&context_mips->hi[dsp_index]); |
1153 | Swap(&context_mips->lo[dsp_index]); |
1154 | } |
1155 | Swap(&context_mips->dsp_control); |
1156 | Swap(&context_mips->epc); |
1157 | Swap(&context_mips->badvaddr); |
1158 | Swap(&context_mips->status); |
1159 | Swap(&context_mips->cause); |
1160 | for (int fpr_index = 0; |
1161 | fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; |
1162 | ++fpr_index) { |
1163 | Swap(&context_mips->float_save.regs[fpr_index]); |
1164 | } |
1165 | Swap(&context_mips->float_save.fpcsr); |
1166 | Swap(&context_mips->float_save.fir); |
1167 | } |
1168 | SetContextMIPS(context_mips.release()); |
1169 | |
1170 | break; |
1171 | } |
1172 | |
1173 | default: { |
1174 | // Unknown context type - Don't log as an error yet. Let the |
1175 | // caller work that out. |
1176 | BPLOG(INFO) << "MinidumpContext unknown context type " << |
1177 | HexString(cpu_type); |
1178 | return false; |
1179 | } |
1180 | } |
1181 | SetContextFlags(context_flags); |
1182 | } |
1183 | |
1184 | valid_ = true; |
1185 | return true; |
1186 | } |
1187 | |
1188 | bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) { |
1189 | // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM, |
1190 | // as this function just implements a sanity check. |
1191 | MinidumpSystemInfo* system_info = minidump_->GetSystemInfo(); |
1192 | if (!system_info) { |
1193 | BPLOG(INFO) << "MinidumpContext could not be compared against " |
1194 | "MinidumpSystemInfo" ; |
1195 | return true; |
1196 | } |
1197 | |
1198 | // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info. |
1199 | const MDRawSystemInfo* raw_system_info = system_info->system_info(); |
1200 | if (!raw_system_info) { |
1201 | BPLOG(INFO) << "MinidumpContext could not be compared against " |
1202 | "MDRawSystemInfo" ; |
1203 | return false; |
1204 | } |
1205 | |
1206 | MDCPUArchitecture system_info_cpu_type = static_cast<MDCPUArchitecture>( |
1207 | raw_system_info->processor_architecture); |
1208 | |
1209 | // Compare the CPU type of the context record to the CPU type in the |
1210 | // minidump's system info stream. |
1211 | bool return_value = false; |
1212 | switch (context_cpu_type) { |
1213 | case MD_CONTEXT_X86: |
1214 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 || |
1215 | system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 || |
1216 | system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) { |
1217 | return_value = true; |
1218 | } |
1219 | break; |
1220 | |
1221 | case MD_CONTEXT_PPC: |
1222 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC) |
1223 | return_value = true; |
1224 | break; |
1225 | |
1226 | case MD_CONTEXT_PPC64: |
1227 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC64) |
1228 | return_value = true; |
1229 | break; |
1230 | |
1231 | case MD_CONTEXT_AMD64: |
1232 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) |
1233 | return_value = true; |
1234 | break; |
1235 | |
1236 | case MD_CONTEXT_SPARC: |
1237 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) |
1238 | return_value = true; |
1239 | break; |
1240 | |
1241 | case MD_CONTEXT_ARM: |
1242 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM) |
1243 | return_value = true; |
1244 | break; |
1245 | |
1246 | case MD_CONTEXT_ARM64: |
1247 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64) |
1248 | return_value = true; |
1249 | break; |
1250 | |
1251 | case MD_CONTEXT_ARM64_OLD: |
1252 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64_OLD) |
1253 | return_value = true; |
1254 | break; |
1255 | |
1256 | case MD_CONTEXT_MIPS: |
1257 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS) |
1258 | return_value = true; |
1259 | break; |
1260 | |
1261 | case MD_CONTEXT_MIPS64: |
1262 | if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64) |
1263 | return_value = true; |
1264 | break; |
1265 | } |
1266 | |
1267 | BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << |
1268 | HexString(context_cpu_type) << |
1269 | " wrong for MinidumpSystemInfo CPU " << |
1270 | HexString(system_info_cpu_type); |
1271 | |
1272 | return return_value; |
1273 | } |
1274 | |
1275 | |
1276 | // |
1277 | // MinidumpMemoryRegion |
1278 | // |
1279 | |
1280 | |
1281 | uint32_t MinidumpMemoryRegion::max_bytes_ = 64 * 1024 * 1024; // 64MB |
1282 | |
1283 | |
1284 | MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) |
1285 | : MinidumpObject(minidump), |
1286 | descriptor_(NULL), |
1287 | memory_(NULL) { |
1288 | hexdump_width_ = minidump_ ? minidump_->HexdumpMode() : 0; |
1289 | hexdump_ = hexdump_width_ != 0; |
1290 | } |
1291 | |
1292 | |
1293 | MinidumpMemoryRegion::~MinidumpMemoryRegion() { |
1294 | delete memory_; |
1295 | } |
1296 | |
1297 | |
1298 | void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) { |
1299 | descriptor_ = descriptor; |
1300 | valid_ = descriptor && |
1301 | descriptor_->memory.data_size <= |
1302 | numeric_limits<uint64_t>::max() - |
1303 | descriptor_->start_of_memory_range; |
1304 | } |
1305 | |
1306 | |
1307 | const uint8_t* MinidumpMemoryRegion::GetMemory() const { |
1308 | if (!valid_) { |
1309 | BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory" ; |
1310 | return NULL; |
1311 | } |
1312 | |
1313 | if (!memory_) { |
1314 | if (descriptor_->memory.data_size == 0) { |
1315 | BPLOG(ERROR) << "MinidumpMemoryRegion is empty" ; |
1316 | return NULL; |
1317 | } |
1318 | |
1319 | if (!minidump_->SeekSet(descriptor_->memory.rva)) { |
1320 | BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region" ; |
1321 | return NULL; |
1322 | } |
1323 | |
1324 | if (descriptor_->memory.data_size > max_bytes_) { |
1325 | BPLOG(ERROR) << "MinidumpMemoryRegion size " << |
1326 | descriptor_->memory.data_size << " exceeds maximum " << |
1327 | max_bytes_; |
1328 | return NULL; |
1329 | } |
1330 | |
1331 | scoped_ptr< vector<uint8_t> > memory( |
1332 | new vector<uint8_t>(descriptor_->memory.data_size)); |
1333 | |
1334 | if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) { |
1335 | BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region" ; |
1336 | return NULL; |
1337 | } |
1338 | |
1339 | memory_ = memory.release(); |
1340 | } |
1341 | |
1342 | return &(*memory_)[0]; |
1343 | } |
1344 | |
1345 | |
1346 | uint64_t MinidumpMemoryRegion::GetBase() const { |
1347 | if (!valid_) { |
1348 | BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase" ; |
1349 | return static_cast<uint64_t>(-1); |
1350 | } |
1351 | |
1352 | return descriptor_->start_of_memory_range; |
1353 | } |
1354 | |
1355 | |
1356 | uint32_t MinidumpMemoryRegion::GetSize() const { |
1357 | if (!valid_) { |
1358 | BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize" ; |
1359 | return 0; |
1360 | } |
1361 | |
1362 | return descriptor_->memory.data_size; |
1363 | } |
1364 | |
1365 | |
1366 | void MinidumpMemoryRegion::FreeMemory() { |
1367 | delete memory_; |
1368 | memory_ = NULL; |
1369 | } |
1370 | |
1371 | |
1372 | template<typename T> |
1373 | bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address, |
1374 | T* value) const { |
1375 | BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal " |
1376 | "requires |value|" ; |
1377 | assert(value); |
1378 | *value = 0; |
1379 | |
1380 | if (!valid_) { |
1381 | BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for " |
1382 | "GetMemoryAtAddressInternal" ; |
1383 | return false; |
1384 | } |
1385 | |
1386 | // Common failure case |
1387 | if (address < descriptor_->start_of_memory_range || |
1388 | sizeof(T) > numeric_limits<uint64_t>::max() - address || |
1389 | address + sizeof(T) > descriptor_->start_of_memory_range + |
1390 | descriptor_->memory.data_size) { |
1391 | BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " << |
1392 | HexString(address) << "+" << sizeof(T) << "/" << |
1393 | HexString(descriptor_->start_of_memory_range) << "+" << |
1394 | HexString(descriptor_->memory.data_size); |
1395 | return false; |
1396 | } |
1397 | |
1398 | const uint8_t* memory = GetMemory(); |
1399 | if (!memory) { |
1400 | // GetMemory already logged a perfectly good message. |
1401 | return false; |
1402 | } |
1403 | |
1404 | // If the CPU requires memory accesses to be aligned, this can crash. |
1405 | // x86 and ppc are able to cope, though. |
1406 | *value = *reinterpret_cast<const T*>( |
1407 | &memory[address - descriptor_->start_of_memory_range]); |
1408 | |
1409 | if (minidump_->swap()) |
1410 | Swap(value); |
1411 | |
1412 | return true; |
1413 | } |
1414 | |
1415 | |
1416 | bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
1417 | uint8_t* value) const { |
1418 | return GetMemoryAtAddressInternal(address, value); |
1419 | } |
1420 | |
1421 | |
1422 | bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
1423 | uint16_t* value) const { |
1424 | return GetMemoryAtAddressInternal(address, value); |
1425 | } |
1426 | |
1427 | |
1428 | bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
1429 | uint32_t* value) const { |
1430 | return GetMemoryAtAddressInternal(address, value); |
1431 | } |
1432 | |
1433 | |
1434 | bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
1435 | uint64_t* value) const { |
1436 | return GetMemoryAtAddressInternal(address, value); |
1437 | } |
1438 | |
1439 | |
1440 | void MinidumpMemoryRegion::Print() const { |
1441 | if (!valid_) { |
1442 | BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data" ; |
1443 | return; |
1444 | } |
1445 | |
1446 | const uint8_t* memory = GetMemory(); |
1447 | if (memory) { |
1448 | if (hexdump_) { |
1449 | // Pretty hexdump view. |
1450 | for (unsigned int byte_index = 0; |
1451 | byte_index < descriptor_->memory.data_size; |
1452 | byte_index += hexdump_width_) { |
1453 | // In case the memory won't fill a whole line. |
1454 | unsigned int num_bytes = std::min( |
1455 | descriptor_->memory.data_size - byte_index, hexdump_width_); |
1456 | |
1457 | // Display the leading address. |
1458 | printf("%08x " , byte_index); |
1459 | |
1460 | // Show the bytes in hex. |
1461 | for (unsigned int i = 0; i < hexdump_width_; ++i) { |
1462 | if (i < num_bytes) { |
1463 | // Show the single byte of memory in hex. |
1464 | printf("%02x " , memory[byte_index + i]); |
1465 | } else { |
1466 | // If this line doesn't fill up, pad it out. |
1467 | printf(" " ); |
1468 | } |
1469 | |
1470 | // Insert a space every 8 bytes to make it more readable. |
1471 | if (((i + 1) % 8) == 0) { |
1472 | printf(" " ); |
1473 | } |
1474 | } |
1475 | |
1476 | // Decode the line as ASCII. |
1477 | printf("|" ); |
1478 | for (unsigned int i = 0; i < hexdump_width_; ++i) { |
1479 | if (i < num_bytes) { |
1480 | uint8_t byte = memory[byte_index + i]; |
1481 | printf("%c" , isprint(byte) ? byte : '.'); |
1482 | } else { |
1483 | // If this line doesn't fill up, pad it out. |
1484 | printf(" " ); |
1485 | } |
1486 | } |
1487 | printf("|\n" ); |
1488 | } |
1489 | } else { |
1490 | // Ugly raw string view. |
1491 | printf("0x" ); |
1492 | for (unsigned int i = 0; |
1493 | i < descriptor_->memory.data_size; |
1494 | i++) { |
1495 | printf("%02x" , memory[i]); |
1496 | } |
1497 | printf("\n" ); |
1498 | } |
1499 | } else { |
1500 | printf("No memory\n" ); |
1501 | } |
1502 | } |
1503 | |
1504 | |
1505 | void MinidumpMemoryRegion::SetPrintMode(bool hexdump, |
1506 | unsigned int hexdump_width) { |
1507 | // Require the width to be a multiple of 8 bytes. |
1508 | if (hexdump_width == 0 || (hexdump_width % 8) != 0) { |
1509 | BPLOG(ERROR) << "MinidumpMemoryRegion print hexdump_width must be " |
1510 | "multiple of 8, not " << hexdump_width; |
1511 | return; |
1512 | } |
1513 | |
1514 | hexdump_ = hexdump; |
1515 | hexdump_width_ = hexdump_width; |
1516 | } |
1517 | |
1518 | |
1519 | // |
1520 | // MinidumpThread |
1521 | // |
1522 | |
1523 | |
1524 | MinidumpThread::MinidumpThread(Minidump* minidump) |
1525 | : MinidumpObject(minidump), |
1526 | thread_(), |
1527 | memory_(NULL), |
1528 | context_(NULL) { |
1529 | } |
1530 | |
1531 | |
1532 | MinidumpThread::~MinidumpThread() { |
1533 | delete memory_; |
1534 | delete context_; |
1535 | } |
1536 | |
1537 | |
1538 | bool MinidumpThread::Read() { |
1539 | // Invalidate cached data. |
1540 | delete memory_; |
1541 | memory_ = NULL; |
1542 | delete context_; |
1543 | context_ = NULL; |
1544 | |
1545 | valid_ = false; |
1546 | |
1547 | if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) { |
1548 | BPLOG(ERROR) << "MinidumpThread cannot read thread" ; |
1549 | return false; |
1550 | } |
1551 | |
1552 | if (minidump_->swap()) { |
1553 | Swap(&thread_.thread_id); |
1554 | Swap(&thread_.suspend_count); |
1555 | Swap(&thread_.priority_class); |
1556 | Swap(&thread_.priority); |
1557 | Swap(&thread_.teb); |
1558 | Swap(&thread_.stack); |
1559 | Swap(&thread_.thread_context); |
1560 | } |
1561 | |
1562 | // Check for base + size overflow or undersize. |
1563 | if (thread_.stack.memory.rva == 0 || |
1564 | thread_.stack.memory.data_size == 0 || |
1565 | thread_.stack.memory.data_size > numeric_limits<uint64_t>::max() - |
1566 | thread_.stack.start_of_memory_range) { |
1567 | // This is ok, but log an error anyway. |
1568 | BPLOG(ERROR) << "MinidumpThread has a memory region problem, " << |
1569 | HexString(thread_.stack.start_of_memory_range) << "+" << |
1570 | HexString(thread_.stack.memory.data_size) << |
1571 | ", RVA 0x" << HexString(thread_.stack.memory.rva); |
1572 | } else { |
1573 | memory_ = new MinidumpMemoryRegion(minidump_); |
1574 | memory_->SetDescriptor(&thread_.stack); |
1575 | } |
1576 | |
1577 | valid_ = true; |
1578 | return true; |
1579 | } |
1580 | |
1581 | uint64_t MinidumpThread::GetStartOfStackMemoryRange() const { |
1582 | if (!valid_) { |
1583 | BPLOG(ERROR) << "GetStartOfStackMemoryRange: Invalid MinidumpThread" ; |
1584 | return 0; |
1585 | } |
1586 | |
1587 | return thread_.stack.start_of_memory_range; |
1588 | } |
1589 | |
1590 | MinidumpMemoryRegion* MinidumpThread::GetMemory() { |
1591 | if (!valid_) { |
1592 | BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory" ; |
1593 | return NULL; |
1594 | } |
1595 | |
1596 | return memory_; |
1597 | } |
1598 | |
1599 | |
1600 | MinidumpContext* MinidumpThread::GetContext() { |
1601 | if (!valid_) { |
1602 | BPLOG(ERROR) << "Invalid MinidumpThread for GetContext" ; |
1603 | return NULL; |
1604 | } |
1605 | |
1606 | if (!context_) { |
1607 | if (!minidump_->SeekSet(thread_.thread_context.rva)) { |
1608 | BPLOG(ERROR) << "MinidumpThread cannot seek to context" ; |
1609 | return NULL; |
1610 | } |
1611 | |
1612 | scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_)); |
1613 | |
1614 | if (!context->Read(thread_.thread_context.data_size)) { |
1615 | BPLOG(ERROR) << "MinidumpThread cannot read context" ; |
1616 | return NULL; |
1617 | } |
1618 | |
1619 | context_ = context.release(); |
1620 | } |
1621 | |
1622 | return context_; |
1623 | } |
1624 | |
1625 | |
1626 | bool MinidumpThread::GetThreadID(uint32_t* thread_id) const { |
1627 | BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires " |
1628 | "|thread_id|" ; |
1629 | assert(thread_id); |
1630 | *thread_id = 0; |
1631 | |
1632 | if (!valid_) { |
1633 | BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID" ; |
1634 | return false; |
1635 | } |
1636 | |
1637 | *thread_id = thread_.thread_id; |
1638 | return true; |
1639 | } |
1640 | |
1641 | |
1642 | void MinidumpThread::Print() { |
1643 | if (!valid_) { |
1644 | BPLOG(ERROR) << "MinidumpThread cannot print invalid data" ; |
1645 | return; |
1646 | } |
1647 | |
1648 | printf("MDRawThread\n" ); |
1649 | printf(" thread_id = 0x%x\n" , thread_.thread_id); |
1650 | printf(" suspend_count = %d\n" , thread_.suspend_count); |
1651 | printf(" priority_class = 0x%x\n" , thread_.priority_class); |
1652 | printf(" priority = 0x%x\n" , thread_.priority); |
1653 | printf(" teb = 0x%" PRIx64 "\n" , thread_.teb); |
1654 | printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n" , |
1655 | thread_.stack.start_of_memory_range); |
1656 | printf(" stack.memory.data_size = 0x%x\n" , |
1657 | thread_.stack.memory.data_size); |
1658 | printf(" stack.memory.rva = 0x%x\n" , thread_.stack.memory.rva); |
1659 | printf(" thread_context.data_size = 0x%x\n" , |
1660 | thread_.thread_context.data_size); |
1661 | printf(" thread_context.rva = 0x%x\n" , |
1662 | thread_.thread_context.rva); |
1663 | |
1664 | MinidumpContext* context = GetContext(); |
1665 | if (context) { |
1666 | printf("\n" ); |
1667 | context->Print(); |
1668 | } else { |
1669 | printf(" (no context)\n" ); |
1670 | printf("\n" ); |
1671 | } |
1672 | |
1673 | MinidumpMemoryRegion* memory = GetMemory(); |
1674 | if (memory) { |
1675 | printf("Stack\n" ); |
1676 | memory->Print(); |
1677 | } else { |
1678 | printf("No stack\n" ); |
1679 | } |
1680 | printf("\n" ); |
1681 | } |
1682 | |
1683 | |
1684 | // |
1685 | // MinidumpThreadList |
1686 | // |
1687 | |
1688 | |
1689 | uint32_t MinidumpThreadList::max_threads_ = 4096; |
1690 | |
1691 | |
1692 | MinidumpThreadList::MinidumpThreadList(Minidump* minidump) |
1693 | : MinidumpStream(minidump), |
1694 | id_to_thread_map_(), |
1695 | threads_(NULL), |
1696 | thread_count_(0) { |
1697 | } |
1698 | |
1699 | |
1700 | MinidumpThreadList::~MinidumpThreadList() { |
1701 | delete threads_; |
1702 | } |
1703 | |
1704 | |
1705 | bool MinidumpThreadList::Read(uint32_t expected_size) { |
1706 | // Invalidate cached data. |
1707 | id_to_thread_map_.clear(); |
1708 | delete threads_; |
1709 | threads_ = NULL; |
1710 | thread_count_ = 0; |
1711 | |
1712 | valid_ = false; |
1713 | |
1714 | uint32_t thread_count; |
1715 | if (expected_size < sizeof(thread_count)) { |
1716 | BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " << |
1717 | expected_size << " < " << sizeof(thread_count); |
1718 | return false; |
1719 | } |
1720 | if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) { |
1721 | BPLOG(ERROR) << "MinidumpThreadList cannot read thread count" ; |
1722 | return false; |
1723 | } |
1724 | |
1725 | if (minidump_->swap()) |
1726 | Swap(&thread_count); |
1727 | |
1728 | if (thread_count > numeric_limits<uint32_t>::max() / sizeof(MDRawThread)) { |
1729 | BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count << |
1730 | " would cause multiplication overflow" ; |
1731 | return false; |
1732 | } |
1733 | |
1734 | if (expected_size != sizeof(thread_count) + |
1735 | thread_count * sizeof(MDRawThread)) { |
1736 | // may be padded with 4 bytes on 64bit ABIs for alignment |
1737 | if (expected_size == sizeof(thread_count) + 4 + |
1738 | thread_count * sizeof(MDRawThread)) { |
1739 | uint32_t useless; |
1740 | if (!minidump_->ReadBytes(&useless, 4)) { |
1741 | BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded " |
1742 | "bytes" ; |
1743 | return false; |
1744 | } |
1745 | } else { |
1746 | BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << |
1747 | " != " << sizeof(thread_count) + |
1748 | thread_count * sizeof(MDRawThread); |
1749 | return false; |
1750 | } |
1751 | } |
1752 | |
1753 | |
1754 | if (thread_count > max_threads_) { |
1755 | BPLOG(ERROR) << "MinidumpThreadList count " << thread_count << |
1756 | " exceeds maximum " << max_threads_; |
1757 | return false; |
1758 | } |
1759 | |
1760 | if (thread_count != 0) { |
1761 | scoped_ptr<MinidumpThreads> threads( |
1762 | new MinidumpThreads(thread_count, MinidumpThread(minidump_))); |
1763 | |
1764 | for (unsigned int thread_index = 0; |
1765 | thread_index < thread_count; |
1766 | ++thread_index) { |
1767 | MinidumpThread* thread = &(*threads)[thread_index]; |
1768 | |
1769 | // Assume that the file offset is correct after the last read. |
1770 | if (!thread->Read()) { |
1771 | BPLOG(ERROR) << "MinidumpThreadList cannot read thread " << |
1772 | thread_index << "/" << thread_count; |
1773 | return false; |
1774 | } |
1775 | |
1776 | uint32_t thread_id; |
1777 | if (!thread->GetThreadID(&thread_id)) { |
1778 | BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " << |
1779 | thread_index << "/" << thread_count; |
1780 | return false; |
1781 | } |
1782 | |
1783 | if (GetThreadByID(thread_id)) { |
1784 | // Another thread with this ID is already in the list. Data error. |
1785 | BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " << |
1786 | HexString(thread_id) << " at thread " << |
1787 | thread_index << "/" << thread_count; |
1788 | return false; |
1789 | } |
1790 | id_to_thread_map_[thread_id] = thread; |
1791 | } |
1792 | |
1793 | threads_ = threads.release(); |
1794 | } |
1795 | |
1796 | thread_count_ = thread_count; |
1797 | |
1798 | valid_ = true; |
1799 | return true; |
1800 | } |
1801 | |
1802 | |
1803 | MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index) |
1804 | const { |
1805 | if (!valid_) { |
1806 | BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex" ; |
1807 | return NULL; |
1808 | } |
1809 | |
1810 | if (index >= thread_count_) { |
1811 | BPLOG(ERROR) << "MinidumpThreadList index out of range: " << |
1812 | index << "/" << thread_count_; |
1813 | return NULL; |
1814 | } |
1815 | |
1816 | return &(*threads_)[index]; |
1817 | } |
1818 | |
1819 | |
1820 | MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) { |
1821 | // Don't check valid_. Read calls this method before everything is |
1822 | // validated. It is safe to not check valid_ here. |
1823 | return id_to_thread_map_[thread_id]; |
1824 | } |
1825 | |
1826 | |
1827 | void MinidumpThreadList::Print() { |
1828 | if (!valid_) { |
1829 | BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data" ; |
1830 | return; |
1831 | } |
1832 | |
1833 | printf("MinidumpThreadList\n" ); |
1834 | printf(" thread_count = %d\n" , thread_count_); |
1835 | printf("\n" ); |
1836 | |
1837 | for (unsigned int thread_index = 0; |
1838 | thread_index < thread_count_; |
1839 | ++thread_index) { |
1840 | printf("thread[%d]\n" , thread_index); |
1841 | |
1842 | (*threads_)[thread_index].Print(); |
1843 | } |
1844 | } |
1845 | |
1846 | |
1847 | // |
1848 | // MinidumpModule |
1849 | // |
1850 | |
1851 | |
1852 | uint32_t MinidumpModule::max_cv_bytes_ = 32768; |
1853 | uint32_t MinidumpModule::max_misc_bytes_ = 32768; |
1854 | |
1855 | |
1856 | MinidumpModule::MinidumpModule(Minidump* minidump) |
1857 | : MinidumpObject(minidump), |
1858 | module_valid_(false), |
1859 | has_debug_info_(false), |
1860 | module_(), |
1861 | name_(NULL), |
1862 | cv_record_(NULL), |
1863 | cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE), |
1864 | misc_record_(NULL) { |
1865 | } |
1866 | |
1867 | |
1868 | MinidumpModule::~MinidumpModule() { |
1869 | delete name_; |
1870 | delete cv_record_; |
1871 | delete misc_record_; |
1872 | } |
1873 | |
1874 | |
1875 | bool MinidumpModule::Read() { |
1876 | // Invalidate cached data. |
1877 | delete name_; |
1878 | name_ = NULL; |
1879 | delete cv_record_; |
1880 | cv_record_ = NULL; |
1881 | cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE; |
1882 | delete misc_record_; |
1883 | misc_record_ = NULL; |
1884 | |
1885 | module_valid_ = false; |
1886 | has_debug_info_ = false; |
1887 | valid_ = false; |
1888 | |
1889 | if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) { |
1890 | BPLOG(ERROR) << "MinidumpModule cannot read module" ; |
1891 | return false; |
1892 | } |
1893 | |
1894 | if (minidump_->swap()) { |
1895 | Swap(&module_.base_of_image); |
1896 | Swap(&module_.size_of_image); |
1897 | Swap(&module_.checksum); |
1898 | Swap(&module_.time_date_stamp); |
1899 | Swap(&module_.module_name_rva); |
1900 | Swap(&module_.version_info.signature); |
1901 | Swap(&module_.version_info.struct_version); |
1902 | Swap(&module_.version_info.file_version_hi); |
1903 | Swap(&module_.version_info.file_version_lo); |
1904 | Swap(&module_.version_info.product_version_hi); |
1905 | Swap(&module_.version_info.product_version_lo); |
1906 | Swap(&module_.version_info.file_flags_mask); |
1907 | Swap(&module_.version_info.file_flags); |
1908 | Swap(&module_.version_info.file_os); |
1909 | Swap(&module_.version_info.file_type); |
1910 | Swap(&module_.version_info.file_subtype); |
1911 | Swap(&module_.version_info.file_date_hi); |
1912 | Swap(&module_.version_info.file_date_lo); |
1913 | Swap(&module_.cv_record); |
1914 | Swap(&module_.misc_record); |
1915 | // Don't swap reserved fields because their contents are unknown (as |
1916 | // are their proper widths). |
1917 | } |
1918 | |
1919 | // Check for base + size overflow or undersize. |
1920 | if (module_.size_of_image == 0 || |
1921 | module_.size_of_image > |
1922 | numeric_limits<uint64_t>::max() - module_.base_of_image) { |
1923 | BPLOG(ERROR) << "MinidumpModule has a module problem, " << |
1924 | HexString(module_.base_of_image) << "+" << |
1925 | HexString(module_.size_of_image); |
1926 | return false; |
1927 | } |
1928 | |
1929 | module_valid_ = true; |
1930 | return true; |
1931 | } |
1932 | |
1933 | |
1934 | bool MinidumpModule::ReadAuxiliaryData() { |
1935 | if (!module_valid_) { |
1936 | BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData" ; |
1937 | return false; |
1938 | } |
1939 | |
1940 | // Each module must have a name. |
1941 | name_ = minidump_->ReadString(module_.module_name_rva); |
1942 | if (!name_) { |
1943 | BPLOG(ERROR) << "MinidumpModule could not read name" ; |
1944 | return false; |
1945 | } |
1946 | |
1947 | // At this point, we have enough info for the module to be valid. |
1948 | valid_ = true; |
1949 | |
1950 | // CodeView and miscellaneous debug records are only required if the |
1951 | // module indicates that they exist. |
1952 | if (module_.cv_record.data_size && !GetCVRecord(NULL)) { |
1953 | BPLOG(ERROR) << "MinidumpModule has no CodeView record, " |
1954 | "but one was expected" ; |
1955 | return false; |
1956 | } |
1957 | |
1958 | if (module_.misc_record.data_size && !GetMiscRecord(NULL)) { |
1959 | BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, " |
1960 | "but one was expected" ; |
1961 | return false; |
1962 | } |
1963 | |
1964 | has_debug_info_ = true; |
1965 | return true; |
1966 | } |
1967 | |
1968 | |
1969 | string MinidumpModule::code_file() const { |
1970 | if (!valid_) { |
1971 | BPLOG(ERROR) << "Invalid MinidumpModule for code_file" ; |
1972 | return "" ; |
1973 | } |
1974 | |
1975 | return *name_; |
1976 | } |
1977 | |
1978 | |
1979 | string MinidumpModule::code_identifier() const { |
1980 | if (!valid_) { |
1981 | BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier" ; |
1982 | return "" ; |
1983 | } |
1984 | |
1985 | if (!has_debug_info_) |
1986 | return "" ; |
1987 | |
1988 | MinidumpSystemInfo* minidump_system_info = minidump_->GetSystemInfo(); |
1989 | if (!minidump_system_info) { |
1990 | BPLOG(ERROR) << "MinidumpModule code_identifier requires " |
1991 | "MinidumpSystemInfo" ; |
1992 | return "" ; |
1993 | } |
1994 | |
1995 | const MDRawSystemInfo* raw_system_info = minidump_system_info->system_info(); |
1996 | if (!raw_system_info) { |
1997 | BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo" ; |
1998 | return "" ; |
1999 | } |
2000 | |
2001 | string identifier; |
2002 | |
2003 | switch (raw_system_info->platform_id) { |
2004 | case MD_OS_WIN32_NT: |
2005 | case MD_OS_WIN32_WINDOWS: { |
2006 | // Use the same format that the MS symbol server uses in filesystem |
2007 | // hierarchies. |
2008 | char identifier_string[17]; |
2009 | snprintf(identifier_string, sizeof(identifier_string), "%08X%x" , |
2010 | module_.time_date_stamp, module_.size_of_image); |
2011 | identifier = identifier_string; |
2012 | break; |
2013 | } |
2014 | |
2015 | case MD_OS_ANDROID: |
2016 | case MD_OS_FUCHSIA: |
2017 | case MD_OS_LINUX: { |
2018 | // If ELF CodeView data is present, return the debug id. |
2019 | if (cv_record_ && cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { |
2020 | const MDCVInfoELF* cv_record_elf = |
2021 | reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]); |
2022 | assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); |
2023 | |
2024 | for (unsigned int build_id_index = 0; |
2025 | build_id_index < (cv_record_->size() - MDCVInfoELF_minsize); |
2026 | ++build_id_index) { |
2027 | char hexbyte[3]; |
2028 | snprintf(hexbyte, sizeof(hexbyte), "%02x" , |
2029 | cv_record_elf->build_id[build_id_index]); |
2030 | identifier += hexbyte; |
2031 | } |
2032 | break; |
2033 | } |
2034 | // Otherwise fall through to the case below. |
2035 | BP_FALLTHROUGH; |
2036 | } |
2037 | |
2038 | case MD_OS_MAC_OS_X: |
2039 | case MD_OS_IOS: |
2040 | case MD_OS_SOLARIS: |
2041 | case MD_OS_NACL: |
2042 | case MD_OS_PS3: { |
2043 | // TODO(mmentovai): support uuid extension if present, otherwise fall |
2044 | // back to version (from LC_ID_DYLIB?), otherwise fall back to something |
2045 | // else. |
2046 | identifier = "id" ; |
2047 | break; |
2048 | } |
2049 | |
2050 | default: { |
2051 | // Without knowing what OS generated the dump, we can't generate a good |
2052 | // identifier. Return an empty string, signalling failure. |
2053 | BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, " |
2054 | "found " << HexString(raw_system_info->platform_id); |
2055 | break; |
2056 | } |
2057 | } |
2058 | |
2059 | return identifier; |
2060 | } |
2061 | |
2062 | |
2063 | string MinidumpModule::debug_file() const { |
2064 | if (!valid_) { |
2065 | BPLOG(ERROR) << "Invalid MinidumpModule for debug_file" ; |
2066 | return "" ; |
2067 | } |
2068 | |
2069 | if (!has_debug_info_) |
2070 | return "" ; |
2071 | |
2072 | string file; |
2073 | // Prefer the CodeView record if present. |
2074 | if (cv_record_) { |
2075 | if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
2076 | // It's actually an MDCVInfoPDB70 structure. |
2077 | const MDCVInfoPDB70* cv_record_70 = |
2078 | reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); |
2079 | assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
2080 | |
2081 | // GetCVRecord guarantees pdb_file_name is null-terminated. |
2082 | file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name); |
2083 | } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
2084 | // It's actually an MDCVInfoPDB20 structure. |
2085 | const MDCVInfoPDB20* cv_record_20 = |
2086 | reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); |
2087 | assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
2088 | |
2089 | // GetCVRecord guarantees pdb_file_name is null-terminated. |
2090 | file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name); |
2091 | } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { |
2092 | // It's actually an MDCVInfoELF structure. |
2093 | assert(reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0])-> |
2094 | cv_signature == MD_CVINFOELF_SIGNATURE); |
2095 | |
2096 | // For MDCVInfoELF, the debug file is the code file. |
2097 | file = *name_; |
2098 | } |
2099 | |
2100 | // If there's a CodeView record but it doesn't match a known signature, |
2101 | // try the miscellaneous record. |
2102 | } |
2103 | |
2104 | if (file.empty()) { |
2105 | // No usable CodeView record. Try the miscellaneous debug record. |
2106 | if (misc_record_) { |
2107 | const MDImageDebugMisc* misc_record = |
2108 | reinterpret_cast<const MDImageDebugMisc*>(&(*misc_record_)[0]); |
2109 | if (!misc_record->unicode) { |
2110 | // If it's not Unicode, just stuff it into the string. It's unclear |
2111 | // if misc_record->data is 0-terminated, so use an explicit size. |
2112 | file = string( |
2113 | reinterpret_cast<const char*>(misc_record->data), |
2114 | module_.misc_record.data_size - MDImageDebugMisc_minsize); |
2115 | } else { |
2116 | // There's a misc_record but it encodes the debug filename in UTF-16. |
2117 | // (Actually, because miscellaneous records are so old, it's probably |
2118 | // UCS-2.) Convert it to UTF-8 for congruity with the other strings |
2119 | // that this method (and all other methods in the Minidump family) |
2120 | // return. |
2121 | |
2122 | size_t bytes = |
2123 | module_.misc_record.data_size - MDImageDebugMisc_minsize; |
2124 | if (bytes % 2 == 0) { |
2125 | size_t utf16_words = bytes / 2; |
2126 | |
2127 | // UTF16ToUTF8 expects a vector<uint16_t>, so create a temporary one |
2128 | // and copy the UTF-16 data into it. |
2129 | vector<uint16_t> string_utf16(utf16_words); |
2130 | if (utf16_words) |
2131 | memcpy(&string_utf16[0], &misc_record->data, bytes); |
2132 | |
2133 | // GetMiscRecord already byte-swapped the data[] field if it contains |
2134 | // UTF-16, so pass false as the swap argument. |
2135 | scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false)); |
2136 | if (new_file.get() != nullptr) { |
2137 | file = *new_file; |
2138 | } |
2139 | } |
2140 | } |
2141 | } |
2142 | } |
2143 | |
2144 | // Relatively common case |
2145 | BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine " |
2146 | "debug_file for " << *name_; |
2147 | |
2148 | return file; |
2149 | } |
2150 | |
2151 | static string guid_and_age_to_debug_id(const MDGUID& guid, |
2152 | uint32_t age) { |
2153 | char identifier_string[41]; |
2154 | snprintf(identifier_string, sizeof(identifier_string), |
2155 | "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x" , |
2156 | guid.data1, |
2157 | guid.data2, |
2158 | guid.data3, |
2159 | guid.data4[0], |
2160 | guid.data4[1], |
2161 | guid.data4[2], |
2162 | guid.data4[3], |
2163 | guid.data4[4], |
2164 | guid.data4[5], |
2165 | guid.data4[6], |
2166 | guid.data4[7], |
2167 | age); |
2168 | return identifier_string; |
2169 | } |
2170 | |
2171 | string MinidumpModule::debug_identifier() const { |
2172 | if (!valid_) { |
2173 | BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier" ; |
2174 | return "" ; |
2175 | } |
2176 | |
2177 | if (!has_debug_info_) |
2178 | return "" ; |
2179 | |
2180 | string identifier; |
2181 | |
2182 | // Use the CodeView record if present. |
2183 | if (cv_record_) { |
2184 | if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
2185 | // It's actually an MDCVInfoPDB70 structure. |
2186 | const MDCVInfoPDB70* cv_record_70 = |
2187 | reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); |
2188 | assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
2189 | |
2190 | // Use the same format that the MS symbol server uses in filesystem |
2191 | // hierarchies. |
2192 | identifier = guid_and_age_to_debug_id(cv_record_70->signature, |
2193 | cv_record_70->age); |
2194 | } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
2195 | // It's actually an MDCVInfoPDB20 structure. |
2196 | const MDCVInfoPDB20* cv_record_20 = |
2197 | reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); |
2198 | assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
2199 | |
2200 | // Use the same format that the MS symbol server uses in filesystem |
2201 | // hierarchies. |
2202 | char identifier_string[17]; |
2203 | snprintf(identifier_string, sizeof(identifier_string), |
2204 | "%08X%x" , cv_record_20->signature, cv_record_20->age); |
2205 | identifier = identifier_string; |
2206 | } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { |
2207 | // It's actually an MDCVInfoELF structure. |
2208 | const MDCVInfoELF* cv_record_elf = |
2209 | reinterpret_cast<const MDCVInfoELF*>(&(*cv_record_)[0]); |
2210 | assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); |
2211 | |
2212 | // For backwards-compatibility, stuff as many bytes as will fit into |
2213 | // a MDGUID and use the MS symbol server format as MDCVInfoPDB70 does |
2214 | // with age = 0. Historically Breakpad would do this during dump |
2215 | // writing to fit the build id data into a MDCVInfoPDB70 struct. |
2216 | // The full build id is available by calling code_identifier. |
2217 | MDGUID guid = {0}; |
2218 | memcpy(&guid, &cv_record_elf->build_id, |
2219 | std::min(cv_record_->size() - MDCVInfoELF_minsize, |
2220 | sizeof(MDGUID))); |
2221 | identifier = guid_and_age_to_debug_id(guid, 0); |
2222 | } |
2223 | } |
2224 | |
2225 | // TODO(mmentovai): if there's no usable CodeView record, there might be a |
2226 | // miscellaneous debug record. It only carries a filename, though, and no |
2227 | // identifier. I'm not sure what the right thing to do for the identifier |
2228 | // is in that case, but I don't expect to find many modules without a |
2229 | // CodeView record (or some other Breakpad extension structure in place of |
2230 | // a CodeView record). Treat it as an error (empty identifier) for now. |
2231 | |
2232 | // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). |
2233 | |
2234 | // Relatively common case |
2235 | BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine " |
2236 | "debug_identifier for " << *name_; |
2237 | |
2238 | return identifier; |
2239 | } |
2240 | |
2241 | |
2242 | string MinidumpModule::version() const { |
2243 | if (!valid_) { |
2244 | BPLOG(ERROR) << "Invalid MinidumpModule for version" ; |
2245 | return "" ; |
2246 | } |
2247 | |
2248 | string version; |
2249 | |
2250 | if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && |
2251 | module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { |
2252 | char version_string[24]; |
2253 | snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u" , |
2254 | module_.version_info.file_version_hi >> 16, |
2255 | module_.version_info.file_version_hi & 0xffff, |
2256 | module_.version_info.file_version_lo >> 16, |
2257 | module_.version_info.file_version_lo & 0xffff); |
2258 | version = version_string; |
2259 | } |
2260 | |
2261 | // TODO(mmentovai): possibly support other struct types in place of |
2262 | // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use |
2263 | // a different structure that better represents versioning facilities on |
2264 | // Mac OS X and Linux, instead of forcing them to adhere to the dotted |
2265 | // quad of 16-bit ints that Windows uses. |
2266 | |
2267 | BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine " |
2268 | "version for " << *name_; |
2269 | |
2270 | return version; |
2271 | } |
2272 | |
2273 | |
2274 | CodeModule* MinidumpModule::Copy() const { |
2275 | return new BasicCodeModule(this); |
2276 | } |
2277 | |
2278 | |
2279 | uint64_t MinidumpModule::shrink_down_delta() const { |
2280 | return 0; |
2281 | } |
2282 | |
2283 | void MinidumpModule::SetShrinkDownDelta(uint64_t shrink_down_delta) { |
2284 | // Not implemented |
2285 | assert(false); |
2286 | } |
2287 | |
2288 | |
2289 | const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) { |
2290 | if (!module_valid_) { |
2291 | BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord" ; |
2292 | return NULL; |
2293 | } |
2294 | |
2295 | if (!cv_record_) { |
2296 | // This just guards against 0-sized CodeView records; more specific checks |
2297 | // are used when the signature is checked against various structure types. |
2298 | if (module_.cv_record.data_size == 0) { |
2299 | return NULL; |
2300 | } |
2301 | |
2302 | if (!minidump_->SeekSet(module_.cv_record.rva)) { |
2303 | BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record" ; |
2304 | return NULL; |
2305 | } |
2306 | |
2307 | if (module_.cv_record.data_size > max_cv_bytes_) { |
2308 | BPLOG(ERROR) << "MinidumpModule CodeView record size " << |
2309 | module_.cv_record.data_size << " exceeds maximum " << |
2310 | max_cv_bytes_; |
2311 | return NULL; |
2312 | } |
2313 | |
2314 | // Allocating something that will be accessed as MDCVInfoPDB70 or |
2315 | // MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment |
2316 | // problems. x86 and ppc are able to cope, though. This allocation |
2317 | // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are |
2318 | // variable-sized due to their pdb_file_name fields; these structures |
2319 | // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating |
2320 | // them as such would result in incomplete structures or overruns. |
2321 | scoped_ptr< vector<uint8_t> > cv_record( |
2322 | new vector<uint8_t>(module_.cv_record.data_size)); |
2323 | |
2324 | if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) { |
2325 | BPLOG(ERROR) << "MinidumpModule could not read CodeView record" ; |
2326 | return NULL; |
2327 | } |
2328 | |
2329 | uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE; |
2330 | if (module_.cv_record.data_size > sizeof(signature)) { |
2331 | MDCVInfoPDB70* cv_record_signature = |
2332 | reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]); |
2333 | signature = cv_record_signature->cv_signature; |
2334 | if (minidump_->swap()) |
2335 | Swap(&signature); |
2336 | } |
2337 | |
2338 | if (signature == MD_CVINFOPDB70_SIGNATURE) { |
2339 | // Now that the structure type is known, recheck the size, |
2340 | // ensuring at least one byte for the null terminator. |
2341 | if (MDCVInfoPDB70_minsize + 1 > module_.cv_record.data_size) { |
2342 | BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " << |
2343 | MDCVInfoPDB70_minsize << " > " << |
2344 | module_.cv_record.data_size; |
2345 | return NULL; |
2346 | } |
2347 | |
2348 | if (minidump_->swap()) { |
2349 | MDCVInfoPDB70* cv_record_70 = |
2350 | reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]); |
2351 | Swap(&cv_record_70->cv_signature); |
2352 | Swap(&cv_record_70->signature); |
2353 | Swap(&cv_record_70->age); |
2354 | // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit |
2355 | // quantities. (It's a path, is it UTF-8?) |
2356 | } |
2357 | |
2358 | // The last field of either structure is null-terminated 8-bit character |
2359 | // data. Ensure that it's null-terminated. |
2360 | if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { |
2361 | BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not " |
2362 | "0-terminated" ; |
2363 | return NULL; |
2364 | } |
2365 | } else if (signature == MD_CVINFOPDB20_SIGNATURE) { |
2366 | // Now that the structure type is known, recheck the size, |
2367 | // ensuring at least one byte for the null terminator. |
2368 | if (MDCVInfoPDB20_minsize + 1 > module_.cv_record.data_size) { |
2369 | BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " << |
2370 | MDCVInfoPDB20_minsize << " > " << |
2371 | module_.cv_record.data_size; |
2372 | return NULL; |
2373 | } |
2374 | if (minidump_->swap()) { |
2375 | MDCVInfoPDB20* cv_record_20 = |
2376 | reinterpret_cast<MDCVInfoPDB20*>(&(*cv_record)[0]); |
2377 | Swap(&cv_record_20->cv_header.signature); |
2378 | Swap(&cv_record_20->cv_header.offset); |
2379 | Swap(&cv_record_20->signature); |
2380 | Swap(&cv_record_20->age); |
2381 | // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit |
2382 | // quantities. (It's a path, is it UTF-8?) |
2383 | } |
2384 | |
2385 | // The last field of either structure is null-terminated 8-bit character |
2386 | // data. Ensure that it's null-terminated. |
2387 | if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { |
2388 | BPLOG(ERROR) << "MindumpModule CodeView2 record string is not " |
2389 | "0-terminated" ; |
2390 | return NULL; |
2391 | } |
2392 | } else if (signature == MD_CVINFOELF_SIGNATURE) { |
2393 | // Now that the structure type is known, recheck the size. |
2394 | if (MDCVInfoELF_minsize > module_.cv_record.data_size) { |
2395 | BPLOG(ERROR) << "MinidumpModule CodeViewELF record size mismatch, " << |
2396 | MDCVInfoELF_minsize << " > " << |
2397 | module_.cv_record.data_size; |
2398 | return NULL; |
2399 | } |
2400 | if (minidump_->swap()) { |
2401 | MDCVInfoELF* cv_record_elf = |
2402 | reinterpret_cast<MDCVInfoELF*>(&(*cv_record)[0]); |
2403 | Swap(&cv_record_elf->cv_signature); |
2404 | } |
2405 | } |
2406 | |
2407 | // If the signature doesn't match something above, it's not something |
2408 | // that Breakpad can presently handle directly. Because some modules in |
2409 | // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE, |
2410 | // don't bail out here - allow the data to be returned to the user, |
2411 | // although byte-swapping can't be done. |
2412 | |
2413 | // Store the vector type because that's how storage was allocated, but |
2414 | // return it casted to uint8_t*. |
2415 | cv_record_ = cv_record.release(); |
2416 | cv_record_signature_ = signature; |
2417 | } |
2418 | |
2419 | if (size) |
2420 | *size = module_.cv_record.data_size; |
2421 | |
2422 | return &(*cv_record_)[0]; |
2423 | } |
2424 | |
2425 | |
2426 | const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) { |
2427 | if (!module_valid_) { |
2428 | BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord" ; |
2429 | return NULL; |
2430 | } |
2431 | |
2432 | if (!misc_record_) { |
2433 | if (module_.misc_record.data_size == 0) { |
2434 | return NULL; |
2435 | } |
2436 | |
2437 | if (MDImageDebugMisc_minsize > module_.misc_record.data_size) { |
2438 | BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record " |
2439 | "size mismatch, " << MDImageDebugMisc_minsize << " > " << |
2440 | module_.misc_record.data_size; |
2441 | return NULL; |
2442 | } |
2443 | |
2444 | if (!minidump_->SeekSet(module_.misc_record.rva)) { |
2445 | BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous " |
2446 | "debugging record" ; |
2447 | return NULL; |
2448 | } |
2449 | |
2450 | if (module_.misc_record.data_size > max_misc_bytes_) { |
2451 | BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " << |
2452 | module_.misc_record.data_size << " exceeds maximum " << |
2453 | max_misc_bytes_; |
2454 | return NULL; |
2455 | } |
2456 | |
2457 | // Allocating something that will be accessed as MDImageDebugMisc but |
2458 | // is allocated as uint8_t[] can cause alignment problems. x86 and |
2459 | // ppc are able to cope, though. This allocation style is needed |
2460 | // because the MDImageDebugMisc is variable-sized due to its data field; |
2461 | // this structure is not MDImageDebugMisc_minsize and treating it as such |
2462 | // would result in an incomplete structure or an overrun. |
2463 | scoped_ptr< vector<uint8_t> > misc_record_mem( |
2464 | new vector<uint8_t>(module_.misc_record.data_size)); |
2465 | MDImageDebugMisc* misc_record = |
2466 | reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]); |
2467 | |
2468 | if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) { |
2469 | BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging " |
2470 | "record" ; |
2471 | return NULL; |
2472 | } |
2473 | |
2474 | if (minidump_->swap()) { |
2475 | Swap(&misc_record->data_type); |
2476 | Swap(&misc_record->length); |
2477 | // Don't swap misc_record.unicode because it's an 8-bit quantity. |
2478 | // Don't swap the reserved fields for the same reason, and because |
2479 | // they don't contain any valid data. |
2480 | if (misc_record->unicode) { |
2481 | // There is a potential alignment problem, but shouldn't be a problem |
2482 | // in practice due to the layout of MDImageDebugMisc. |
2483 | uint16_t* data16 = reinterpret_cast<uint16_t*>(&(misc_record->data)); |
2484 | size_t dataBytes = module_.misc_record.data_size - |
2485 | MDImageDebugMisc_minsize; |
2486 | Swap(data16, dataBytes); |
2487 | } |
2488 | } |
2489 | |
2490 | if (module_.misc_record.data_size != misc_record->length) { |
2491 | BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data " |
2492 | "size mismatch, " << module_.misc_record.data_size << |
2493 | " != " << misc_record->length; |
2494 | return NULL; |
2495 | } |
2496 | |
2497 | // Store the vector type because that's how storage was allocated, but |
2498 | // return it casted to MDImageDebugMisc*. |
2499 | misc_record_ = misc_record_mem.release(); |
2500 | } |
2501 | |
2502 | if (size) |
2503 | *size = module_.misc_record.data_size; |
2504 | |
2505 | return reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_)[0]); |
2506 | } |
2507 | |
2508 | |
2509 | void MinidumpModule::Print() { |
2510 | if (!valid_) { |
2511 | BPLOG(ERROR) << "MinidumpModule cannot print invalid data" ; |
2512 | return; |
2513 | } |
2514 | |
2515 | printf("MDRawModule\n" ); |
2516 | printf(" base_of_image = 0x%" PRIx64 "\n" , |
2517 | module_.base_of_image); |
2518 | printf(" size_of_image = 0x%x\n" , |
2519 | module_.size_of_image); |
2520 | printf(" checksum = 0x%x\n" , |
2521 | module_.checksum); |
2522 | printf(" time_date_stamp = 0x%x %s\n" , |
2523 | module_.time_date_stamp, |
2524 | TimeTToUTCString(module_.time_date_stamp).c_str()); |
2525 | printf(" module_name_rva = 0x%x\n" , |
2526 | module_.module_name_rva); |
2527 | printf(" version_info.signature = 0x%x\n" , |
2528 | module_.version_info.signature); |
2529 | printf(" version_info.struct_version = 0x%x\n" , |
2530 | module_.version_info.struct_version); |
2531 | printf(" version_info.file_version = 0x%x:0x%x\n" , |
2532 | module_.version_info.file_version_hi, |
2533 | module_.version_info.file_version_lo); |
2534 | printf(" version_info.product_version = 0x%x:0x%x\n" , |
2535 | module_.version_info.product_version_hi, |
2536 | module_.version_info.product_version_lo); |
2537 | printf(" version_info.file_flags_mask = 0x%x\n" , |
2538 | module_.version_info.file_flags_mask); |
2539 | printf(" version_info.file_flags = 0x%x\n" , |
2540 | module_.version_info.file_flags); |
2541 | printf(" version_info.file_os = 0x%x\n" , |
2542 | module_.version_info.file_os); |
2543 | printf(" version_info.file_type = 0x%x\n" , |
2544 | module_.version_info.file_type); |
2545 | printf(" version_info.file_subtype = 0x%x\n" , |
2546 | module_.version_info.file_subtype); |
2547 | printf(" version_info.file_date = 0x%x:0x%x\n" , |
2548 | module_.version_info.file_date_hi, |
2549 | module_.version_info.file_date_lo); |
2550 | printf(" cv_record.data_size = %d\n" , |
2551 | module_.cv_record.data_size); |
2552 | printf(" cv_record.rva = 0x%x\n" , |
2553 | module_.cv_record.rva); |
2554 | printf(" misc_record.data_size = %d\n" , |
2555 | module_.misc_record.data_size); |
2556 | printf(" misc_record.rva = 0x%x\n" , |
2557 | module_.misc_record.rva); |
2558 | |
2559 | printf(" (code_file) = \"%s\"\n" , code_file().c_str()); |
2560 | printf(" (code_identifier) = \"%s\"\n" , |
2561 | code_identifier().c_str()); |
2562 | |
2563 | uint32_t cv_record_size; |
2564 | const uint8_t* cv_record = GetCVRecord(&cv_record_size); |
2565 | if (cv_record) { |
2566 | if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
2567 | const MDCVInfoPDB70* cv_record_70 = |
2568 | reinterpret_cast<const MDCVInfoPDB70*>(cv_record); |
2569 | assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
2570 | |
2571 | printf(" (cv_record).cv_signature = 0x%x\n" , |
2572 | cv_record_70->cv_signature); |
2573 | printf(" (cv_record).signature = %s\n" , |
2574 | MDGUIDToString(cv_record_70->signature).c_str()); |
2575 | printf(" (cv_record).age = %d\n" , |
2576 | cv_record_70->age); |
2577 | printf(" (cv_record).pdb_file_name = \"%s\"\n" , |
2578 | cv_record_70->pdb_file_name); |
2579 | } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
2580 | const MDCVInfoPDB20* cv_record_20 = |
2581 | reinterpret_cast<const MDCVInfoPDB20*>(cv_record); |
2582 | assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
2583 | |
2584 | printf(" (cv_record).cv_header.signature = 0x%x\n" , |
2585 | cv_record_20->cv_header.signature); |
2586 | printf(" (cv_record).cv_header.offset = 0x%x\n" , |
2587 | cv_record_20->cv_header.offset); |
2588 | printf(" (cv_record).signature = 0x%x %s\n" , |
2589 | cv_record_20->signature, |
2590 | TimeTToUTCString(cv_record_20->signature).c_str()); |
2591 | printf(" (cv_record).age = %d\n" , |
2592 | cv_record_20->age); |
2593 | printf(" (cv_record).pdb_file_name = \"%s\"\n" , |
2594 | cv_record_20->pdb_file_name); |
2595 | } else if (cv_record_signature_ == MD_CVINFOELF_SIGNATURE) { |
2596 | const MDCVInfoELF* cv_record_elf = |
2597 | reinterpret_cast<const MDCVInfoELF*>(cv_record); |
2598 | assert(cv_record_elf->cv_signature == MD_CVINFOELF_SIGNATURE); |
2599 | |
2600 | printf(" (cv_record).cv_signature = 0x%x\n" , |
2601 | cv_record_elf->cv_signature); |
2602 | printf(" (cv_record).build_id = " ); |
2603 | for (unsigned int build_id_index = 0; |
2604 | build_id_index < (cv_record_size - MDCVInfoELF_minsize); |
2605 | ++build_id_index) { |
2606 | printf("%02x" , cv_record_elf->build_id[build_id_index]); |
2607 | } |
2608 | printf("\n" ); |
2609 | } else { |
2610 | printf(" (cv_record) = " ); |
2611 | for (unsigned int cv_byte_index = 0; |
2612 | cv_byte_index < cv_record_size; |
2613 | ++cv_byte_index) { |
2614 | printf("%02x" , cv_record[cv_byte_index]); |
2615 | } |
2616 | printf("\n" ); |
2617 | } |
2618 | } else { |
2619 | printf(" (cv_record) = (null)\n" ); |
2620 | } |
2621 | |
2622 | const MDImageDebugMisc* misc_record = GetMiscRecord(NULL); |
2623 | if (misc_record) { |
2624 | printf(" (misc_record).data_type = 0x%x\n" , |
2625 | misc_record->data_type); |
2626 | printf(" (misc_record).length = 0x%x\n" , |
2627 | misc_record->length); |
2628 | printf(" (misc_record).unicode = %d\n" , |
2629 | misc_record->unicode); |
2630 | if (misc_record->unicode) { |
2631 | string misc_record_data_utf8; |
2632 | ConvertUTF16BufferToUTF8String( |
2633 | reinterpret_cast<const uint16_t*>(misc_record->data), |
2634 | misc_record->length - offsetof(MDImageDebugMisc, data), |
2635 | &misc_record_data_utf8, |
2636 | false); // already swapped |
2637 | printf(" (misc_record).data = \"%s\"\n" , |
2638 | misc_record_data_utf8.c_str()); |
2639 | } else { |
2640 | printf(" (misc_record).data = \"%s\"\n" , |
2641 | misc_record->data); |
2642 | } |
2643 | } else { |
2644 | printf(" (misc_record) = (null)\n" ); |
2645 | } |
2646 | |
2647 | printf(" (debug_file) = \"%s\"\n" , debug_file().c_str()); |
2648 | printf(" (debug_identifier) = \"%s\"\n" , |
2649 | debug_identifier().c_str()); |
2650 | printf(" (version) = \"%s\"\n" , version().c_str()); |
2651 | printf("\n" ); |
2652 | } |
2653 | |
2654 | |
2655 | // |
2656 | // MinidumpModuleList |
2657 | // |
2658 | |
2659 | |
2660 | uint32_t MinidumpModuleList::max_modules_ = 2048; |
2661 | |
2662 | |
2663 | MinidumpModuleList::MinidumpModuleList(Minidump* minidump) |
2664 | : MinidumpStream(minidump), |
2665 | range_map_(new RangeMap<uint64_t, unsigned int>()), |
2666 | modules_(NULL), |
2667 | module_count_(0) { |
2668 | MDOSPlatform platform; |
2669 | if (minidump_->GetPlatform(&platform) && |
2670 | (platform == MD_OS_ANDROID || platform == MD_OS_LINUX)) { |
2671 | range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower); |
2672 | } |
2673 | } |
2674 | |
2675 | |
2676 | MinidumpModuleList::~MinidumpModuleList() { |
2677 | delete range_map_; |
2678 | delete modules_; |
2679 | } |
2680 | |
2681 | |
2682 | bool MinidumpModuleList::Read(uint32_t expected_size) { |
2683 | // Invalidate cached data. |
2684 | range_map_->Clear(); |
2685 | delete modules_; |
2686 | modules_ = NULL; |
2687 | module_count_ = 0; |
2688 | |
2689 | valid_ = false; |
2690 | |
2691 | uint32_t module_count; |
2692 | if (expected_size < sizeof(module_count)) { |
2693 | BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " << |
2694 | expected_size << " < " << sizeof(module_count); |
2695 | return false; |
2696 | } |
2697 | if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) { |
2698 | BPLOG(ERROR) << "MinidumpModuleList could not read module count" ; |
2699 | return false; |
2700 | } |
2701 | |
2702 | if (minidump_->swap()) |
2703 | Swap(&module_count); |
2704 | |
2705 | if (module_count > numeric_limits<uint32_t>::max() / MD_MODULE_SIZE) { |
2706 | BPLOG(ERROR) << "MinidumpModuleList module count " << module_count << |
2707 | " would cause multiplication overflow" ; |
2708 | return false; |
2709 | } |
2710 | |
2711 | if (expected_size != sizeof(module_count) + |
2712 | module_count * MD_MODULE_SIZE) { |
2713 | // may be padded with 4 bytes on 64bit ABIs for alignment |
2714 | if (expected_size == sizeof(module_count) + 4 + |
2715 | module_count * MD_MODULE_SIZE) { |
2716 | uint32_t useless; |
2717 | if (!minidump_->ReadBytes(&useless, 4)) { |
2718 | BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded " |
2719 | "bytes" ; |
2720 | return false; |
2721 | } |
2722 | } else { |
2723 | BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << |
2724 | " != " << sizeof(module_count) + |
2725 | module_count * MD_MODULE_SIZE; |
2726 | return false; |
2727 | } |
2728 | } |
2729 | |
2730 | if (module_count > max_modules_) { |
2731 | BPLOG(ERROR) << "MinidumpModuleList count " << module_count << |
2732 | " exceeds maximum " << max_modules_; |
2733 | return false; |
2734 | } |
2735 | |
2736 | if (module_count != 0) { |
2737 | scoped_ptr<MinidumpModules> modules( |
2738 | new MinidumpModules(module_count, MinidumpModule(minidump_))); |
2739 | |
2740 | for (uint32_t module_index = 0; module_index < module_count; |
2741 | ++module_index) { |
2742 | MinidumpModule* module = &(*modules)[module_index]; |
2743 | |
2744 | // Assume that the file offset is correct after the last read. |
2745 | if (!module->Read()) { |
2746 | BPLOG(ERROR) << "MinidumpModuleList could not read module " << |
2747 | module_index << "/" << module_count; |
2748 | return false; |
2749 | } |
2750 | } |
2751 | |
2752 | // Loop through the module list once more to read additional data and |
2753 | // build the range map. This is done in a second pass because |
2754 | // MinidumpModule::ReadAuxiliaryData seeks around, and if it were |
2755 | // included in the loop above, additional seeks would be needed where |
2756 | // none are now to read contiguous data. |
2757 | uint64_t last_end_address = 0; |
2758 | for (uint32_t module_index = 0; module_index < module_count; |
2759 | ++module_index) { |
2760 | MinidumpModule& module = (*modules)[module_index]; |
2761 | |
2762 | // ReadAuxiliaryData fails if any data that the module indicates should |
2763 | // exist is missing, but we treat some such cases as valid anyway. See |
2764 | // issue #222: if a debugging record is of a format that's too large to |
2765 | // handle, it shouldn't render the entire dump invalid. Check module |
2766 | // validity before giving up. |
2767 | if (!module.ReadAuxiliaryData() && !module.valid()) { |
2768 | BPLOG(ERROR) << "MinidumpModuleList could not read required module " |
2769 | "auxiliary data for module " << |
2770 | module_index << "/" << module_count; |
2771 | return false; |
2772 | } |
2773 | |
2774 | // It is safe to use module->code_file() after successfully calling |
2775 | // module->ReadAuxiliaryData or noting that the module is valid. |
2776 | |
2777 | uint64_t base_address = module.base_address(); |
2778 | uint64_t module_size = module.size(); |
2779 | if (base_address == static_cast<uint64_t>(-1)) { |
2780 | BPLOG(ERROR) << "MinidumpModuleList found bad base address for module " |
2781 | << module_index << "/" << module_count << ", " |
2782 | << module.code_file(); |
2783 | return false; |
2784 | } |
2785 | |
2786 | // Some minidumps have additional modules in the list that are duplicates. |
2787 | // Ignore them. See https://crbug.com/838322 |
2788 | uint32_t existing_module_index; |
2789 | if (range_map_->RetrieveRange(base_address, &existing_module_index, |
2790 | nullptr, nullptr, nullptr) && |
2791 | existing_module_index < module_count) { |
2792 | const MinidumpModule& existing_module = |
2793 | (*modules)[existing_module_index]; |
2794 | if (existing_module.base_address() == module.base_address() && |
2795 | existing_module.size() == module.size() && |
2796 | existing_module.code_file() == module.code_file() && |
2797 | existing_module.code_identifier() == module.code_identifier()) { |
2798 | continue; |
2799 | } |
2800 | } |
2801 | |
2802 | const bool is_android = minidump_->IsAndroid(); |
2803 | if (!StoreRange(module, base_address, module_index, module_count, |
2804 | is_android)) { |
2805 | if (!is_android || base_address >= last_end_address) { |
2806 | BPLOG(ERROR) << "MinidumpModuleList could not store module " |
2807 | << module_index << "/" << module_count << ", " |
2808 | << module.code_file() << ", " << HexString(base_address) |
2809 | << "+" << HexString(module_size); |
2810 | return false; |
2811 | } |
2812 | |
2813 | // If failed due to apparent range overlap the cause may be the client |
2814 | // correction applied for Android packed relocations. If this is the |
2815 | // case, back out the client correction and retry. |
2816 | assert(is_android); |
2817 | module_size -= last_end_address - base_address; |
2818 | base_address = last_end_address; |
2819 | if (!range_map_->StoreRange(base_address, module_size, module_index)) { |
2820 | BPLOG(ERROR) << "MinidumpModuleList could not store module " |
2821 | << module_index << "/" << module_count << ", " |
2822 | << module.code_file() << ", " << HexString(base_address) |
2823 | << "+" << HexString(module_size) << ", after adjusting" ; |
2824 | return false; |
2825 | } |
2826 | } |
2827 | last_end_address = base_address + module_size; |
2828 | } |
2829 | |
2830 | modules_ = modules.release(); |
2831 | } |
2832 | |
2833 | module_count_ = module_count; |
2834 | |
2835 | valid_ = true; |
2836 | return true; |
2837 | } |
2838 | |
2839 | bool MinidumpModuleList::StoreRange(const MinidumpModule& module, |
2840 | uint64_t base_address, |
2841 | uint32_t module_index, |
2842 | uint32_t module_count, |
2843 | bool is_android) { |
2844 | if (range_map_->StoreRange(base_address, module.size(), module_index)) |
2845 | return true; |
2846 | |
2847 | // Android's shared memory implementation /dev/ashmem can contain duplicate |
2848 | // entries for JITted code, so ignore these. |
2849 | // TODO(wfh): Remove this code when Android is fixed. |
2850 | // See https://crbug.com/439531 |
2851 | if (is_android && IsDevAshmem(module.code_file())) { |
2852 | BPLOG(INFO) << "MinidumpModuleList ignoring overlapping module " |
2853 | << module_index << "/" << module_count << ", " |
2854 | << module.code_file() << ", " << HexString(base_address) << "+" |
2855 | << HexString(module.size()); |
2856 | return true; |
2857 | } |
2858 | |
2859 | return false; |
2860 | } |
2861 | |
2862 | const MinidumpModule* MinidumpModuleList::GetModuleForAddress( |
2863 | uint64_t address) const { |
2864 | if (!valid_) { |
2865 | BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress" ; |
2866 | return NULL; |
2867 | } |
2868 | |
2869 | unsigned int module_index; |
2870 | if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */, |
2871 | NULL /* delta */, NULL /* size */)) { |
2872 | BPLOG(INFO) << "MinidumpModuleList has no module at " << |
2873 | HexString(address); |
2874 | return NULL; |
2875 | } |
2876 | |
2877 | return GetModuleAtIndex(module_index); |
2878 | } |
2879 | |
2880 | |
2881 | const MinidumpModule* MinidumpModuleList::GetMainModule() const { |
2882 | if (!valid_) { |
2883 | BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule" ; |
2884 | return NULL; |
2885 | } |
2886 | |
2887 | // The main code module is the first one present in a minidump file's |
2888 | // MDRawModuleList. |
2889 | return GetModuleAtIndex(0); |
2890 | } |
2891 | |
2892 | |
2893 | const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( |
2894 | unsigned int sequence) const { |
2895 | if (!valid_) { |
2896 | BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence" ; |
2897 | return NULL; |
2898 | } |
2899 | |
2900 | if (sequence >= module_count_) { |
2901 | BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " << |
2902 | sequence << "/" << module_count_; |
2903 | return NULL; |
2904 | } |
2905 | |
2906 | unsigned int module_index; |
2907 | if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, |
2908 | NULL /* base */, NULL /* delta */, |
2909 | NULL /* size */)) { |
2910 | BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence; |
2911 | return NULL; |
2912 | } |
2913 | |
2914 | return GetModuleAtIndex(module_index); |
2915 | } |
2916 | |
2917 | |
2918 | const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( |
2919 | unsigned int index) const { |
2920 | if (!valid_) { |
2921 | BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex" ; |
2922 | return NULL; |
2923 | } |
2924 | |
2925 | if (index >= module_count_) { |
2926 | BPLOG(ERROR) << "MinidumpModuleList index out of range: " << |
2927 | index << "/" << module_count_; |
2928 | return NULL; |
2929 | } |
2930 | |
2931 | return &(*modules_)[index]; |
2932 | } |
2933 | |
2934 | |
2935 | const CodeModules* MinidumpModuleList::Copy() const { |
2936 | return new BasicCodeModules(this, range_map_->GetMergeStrategy()); |
2937 | } |
2938 | |
2939 | vector<linked_ptr<const CodeModule> > |
2940 | MinidumpModuleList::GetShrunkRangeModules() const { |
2941 | return vector<linked_ptr<const CodeModule> >(); |
2942 | } |
2943 | |
2944 | void MinidumpModuleList::Print() { |
2945 | if (!valid_) { |
2946 | BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data" ; |
2947 | return; |
2948 | } |
2949 | |
2950 | printf("MinidumpModuleList\n" ); |
2951 | printf(" module_count = %d\n" , module_count_); |
2952 | printf("\n" ); |
2953 | |
2954 | for (unsigned int module_index = 0; |
2955 | module_index < module_count_; |
2956 | ++module_index) { |
2957 | printf("module[%d]\n" , module_index); |
2958 | |
2959 | (*modules_)[module_index].Print(); |
2960 | } |
2961 | } |
2962 | |
2963 | |
2964 | // |
2965 | // MinidumpMemoryList |
2966 | // |
2967 | |
2968 | |
2969 | uint32_t MinidumpMemoryList::max_regions_ = 4096; |
2970 | |
2971 | |
2972 | MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) |
2973 | : MinidumpStream(minidump), |
2974 | range_map_(new RangeMap<uint64_t, unsigned int>()), |
2975 | descriptors_(NULL), |
2976 | regions_(NULL), |
2977 | region_count_(0) { |
2978 | } |
2979 | |
2980 | |
2981 | MinidumpMemoryList::~MinidumpMemoryList() { |
2982 | delete range_map_; |
2983 | delete descriptors_; |
2984 | delete regions_; |
2985 | } |
2986 | |
2987 | |
2988 | bool MinidumpMemoryList::Read(uint32_t expected_size) { |
2989 | // Invalidate cached data. |
2990 | delete descriptors_; |
2991 | descriptors_ = NULL; |
2992 | delete regions_; |
2993 | regions_ = NULL; |
2994 | range_map_->Clear(); |
2995 | region_count_ = 0; |
2996 | |
2997 | valid_ = false; |
2998 | |
2999 | uint32_t region_count; |
3000 | if (expected_size < sizeof(region_count)) { |
3001 | BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " << |
3002 | expected_size << " < " << sizeof(region_count); |
3003 | return false; |
3004 | } |
3005 | if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) { |
3006 | BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count" ; |
3007 | return false; |
3008 | } |
3009 | |
3010 | if (minidump_->swap()) |
3011 | Swap(®ion_count); |
3012 | |
3013 | if (region_count > |
3014 | numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) { |
3015 | BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count << |
3016 | " would cause multiplication overflow" ; |
3017 | return false; |
3018 | } |
3019 | |
3020 | if (expected_size != sizeof(region_count) + |
3021 | region_count * sizeof(MDMemoryDescriptor)) { |
3022 | // may be padded with 4 bytes on 64bit ABIs for alignment |
3023 | if (expected_size == sizeof(region_count) + 4 + |
3024 | region_count * sizeof(MDMemoryDescriptor)) { |
3025 | uint32_t useless; |
3026 | if (!minidump_->ReadBytes(&useless, 4)) { |
3027 | BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded " |
3028 | "bytes" ; |
3029 | return false; |
3030 | } |
3031 | } else { |
3032 | BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << |
3033 | " != " << sizeof(region_count) + |
3034 | region_count * sizeof(MDMemoryDescriptor); |
3035 | return false; |
3036 | } |
3037 | } |
3038 | |
3039 | if (region_count > max_regions_) { |
3040 | BPLOG(ERROR) << "MinidumpMemoryList count " << region_count << |
3041 | " exceeds maximum " << max_regions_; |
3042 | return false; |
3043 | } |
3044 | |
3045 | if (region_count != 0) { |
3046 | scoped_ptr<MemoryDescriptors> descriptors( |
3047 | new MemoryDescriptors(region_count)); |
3048 | |
3049 | // Read the entire array in one fell swoop, instead of reading one entry |
3050 | // at a time in the loop. |
3051 | if (!minidump_->ReadBytes(&(*descriptors)[0], |
3052 | sizeof(MDMemoryDescriptor) * region_count)) { |
3053 | BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list" ; |
3054 | return false; |
3055 | } |
3056 | |
3057 | scoped_ptr<MemoryRegions> regions( |
3058 | new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_))); |
3059 | |
3060 | for (unsigned int region_index = 0; |
3061 | region_index < region_count; |
3062 | ++region_index) { |
3063 | MDMemoryDescriptor* descriptor = &(*descriptors)[region_index]; |
3064 | |
3065 | if (minidump_->swap()) |
3066 | Swap(descriptor); |
3067 | |
3068 | uint64_t base_address = descriptor->start_of_memory_range; |
3069 | uint32_t region_size = descriptor->memory.data_size; |
3070 | |
3071 | // Check for base + size overflow or undersize. |
3072 | if (region_size == 0 || |
3073 | region_size > numeric_limits<uint64_t>::max() - base_address) { |
3074 | BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " << |
3075 | " region " << region_index << "/" << region_count << |
3076 | ", " << HexString(base_address) << "+" << |
3077 | HexString(region_size); |
3078 | return false; |
3079 | } |
3080 | |
3081 | if (!range_map_->StoreRange(base_address, region_size, region_index)) { |
3082 | BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " << |
3083 | region_index << "/" << region_count << ", " << |
3084 | HexString(base_address) << "+" << |
3085 | HexString(region_size); |
3086 | return false; |
3087 | } |
3088 | |
3089 | (*regions)[region_index].SetDescriptor(descriptor); |
3090 | } |
3091 | |
3092 | descriptors_ = descriptors.release(); |
3093 | regions_ = regions.release(); |
3094 | } |
3095 | |
3096 | region_count_ = region_count; |
3097 | |
3098 | valid_ = true; |
3099 | return true; |
3100 | } |
3101 | |
3102 | |
3103 | MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex( |
3104 | unsigned int index) { |
3105 | if (!valid_) { |
3106 | BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex" ; |
3107 | return NULL; |
3108 | } |
3109 | |
3110 | if (index >= region_count_) { |
3111 | BPLOG(ERROR) << "MinidumpMemoryList index out of range: " << |
3112 | index << "/" << region_count_; |
3113 | return NULL; |
3114 | } |
3115 | |
3116 | return &(*regions_)[index]; |
3117 | } |
3118 | |
3119 | |
3120 | MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress( |
3121 | uint64_t address) { |
3122 | if (!valid_) { |
3123 | BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress" ; |
3124 | return NULL; |
3125 | } |
3126 | |
3127 | unsigned int region_index; |
3128 | if (!range_map_->RetrieveRange(address, ®ion_index, NULL /* base */, |
3129 | NULL /* delta */, NULL /* size */)) { |
3130 | BPLOG(INFO) << "MinidumpMemoryList has no memory region at " << |
3131 | HexString(address); |
3132 | return NULL; |
3133 | } |
3134 | |
3135 | return GetMemoryRegionAtIndex(region_index); |
3136 | } |
3137 | |
3138 | |
3139 | void MinidumpMemoryList::Print() { |
3140 | if (!valid_) { |
3141 | BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data" ; |
3142 | return; |
3143 | } |
3144 | |
3145 | printf("MinidumpMemoryList\n" ); |
3146 | printf(" region_count = %d\n" , region_count_); |
3147 | printf("\n" ); |
3148 | |
3149 | for (unsigned int region_index = 0; |
3150 | region_index < region_count_; |
3151 | ++region_index) { |
3152 | MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index]; |
3153 | printf("region[%d]\n" , region_index); |
3154 | printf("MDMemoryDescriptor\n" ); |
3155 | printf(" start_of_memory_range = 0x%" PRIx64 "\n" , |
3156 | descriptor->start_of_memory_range); |
3157 | printf(" memory.data_size = 0x%x\n" , descriptor->memory.data_size); |
3158 | printf(" memory.rva = 0x%x\n" , descriptor->memory.rva); |
3159 | MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index); |
3160 | if (region) { |
3161 | printf("Memory\n" ); |
3162 | region->Print(); |
3163 | } else { |
3164 | printf("No memory\n" ); |
3165 | } |
3166 | printf("\n" ); |
3167 | } |
3168 | } |
3169 | |
3170 | |
3171 | // |
3172 | // MinidumpException |
3173 | // |
3174 | |
3175 | |
3176 | MinidumpException::MinidumpException(Minidump* minidump) |
3177 | : MinidumpStream(minidump), |
3178 | exception_(), |
3179 | context_(NULL) { |
3180 | } |
3181 | |
3182 | |
3183 | MinidumpException::~MinidumpException() { |
3184 | delete context_; |
3185 | } |
3186 | |
3187 | |
3188 | bool MinidumpException::Read(uint32_t expected_size) { |
3189 | // Invalidate cached data. |
3190 | delete context_; |
3191 | context_ = NULL; |
3192 | |
3193 | valid_ = false; |
3194 | |
3195 | if (expected_size != sizeof(exception_)) { |
3196 | BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size << |
3197 | " != " << sizeof(exception_); |
3198 | return false; |
3199 | } |
3200 | |
3201 | if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) { |
3202 | BPLOG(ERROR) << "MinidumpException cannot read exception" ; |
3203 | return false; |
3204 | } |
3205 | |
3206 | if (minidump_->swap()) { |
3207 | Swap(&exception_.thread_id); |
3208 | // exception_.__align is for alignment only and does not need to be |
3209 | // swapped. |
3210 | Swap(&exception_.exception_record.exception_code); |
3211 | Swap(&exception_.exception_record.exception_flags); |
3212 | Swap(&exception_.exception_record.exception_record); |
3213 | Swap(&exception_.exception_record.exception_address); |
3214 | Swap(&exception_.exception_record.number_parameters); |
3215 | // exception_.exception_record.__align is for alignment only and does not |
3216 | // need to be swapped. |
3217 | for (unsigned int parameter_index = 0; |
3218 | parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS; |
3219 | ++parameter_index) { |
3220 | Swap(&exception_.exception_record.exception_information[parameter_index]); |
3221 | } |
3222 | Swap(&exception_.thread_context); |
3223 | } |
3224 | |
3225 | valid_ = true; |
3226 | return true; |
3227 | } |
3228 | |
3229 | |
3230 | bool MinidumpException::GetThreadID(uint32_t* thread_id) const { |
3231 | BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires " |
3232 | "|thread_id|" ; |
3233 | assert(thread_id); |
3234 | *thread_id = 0; |
3235 | |
3236 | if (!valid_) { |
3237 | BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID" ; |
3238 | return false; |
3239 | } |
3240 | |
3241 | *thread_id = exception_.thread_id; |
3242 | return true; |
3243 | } |
3244 | |
3245 | |
3246 | MinidumpContext* MinidumpException::GetContext() { |
3247 | if (!valid_) { |
3248 | BPLOG(ERROR) << "Invalid MinidumpException for GetContext" ; |
3249 | return NULL; |
3250 | } |
3251 | |
3252 | if (!context_) { |
3253 | if (!minidump_->SeekSet(exception_.thread_context.rva)) { |
3254 | BPLOG(ERROR) << "MinidumpException cannot seek to context" ; |
3255 | return NULL; |
3256 | } |
3257 | |
3258 | scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_)); |
3259 | |
3260 | // Don't log as an error if we can still fall back on the thread's context |
3261 | // (which must be possible if we got this far.) |
3262 | if (!context->Read(exception_.thread_context.data_size)) { |
3263 | BPLOG(INFO) << "MinidumpException cannot read context" ; |
3264 | return NULL; |
3265 | } |
3266 | |
3267 | context_ = context.release(); |
3268 | } |
3269 | |
3270 | return context_; |
3271 | } |
3272 | |
3273 | |
3274 | void MinidumpException::Print() { |
3275 | if (!valid_) { |
3276 | BPLOG(ERROR) << "MinidumpException cannot print invalid data" ; |
3277 | return; |
3278 | } |
3279 | |
3280 | printf("MDException\n" ); |
3281 | printf(" thread_id = 0x%x\n" , |
3282 | exception_.thread_id); |
3283 | printf(" exception_record.exception_code = 0x%x\n" , |
3284 | exception_.exception_record.exception_code); |
3285 | printf(" exception_record.exception_flags = 0x%x\n" , |
3286 | exception_.exception_record.exception_flags); |
3287 | printf(" exception_record.exception_record = 0x%" PRIx64 "\n" , |
3288 | exception_.exception_record.exception_record); |
3289 | printf(" exception_record.exception_address = 0x%" PRIx64 "\n" , |
3290 | exception_.exception_record.exception_address); |
3291 | printf(" exception_record.number_parameters = %d\n" , |
3292 | exception_.exception_record.number_parameters); |
3293 | for (unsigned int parameterIndex = 0; |
3294 | parameterIndex < exception_.exception_record.number_parameters; |
3295 | ++parameterIndex) { |
3296 | printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n" , |
3297 | parameterIndex, |
3298 | exception_.exception_record.exception_information[parameterIndex]); |
3299 | } |
3300 | printf(" thread_context.data_size = %d\n" , |
3301 | exception_.thread_context.data_size); |
3302 | printf(" thread_context.rva = 0x%x\n" , |
3303 | exception_.thread_context.rva); |
3304 | MinidumpContext* context = GetContext(); |
3305 | if (context) { |
3306 | printf("\n" ); |
3307 | context->Print(); |
3308 | } else { |
3309 | printf(" (no context)\n" ); |
3310 | printf("\n" ); |
3311 | } |
3312 | } |
3313 | |
3314 | // |
3315 | // MinidumpAssertion |
3316 | // |
3317 | |
3318 | |
3319 | MinidumpAssertion::MinidumpAssertion(Minidump* minidump) |
3320 | : MinidumpStream(minidump), |
3321 | assertion_(), |
3322 | expression_(), |
3323 | function_(), |
3324 | file_() { |
3325 | } |
3326 | |
3327 | |
3328 | MinidumpAssertion::~MinidumpAssertion() { |
3329 | } |
3330 | |
3331 | |
3332 | bool MinidumpAssertion::Read(uint32_t expected_size) { |
3333 | // Invalidate cached data. |
3334 | valid_ = false; |
3335 | |
3336 | if (expected_size != sizeof(assertion_)) { |
3337 | BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << |
3338 | " != " << sizeof(assertion_); |
3339 | return false; |
3340 | } |
3341 | |
3342 | if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { |
3343 | BPLOG(ERROR) << "MinidumpAssertion cannot read assertion" ; |
3344 | return false; |
3345 | } |
3346 | |
3347 | // Each of {expression, function, file} is a UTF-16 string, |
3348 | // we'll convert them to UTF-8 for ease of use. |
3349 | ConvertUTF16BufferToUTF8String(assertion_.expression, |
3350 | sizeof(assertion_.expression), &expression_, |
3351 | minidump_->swap()); |
3352 | ConvertUTF16BufferToUTF8String(assertion_.function, |
3353 | sizeof(assertion_.function), &function_, |
3354 | minidump_->swap()); |
3355 | ConvertUTF16BufferToUTF8String(assertion_.file, sizeof(assertion_.file), |
3356 | &file_, minidump_->swap()); |
3357 | |
3358 | if (minidump_->swap()) { |
3359 | Swap(&assertion_.line); |
3360 | Swap(&assertion_.type); |
3361 | } |
3362 | |
3363 | valid_ = true; |
3364 | return true; |
3365 | } |
3366 | |
3367 | void MinidumpAssertion::Print() { |
3368 | if (!valid_) { |
3369 | BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data" ; |
3370 | return; |
3371 | } |
3372 | |
3373 | printf("MDAssertion\n" ); |
3374 | printf(" expression = %s\n" , |
3375 | expression_.c_str()); |
3376 | printf(" function = %s\n" , |
3377 | function_.c_str()); |
3378 | printf(" file = %s\n" , |
3379 | file_.c_str()); |
3380 | printf(" line = %u\n" , |
3381 | assertion_.line); |
3382 | printf(" type = %u\n" , |
3383 | assertion_.type); |
3384 | printf("\n" ); |
3385 | } |
3386 | |
3387 | // |
3388 | // MinidumpSystemInfo |
3389 | // |
3390 | |
3391 | |
3392 | MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) |
3393 | : MinidumpStream(minidump), |
3394 | system_info_(), |
3395 | csd_version_(NULL), |
3396 | cpu_vendor_(NULL) { |
3397 | } |
3398 | |
3399 | |
3400 | MinidumpSystemInfo::~MinidumpSystemInfo() { |
3401 | delete csd_version_; |
3402 | delete cpu_vendor_; |
3403 | } |
3404 | |
3405 | |
3406 | bool MinidumpSystemInfo::Read(uint32_t expected_size) { |
3407 | // Invalidate cached data. |
3408 | delete csd_version_; |
3409 | csd_version_ = NULL; |
3410 | delete cpu_vendor_; |
3411 | cpu_vendor_ = NULL; |
3412 | |
3413 | valid_ = false; |
3414 | |
3415 | if (expected_size != sizeof(system_info_)) { |
3416 | BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size << |
3417 | " != " << sizeof(system_info_); |
3418 | return false; |
3419 | } |
3420 | |
3421 | if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) { |
3422 | BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info" ; |
3423 | return false; |
3424 | } |
3425 | |
3426 | if (minidump_->swap()) { |
3427 | Swap(&system_info_.processor_architecture); |
3428 | Swap(&system_info_.processor_level); |
3429 | Swap(&system_info_.processor_revision); |
3430 | // number_of_processors and product_type are 8-bit quantities and need no |
3431 | // swapping. |
3432 | Swap(&system_info_.major_version); |
3433 | Swap(&system_info_.minor_version); |
3434 | Swap(&system_info_.build_number); |
3435 | Swap(&system_info_.platform_id); |
3436 | Swap(&system_info_.csd_version_rva); |
3437 | Swap(&system_info_.suite_mask); |
3438 | // Don't swap the reserved2 field because its contents are unknown. |
3439 | |
3440 | if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || |
3441 | system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) { |
3442 | for (unsigned int i = 0; i < 3; ++i) |
3443 | Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]); |
3444 | Swap(&system_info_.cpu.x86_cpu_info.version_information); |
3445 | Swap(&system_info_.cpu.x86_cpu_info.feature_information); |
3446 | Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); |
3447 | } else { |
3448 | for (unsigned int i = 0; i < 2; ++i) |
3449 | Swap(&system_info_.cpu.other_cpu_info.processor_features[i]); |
3450 | } |
3451 | } |
3452 | |
3453 | valid_ = true; |
3454 | return true; |
3455 | } |
3456 | |
3457 | |
3458 | string MinidumpSystemInfo::GetOS() { |
3459 | string os; |
3460 | |
3461 | if (!valid_) { |
3462 | BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS" ; |
3463 | return os; |
3464 | } |
3465 | |
3466 | switch (system_info_.platform_id) { |
3467 | case MD_OS_WIN32_NT: |
3468 | case MD_OS_WIN32_WINDOWS: |
3469 | os = "windows" ; |
3470 | break; |
3471 | |
3472 | case MD_OS_MAC_OS_X: |
3473 | os = "mac" ; |
3474 | break; |
3475 | |
3476 | case MD_OS_IOS: |
3477 | os = "ios" ; |
3478 | break; |
3479 | |
3480 | case MD_OS_LINUX: |
3481 | os = "linux" ; |
3482 | break; |
3483 | |
3484 | case MD_OS_SOLARIS: |
3485 | os = "solaris" ; |
3486 | break; |
3487 | |
3488 | case MD_OS_ANDROID: |
3489 | os = "android" ; |
3490 | break; |
3491 | |
3492 | case MD_OS_PS3: |
3493 | os = "ps3" ; |
3494 | break; |
3495 | |
3496 | case MD_OS_NACL: |
3497 | os = "nacl" ; |
3498 | break; |
3499 | |
3500 | case MD_OS_FUCHSIA: |
3501 | os = "fuchsia" ; |
3502 | break; |
3503 | |
3504 | default: |
3505 | BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " << |
3506 | HexString(system_info_.platform_id); |
3507 | break; |
3508 | } |
3509 | |
3510 | return os; |
3511 | } |
3512 | |
3513 | |
3514 | string MinidumpSystemInfo::GetCPU() { |
3515 | if (!valid_) { |
3516 | BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU" ; |
3517 | return "" ; |
3518 | } |
3519 | |
3520 | string cpu; |
3521 | |
3522 | switch (system_info_.processor_architecture) { |
3523 | case MD_CPU_ARCHITECTURE_X86: |
3524 | case MD_CPU_ARCHITECTURE_X86_WIN64: |
3525 | cpu = "x86" ; |
3526 | break; |
3527 | |
3528 | case MD_CPU_ARCHITECTURE_AMD64: |
3529 | cpu = "x86-64" ; |
3530 | break; |
3531 | |
3532 | case MD_CPU_ARCHITECTURE_PPC: |
3533 | cpu = "ppc" ; |
3534 | break; |
3535 | |
3536 | case MD_CPU_ARCHITECTURE_PPC64: |
3537 | cpu = "ppc64" ; |
3538 | break; |
3539 | |
3540 | case MD_CPU_ARCHITECTURE_SPARC: |
3541 | cpu = "sparc" ; |
3542 | break; |
3543 | |
3544 | case MD_CPU_ARCHITECTURE_ARM: |
3545 | cpu = "arm" ; |
3546 | break; |
3547 | |
3548 | case MD_CPU_ARCHITECTURE_ARM64: |
3549 | case MD_CPU_ARCHITECTURE_ARM64_OLD: |
3550 | cpu = "arm64" ; |
3551 | break; |
3552 | |
3553 | default: |
3554 | BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << |
3555 | HexString(system_info_.processor_architecture); |
3556 | break; |
3557 | } |
3558 | |
3559 | return cpu; |
3560 | } |
3561 | |
3562 | |
3563 | const string* MinidumpSystemInfo::GetCSDVersion() { |
3564 | if (!valid_) { |
3565 | BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion" ; |
3566 | return NULL; |
3567 | } |
3568 | |
3569 | if (!csd_version_) |
3570 | csd_version_ = minidump_->ReadString(system_info_.csd_version_rva); |
3571 | |
3572 | BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read " |
3573 | "CSD version" ; |
3574 | |
3575 | return csd_version_; |
3576 | } |
3577 | |
3578 | |
3579 | const string* MinidumpSystemInfo::GetCPUVendor() { |
3580 | if (!valid_) { |
3581 | BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor" ; |
3582 | return NULL; |
3583 | } |
3584 | |
3585 | // CPU vendor information can only be determined from x86 minidumps. |
3586 | if (!cpu_vendor_ && |
3587 | (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || |
3588 | system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) { |
3589 | char cpu_vendor_string[13]; |
3590 | snprintf(cpu_vendor_string, sizeof(cpu_vendor_string), |
3591 | "%c%c%c%c%c%c%c%c%c%c%c%c" , |
3592 | system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff, |
3593 | (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff, |
3594 | (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff, |
3595 | (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff, |
3596 | system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff, |
3597 | (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff, |
3598 | (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff, |
3599 | (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff, |
3600 | system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff, |
3601 | (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff, |
3602 | (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff, |
3603 | (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff); |
3604 | cpu_vendor_ = new string(cpu_vendor_string); |
3605 | } |
3606 | |
3607 | return cpu_vendor_; |
3608 | } |
3609 | |
3610 | |
3611 | void MinidumpSystemInfo::Print() { |
3612 | if (!valid_) { |
3613 | BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data" ; |
3614 | return; |
3615 | } |
3616 | |
3617 | printf("MDRawSystemInfo\n" ); |
3618 | printf(" processor_architecture = 0x%x (%s)\n" , |
3619 | system_info_.processor_architecture, GetCPU().c_str()); |
3620 | printf(" processor_level = %d\n" , |
3621 | system_info_.processor_level); |
3622 | printf(" processor_revision = 0x%x\n" , |
3623 | system_info_.processor_revision); |
3624 | printf(" number_of_processors = %d\n" , |
3625 | system_info_.number_of_processors); |
3626 | printf(" product_type = %d\n" , |
3627 | system_info_.product_type); |
3628 | printf(" major_version = %d\n" , |
3629 | system_info_.major_version); |
3630 | printf(" minor_version = %d\n" , |
3631 | system_info_.minor_version); |
3632 | printf(" build_number = %d\n" , |
3633 | system_info_.build_number); |
3634 | printf(" platform_id = 0x%x (%s)\n" , |
3635 | system_info_.platform_id, GetOS().c_str()); |
3636 | printf(" csd_version_rva = 0x%x\n" , |
3637 | system_info_.csd_version_rva); |
3638 | printf(" suite_mask = 0x%x\n" , |
3639 | system_info_.suite_mask); |
3640 | if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || |
3641 | system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) { |
3642 | printf(" cpu.x86_cpu_info (valid):\n" ); |
3643 | } else { |
3644 | printf(" cpu.x86_cpu_info (invalid):\n" ); |
3645 | } |
3646 | for (unsigned int i = 0; i < 3; ++i) { |
3647 | printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n" , |
3648 | i, system_info_.cpu.x86_cpu_info.vendor_id[i]); |
3649 | } |
3650 | printf(" cpu.x86_cpu_info.version_information = 0x%x\n" , |
3651 | system_info_.cpu.x86_cpu_info.version_information); |
3652 | printf(" cpu.x86_cpu_info.feature_information = 0x%x\n" , |
3653 | system_info_.cpu.x86_cpu_info.feature_information); |
3654 | printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n" , |
3655 | system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); |
3656 | if (system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86 && |
3657 | system_info_.processor_architecture != MD_CPU_ARCHITECTURE_X86_WIN64) { |
3658 | printf(" cpu.other_cpu_info (valid):\n" ); |
3659 | for (unsigned int i = 0; i < 2; ++i) { |
3660 | printf(" cpu.other_cpu_info.processor_features[%d] = 0x%" PRIx64 "\n" , |
3661 | i, system_info_.cpu.other_cpu_info.processor_features[i]); |
3662 | } |
3663 | } |
3664 | const string* csd_version = GetCSDVersion(); |
3665 | if (csd_version) { |
3666 | printf(" (csd_version) = \"%s\"\n" , |
3667 | csd_version->c_str()); |
3668 | } else { |
3669 | printf(" (csd_version) = (null)\n" ); |
3670 | } |
3671 | const string* cpu_vendor = GetCPUVendor(); |
3672 | if (cpu_vendor) { |
3673 | printf(" (cpu_vendor) = \"%s\"\n" , |
3674 | cpu_vendor->c_str()); |
3675 | } else { |
3676 | printf(" (cpu_vendor) = (null)\n" ); |
3677 | } |
3678 | printf("\n" ); |
3679 | } |
3680 | |
3681 | |
3682 | // |
3683 | // MinidumpUnloadedModule |
3684 | // |
3685 | |
3686 | |
3687 | MinidumpUnloadedModule::MinidumpUnloadedModule(Minidump* minidump) |
3688 | : MinidumpObject(minidump), |
3689 | module_valid_(false), |
3690 | unloaded_module_(), |
3691 | name_(NULL) { |
3692 | |
3693 | } |
3694 | |
3695 | MinidumpUnloadedModule::~MinidumpUnloadedModule() { |
3696 | delete name_; |
3697 | } |
3698 | |
3699 | string MinidumpUnloadedModule::code_file() const { |
3700 | if (!valid_) { |
3701 | BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_file" ; |
3702 | return "" ; |
3703 | } |
3704 | |
3705 | return *name_; |
3706 | } |
3707 | |
3708 | string MinidumpUnloadedModule::code_identifier() const { |
3709 | if (!valid_) { |
3710 | BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for code_identifier" ; |
3711 | return "" ; |
3712 | } |
3713 | |
3714 | MinidumpSystemInfo* minidump_system_info = minidump_->GetSystemInfo(); |
3715 | if (!minidump_system_info) { |
3716 | BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires " |
3717 | "MinidumpSystemInfo" ; |
3718 | return "" ; |
3719 | } |
3720 | |
3721 | const MDRawSystemInfo* raw_system_info = minidump_system_info->system_info(); |
3722 | if (!raw_system_info) { |
3723 | BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires " |
3724 | << "MDRawSystemInfo" ; |
3725 | return "" ; |
3726 | } |
3727 | |
3728 | string identifier; |
3729 | |
3730 | switch (raw_system_info->platform_id) { |
3731 | case MD_OS_WIN32_NT: |
3732 | case MD_OS_WIN32_WINDOWS: { |
3733 | // Use the same format that the MS symbol server uses in filesystem |
3734 | // hierarchies. |
3735 | char identifier_string[17]; |
3736 | snprintf(identifier_string, sizeof(identifier_string), "%08X%x" , |
3737 | unloaded_module_.time_date_stamp, |
3738 | unloaded_module_.size_of_image); |
3739 | identifier = identifier_string; |
3740 | break; |
3741 | } |
3742 | |
3743 | case MD_OS_ANDROID: |
3744 | case MD_OS_LINUX: |
3745 | case MD_OS_MAC_OS_X: |
3746 | case MD_OS_IOS: |
3747 | case MD_OS_SOLARIS: |
3748 | case MD_OS_NACL: |
3749 | case MD_OS_PS3: { |
3750 | // TODO(mmentovai): support uuid extension if present, otherwise fall |
3751 | // back to version (from LC_ID_DYLIB?), otherwise fall back to something |
3752 | // else. |
3753 | identifier = "id" ; |
3754 | break; |
3755 | } |
3756 | |
3757 | default: { |
3758 | // Without knowing what OS generated the dump, we can't generate a good |
3759 | // identifier. Return an empty string, signalling failure. |
3760 | BPLOG(ERROR) << "MinidumpUnloadedModule code_identifier requires known " |
3761 | << "platform, found " |
3762 | << HexString(raw_system_info->platform_id); |
3763 | break; |
3764 | } |
3765 | } |
3766 | |
3767 | return identifier; |
3768 | } |
3769 | |
3770 | string MinidumpUnloadedModule::debug_file() const { |
3771 | return "" ; // No debug info provided with unloaded modules |
3772 | } |
3773 | |
3774 | string MinidumpUnloadedModule::debug_identifier() const { |
3775 | return "" ; // No debug info provided with unloaded modules |
3776 | } |
3777 | |
3778 | string MinidumpUnloadedModule::version() const { |
3779 | return "" ; // No version info provided with unloaded modules |
3780 | } |
3781 | |
3782 | CodeModule* MinidumpUnloadedModule::Copy() const { |
3783 | return new BasicCodeModule(this); |
3784 | } |
3785 | |
3786 | uint64_t MinidumpUnloadedModule::shrink_down_delta() const { |
3787 | return 0; |
3788 | } |
3789 | |
3790 | void MinidumpUnloadedModule::SetShrinkDownDelta(uint64_t shrink_down_delta) { |
3791 | // Not implemented |
3792 | assert(false); |
3793 | } |
3794 | |
3795 | bool MinidumpUnloadedModule::Read(uint32_t expected_size) { |
3796 | |
3797 | delete name_; |
3798 | valid_ = false; |
3799 | |
3800 | if (expected_size < sizeof(unloaded_module_)) { |
3801 | BPLOG(ERROR) << "MinidumpUnloadedModule expected size is less than size " |
3802 | << "of struct " << expected_size << " < " |
3803 | << sizeof(unloaded_module_); |
3804 | return false; |
3805 | } |
3806 | |
3807 | if (!minidump_->ReadBytes(&unloaded_module_, sizeof(unloaded_module_))) { |
3808 | BPLOG(ERROR) << "MinidumpUnloadedModule cannot read module" ; |
3809 | return false; |
3810 | } |
3811 | |
3812 | if (expected_size > sizeof(unloaded_module_)) { |
3813 | uint32_t module_bytes_remaining = expected_size - sizeof(unloaded_module_); |
3814 | off_t pos = minidump_->Tell(); |
3815 | if (!minidump_->SeekSet(pos + module_bytes_remaining)) { |
3816 | BPLOG(ERROR) << "MinidumpUnloadedModule unable to seek to end of module" ; |
3817 | return false; |
3818 | } |
3819 | } |
3820 | |
3821 | if (minidump_->swap()) { |
3822 | Swap(&unloaded_module_.base_of_image); |
3823 | Swap(&unloaded_module_.size_of_image); |
3824 | Swap(&unloaded_module_.checksum); |
3825 | Swap(&unloaded_module_.time_date_stamp); |
3826 | Swap(&unloaded_module_.module_name_rva); |
3827 | } |
3828 | |
3829 | // Check for base + size overflow or undersize. |
3830 | if (unloaded_module_.size_of_image == 0 || |
3831 | unloaded_module_.size_of_image > |
3832 | numeric_limits<uint64_t>::max() - unloaded_module_.base_of_image) { |
3833 | BPLOG(ERROR) << "MinidumpUnloadedModule has a module problem, " << |
3834 | HexString(unloaded_module_.base_of_image) << "+" << |
3835 | HexString(unloaded_module_.size_of_image); |
3836 | return false; |
3837 | } |
3838 | |
3839 | |
3840 | module_valid_ = true; |
3841 | return true; |
3842 | } |
3843 | |
3844 | bool MinidumpUnloadedModule::ReadAuxiliaryData() { |
3845 | if (!module_valid_) { |
3846 | BPLOG(ERROR) << "Invalid MinidumpUnloadedModule for ReadAuxiliaryData" ; |
3847 | return false; |
3848 | } |
3849 | |
3850 | // Each module must have a name. |
3851 | name_ = minidump_->ReadString(unloaded_module_.module_name_rva); |
3852 | if (!name_) { |
3853 | BPLOG(ERROR) << "MinidumpUnloadedModule could not read name" ; |
3854 | return false; |
3855 | } |
3856 | |
3857 | // At this point, we have enough info for the module to be valid. |
3858 | valid_ = true; |
3859 | return true; |
3860 | } |
3861 | |
3862 | // |
3863 | // MinidumpUnloadedModuleList |
3864 | // |
3865 | |
3866 | |
3867 | uint32_t MinidumpUnloadedModuleList::max_modules_ = 2048; |
3868 | |
3869 | |
3870 | MinidumpUnloadedModuleList::MinidumpUnloadedModuleList(Minidump* minidump) |
3871 | : MinidumpStream(minidump), |
3872 | range_map_(new RangeMap<uint64_t, unsigned int>()), |
3873 | unloaded_modules_(NULL), |
3874 | module_count_(0) { |
3875 | range_map_->SetMergeStrategy(MergeRangeStrategy::kTruncateLower); |
3876 | } |
3877 | |
3878 | MinidumpUnloadedModuleList::~MinidumpUnloadedModuleList() { |
3879 | delete range_map_; |
3880 | delete unloaded_modules_; |
3881 | } |
3882 | |
3883 | |
3884 | bool MinidumpUnloadedModuleList::Read(uint32_t expected_size) { |
3885 | range_map_->Clear(); |
3886 | delete unloaded_modules_; |
3887 | unloaded_modules_ = NULL; |
3888 | module_count_ = 0; |
3889 | |
3890 | valid_ = false; |
3891 | |
3892 | uint32_t ; |
3893 | if (!minidump_->ReadBytes(&size_of_header, sizeof(size_of_header))) { |
3894 | BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header size" ; |
3895 | return false; |
3896 | } |
3897 | |
3898 | uint32_t size_of_entry; |
3899 | if (!minidump_->ReadBytes(&size_of_entry, sizeof(size_of_entry))) { |
3900 | BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read entry size" ; |
3901 | return false; |
3902 | } |
3903 | |
3904 | uint32_t number_of_entries; |
3905 | if (!minidump_->ReadBytes(&number_of_entries, sizeof(number_of_entries))) { |
3906 | BPLOG(ERROR) << |
3907 | "MinidumpUnloadedModuleList could not read number of entries" ; |
3908 | return false; |
3909 | } |
3910 | |
3911 | if (minidump_->swap()) { |
3912 | Swap(&size_of_header); |
3913 | Swap(&size_of_entry); |
3914 | Swap(&number_of_entries); |
3915 | } |
3916 | |
3917 | uint32_t header_bytes_remaining = size_of_header - sizeof(size_of_header) - |
3918 | sizeof(size_of_entry) - sizeof(number_of_entries); |
3919 | if (header_bytes_remaining) { |
3920 | off_t pos = minidump_->Tell(); |
3921 | if (!minidump_->SeekSet(pos + header_bytes_remaining)) { |
3922 | BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read header sized " |
3923 | << size_of_header; |
3924 | return false; |
3925 | } |
3926 | } |
3927 | |
3928 | if (expected_size != size_of_header + (size_of_entry * number_of_entries)) { |
3929 | BPLOG(ERROR) << "MinidumpUnloadedModuleList expected_size mismatch " << |
3930 | expected_size << " != " << size_of_header << " + (" << |
3931 | size_of_entry << " * " << number_of_entries << ")" ; |
3932 | return false; |
3933 | } |
3934 | |
3935 | if (number_of_entries > max_modules_) { |
3936 | BPLOG(ERROR) << "MinidumpUnloadedModuleList count " << |
3937 | number_of_entries << " exceeds maximum " << max_modules_; |
3938 | return false; |
3939 | } |
3940 | |
3941 | if (number_of_entries != 0) { |
3942 | scoped_ptr<MinidumpUnloadedModules> modules( |
3943 | new MinidumpUnloadedModules(number_of_entries, |
3944 | MinidumpUnloadedModule(minidump_))); |
3945 | |
3946 | for (unsigned int module_index = 0; |
3947 | module_index < number_of_entries; |
3948 | ++module_index) { |
3949 | MinidumpUnloadedModule* module = &(*modules)[module_index]; |
3950 | |
3951 | if (!module->Read(size_of_entry)) { |
3952 | BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read module " << |
3953 | module_index << "/" << number_of_entries; |
3954 | return false; |
3955 | } |
3956 | } |
3957 | |
3958 | for (unsigned int module_index = 0; |
3959 | module_index < number_of_entries; |
3960 | ++module_index) { |
3961 | MinidumpUnloadedModule* module = &(*modules)[module_index]; |
3962 | |
3963 | if (!module->ReadAuxiliaryData()) { |
3964 | BPLOG(ERROR) << "MinidumpUnloadedModuleList could not read required " |
3965 | "module auxiliary data for module " << |
3966 | module_index << "/" << number_of_entries; |
3967 | return false; |
3968 | } |
3969 | |
3970 | uint64_t base_address = module->base_address(); |
3971 | uint64_t module_size = module->size(); |
3972 | |
3973 | // Ignore any failures for conflicting address ranges |
3974 | range_map_->StoreRange(base_address, module_size, module_index); |
3975 | |
3976 | } |
3977 | unloaded_modules_ = modules.release(); |
3978 | } |
3979 | |
3980 | module_count_ = number_of_entries; |
3981 | valid_ = true; |
3982 | return true; |
3983 | } |
3984 | |
3985 | const MinidumpUnloadedModule* MinidumpUnloadedModuleList::GetModuleForAddress( |
3986 | uint64_t address) const { |
3987 | if (!valid_) { |
3988 | BPLOG(ERROR) |
3989 | << "Invalid MinidumpUnloadedModuleList for GetModuleForAddress" ; |
3990 | return NULL; |
3991 | } |
3992 | |
3993 | unsigned int module_index; |
3994 | if (!range_map_->RetrieveRange(address, &module_index, NULL /* base */, |
3995 | NULL /* delta */, NULL /* size */)) { |
3996 | BPLOG(INFO) << "MinidumpUnloadedModuleList has no module at " |
3997 | << HexString(address); |
3998 | return NULL; |
3999 | } |
4000 | |
4001 | return GetModuleAtIndex(module_index); |
4002 | } |
4003 | |
4004 | const MinidumpUnloadedModule* |
4005 | MinidumpUnloadedModuleList::GetMainModule() const { |
4006 | return NULL; |
4007 | } |
4008 | |
4009 | const MinidumpUnloadedModule* |
4010 | MinidumpUnloadedModuleList::GetModuleAtSequence(unsigned int sequence) const { |
4011 | if (!valid_) { |
4012 | BPLOG(ERROR) |
4013 | << "Invalid MinidumpUnloadedModuleList for GetModuleAtSequence" ; |
4014 | return NULL; |
4015 | } |
4016 | |
4017 | if (sequence >= module_count_) { |
4018 | BPLOG(ERROR) << "MinidumpUnloadedModuleList sequence out of range: " |
4019 | << sequence << "/" << module_count_; |
4020 | return NULL; |
4021 | } |
4022 | |
4023 | unsigned int module_index; |
4024 | if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, |
4025 | NULL /* base */, NULL /* delta */, |
4026 | NULL /* size */)) { |
4027 | BPLOG(ERROR) << "MinidumpUnloadedModuleList has no module at sequence " |
4028 | << sequence; |
4029 | return NULL; |
4030 | } |
4031 | |
4032 | return GetModuleAtIndex(module_index); |
4033 | } |
4034 | |
4035 | const MinidumpUnloadedModule* |
4036 | MinidumpUnloadedModuleList::GetModuleAtIndex( |
4037 | unsigned int index) const { |
4038 | if (!valid_) { |
4039 | BPLOG(ERROR) << "Invalid MinidumpUnloadedModuleList for GetModuleAtIndex" ; |
4040 | return NULL; |
4041 | } |
4042 | |
4043 | if (index >= module_count_) { |
4044 | BPLOG(ERROR) << "MinidumpUnloadedModuleList index out of range: " |
4045 | << index << "/" << module_count_; |
4046 | return NULL; |
4047 | } |
4048 | |
4049 | return &(*unloaded_modules_)[index]; |
4050 | } |
4051 | |
4052 | const CodeModules* MinidumpUnloadedModuleList::Copy() const { |
4053 | return new BasicCodeModules(this, range_map_->GetMergeStrategy()); |
4054 | } |
4055 | |
4056 | vector<linked_ptr<const CodeModule>> |
4057 | MinidumpUnloadedModuleList::GetShrunkRangeModules() const { |
4058 | return vector<linked_ptr<const CodeModule> >(); |
4059 | } |
4060 | |
4061 | |
4062 | // |
4063 | // MinidumpMiscInfo |
4064 | // |
4065 | |
4066 | |
4067 | MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump) |
4068 | : MinidumpStream(minidump), |
4069 | misc_info_() { |
4070 | } |
4071 | |
4072 | |
4073 | bool MinidumpMiscInfo::Read(uint32_t expected_size) { |
4074 | valid_ = false; |
4075 | |
4076 | size_t padding = 0; |
4077 | if (expected_size != MD_MISCINFO_SIZE && |
4078 | expected_size != MD_MISCINFO2_SIZE && |
4079 | expected_size != MD_MISCINFO3_SIZE && |
4080 | expected_size != MD_MISCINFO4_SIZE && |
4081 | expected_size != MD_MISCINFO5_SIZE) { |
4082 | if (expected_size > MD_MISCINFO5_SIZE) { |
4083 | // Only read the part of the misc info structure we know how to handle |
4084 | BPLOG(INFO) << "MinidumpMiscInfo size larger than expected " |
4085 | << expected_size << ", skipping over the unknown part" ; |
4086 | padding = expected_size - MD_MISCINFO5_SIZE; |
4087 | expected_size = MD_MISCINFO5_SIZE; |
4088 | } else { |
4089 | BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size |
4090 | << " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE |
4091 | << ", " << MD_MISCINFO3_SIZE << ", " << MD_MISCINFO4_SIZE |
4092 | << ", " << MD_MISCINFO5_SIZE << ")" ; |
4093 | return false; |
4094 | } |
4095 | } |
4096 | |
4097 | if (!minidump_->ReadBytes(&misc_info_, expected_size)) { |
4098 | BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info" ; |
4099 | return false; |
4100 | } |
4101 | |
4102 | if (padding != 0) { |
4103 | off_t saved_position = minidump_->Tell(); |
4104 | if (saved_position == -1) { |
4105 | BPLOG(ERROR) << "MinidumpMiscInfo could not tell the current position" ; |
4106 | return false; |
4107 | } |
4108 | |
4109 | if (!minidump_->SeekSet(saved_position + static_cast<off_t>(padding))) { |
4110 | BPLOG(ERROR) << "MinidumpMiscInfo could not seek past the miscellaneous " |
4111 | << "info structure" ; |
4112 | return false; |
4113 | } |
4114 | } |
4115 | |
4116 | if (minidump_->swap()) { |
4117 | // Swap version 1 fields |
4118 | Swap(&misc_info_.size_of_info); |
4119 | Swap(&misc_info_.flags1); |
4120 | Swap(&misc_info_.process_id); |
4121 | Swap(&misc_info_.process_create_time); |
4122 | Swap(&misc_info_.process_user_time); |
4123 | Swap(&misc_info_.process_kernel_time); |
4124 | if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { |
4125 | // Swap version 2 fields |
4126 | Swap(&misc_info_.processor_max_mhz); |
4127 | Swap(&misc_info_.processor_current_mhz); |
4128 | Swap(&misc_info_.processor_mhz_limit); |
4129 | Swap(&misc_info_.processor_max_idle_state); |
4130 | Swap(&misc_info_.processor_current_idle_state); |
4131 | } |
4132 | if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { |
4133 | // Swap version 3 fields |
4134 | Swap(&misc_info_.process_integrity_level); |
4135 | Swap(&misc_info_.process_execute_flags); |
4136 | Swap(&misc_info_.protected_process); |
4137 | Swap(&misc_info_.time_zone_id); |
4138 | Swap(&misc_info_.time_zone); |
4139 | } |
4140 | if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { |
4141 | // Swap version 4 fields. |
4142 | // Do not swap UTF-16 strings. The swap is done as part of the |
4143 | // conversion to UTF-8 (code follows below). |
4144 | } |
4145 | if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) { |
4146 | // Swap version 5 fields |
4147 | Swap(&misc_info_.xstate_data); |
4148 | Swap(&misc_info_.process_cookie); |
4149 | } |
4150 | } |
4151 | |
4152 | if (expected_size + padding != misc_info_.size_of_info) { |
4153 | BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << |
4154 | expected_size << " != " << misc_info_.size_of_info; |
4155 | return false; |
4156 | } |
4157 | |
4158 | // Convert UTF-16 strings |
4159 | if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { |
4160 | // Convert UTF-16 strings in version 3 fields |
4161 | ConvertUTF16BufferToUTF8String(misc_info_.time_zone.standard_name, |
4162 | sizeof(misc_info_.time_zone.standard_name), |
4163 | &standard_name_, minidump_->swap()); |
4164 | ConvertUTF16BufferToUTF8String(misc_info_.time_zone.daylight_name, |
4165 | sizeof(misc_info_.time_zone.daylight_name), |
4166 | &daylight_name_, minidump_->swap()); |
4167 | } |
4168 | if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { |
4169 | // Convert UTF-16 strings in version 4 fields |
4170 | ConvertUTF16BufferToUTF8String(misc_info_.build_string, |
4171 | sizeof(misc_info_.build_string), |
4172 | &build_string_, minidump_->swap()); |
4173 | ConvertUTF16BufferToUTF8String(misc_info_.dbg_bld_str, |
4174 | sizeof(misc_info_.dbg_bld_str), |
4175 | &dbg_bld_str_, minidump_->swap()); |
4176 | } |
4177 | |
4178 | valid_ = true; |
4179 | return true; |
4180 | } |
4181 | |
4182 | |
4183 | void MinidumpMiscInfo::Print() { |
4184 | if (!valid_) { |
4185 | BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data" ; |
4186 | return; |
4187 | } |
4188 | |
4189 | printf("MDRawMiscInfo\n" ); |
4190 | // Print version 1 fields |
4191 | printf(" size_of_info = %d\n" , misc_info_.size_of_info); |
4192 | printf(" flags1 = 0x%x\n" , misc_info_.flags1); |
4193 | printf(" process_id = " ); |
4194 | PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_ID, |
4195 | kNumberFormatDecimal, misc_info_.process_id); |
4196 | if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES) { |
4197 | printf(" process_create_time = 0x%x %s\n" , |
4198 | misc_info_.process_create_time, |
4199 | TimeTToUTCString(misc_info_.process_create_time).c_str()); |
4200 | } else { |
4201 | printf(" process_create_time = (invalid)\n" ); |
4202 | } |
4203 | printf(" process_user_time = " ); |
4204 | PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES, |
4205 | kNumberFormatDecimal, misc_info_.process_user_time); |
4206 | printf(" process_kernel_time = " ); |
4207 | PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_TIMES, |
4208 | kNumberFormatDecimal, misc_info_.process_kernel_time); |
4209 | if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { |
4210 | // Print version 2 fields |
4211 | printf(" processor_max_mhz = " ); |
4212 | PrintValueOrInvalid(misc_info_.flags1 & |
4213 | MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO, |
4214 | kNumberFormatDecimal, misc_info_.processor_max_mhz); |
4215 | printf(" processor_current_mhz = " ); |
4216 | PrintValueOrInvalid(misc_info_.flags1 & |
4217 | MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO, |
4218 | kNumberFormatDecimal, misc_info_.processor_current_mhz); |
4219 | printf(" processor_mhz_limit = " ); |
4220 | PrintValueOrInvalid(misc_info_.flags1 & |
4221 | MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO, |
4222 | kNumberFormatDecimal, misc_info_.processor_mhz_limit); |
4223 | printf(" processor_max_idle_state = " ); |
4224 | PrintValueOrInvalid(misc_info_.flags1 & |
4225 | MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO, |
4226 | kNumberFormatDecimal, |
4227 | misc_info_.processor_max_idle_state); |
4228 | printf(" processor_current_idle_state = " ); |
4229 | PrintValueOrInvalid(misc_info_.flags1 & |
4230 | MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO, |
4231 | kNumberFormatDecimal, |
4232 | misc_info_.processor_current_idle_state); |
4233 | } |
4234 | if (misc_info_.size_of_info > MD_MISCINFO2_SIZE) { |
4235 | // Print version 3 fields |
4236 | printf(" process_integrity_level = " ); |
4237 | PrintValueOrInvalid(misc_info_.flags1 & |
4238 | MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY, |
4239 | kNumberFormatHexadecimal, |
4240 | misc_info_.process_integrity_level); |
4241 | printf(" process_execute_flags = " ); |
4242 | PrintValueOrInvalid(misc_info_.flags1 & |
4243 | MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS, |
4244 | kNumberFormatHexadecimal, |
4245 | misc_info_.process_execute_flags); |
4246 | printf(" protected_process = " ); |
4247 | PrintValueOrInvalid(misc_info_.flags1 & |
4248 | MD_MISCINFO_FLAGS1_PROTECTED_PROCESS, |
4249 | kNumberFormatDecimal, misc_info_.protected_process); |
4250 | printf(" time_zone_id = " ); |
4251 | PrintValueOrInvalid(misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE, |
4252 | kNumberFormatDecimal, misc_info_.time_zone_id); |
4253 | if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_TIMEZONE) { |
4254 | printf(" time_zone.bias = %d\n" , |
4255 | misc_info_.time_zone.bias); |
4256 | printf(" time_zone.standard_name = %s\n" , standard_name_.c_str()); |
4257 | printf(" time_zone.standard_date = " |
4258 | "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n" , |
4259 | misc_info_.time_zone.standard_date.year, |
4260 | misc_info_.time_zone.standard_date.month, |
4261 | misc_info_.time_zone.standard_date.day, |
4262 | misc_info_.time_zone.standard_date.day_of_week, |
4263 | misc_info_.time_zone.standard_date.hour, |
4264 | misc_info_.time_zone.standard_date.minute, |
4265 | misc_info_.time_zone.standard_date.second, |
4266 | misc_info_.time_zone.standard_date.milliseconds); |
4267 | printf(" time_zone.standard_bias = %d\n" , |
4268 | misc_info_.time_zone.standard_bias); |
4269 | printf(" time_zone.daylight_name = %s\n" , daylight_name_.c_str()); |
4270 | printf(" time_zone.daylight_date = " |
4271 | "%04d-%02d-%02d (%d) %02d:%02d:%02d.%03d\n" , |
4272 | misc_info_.time_zone.daylight_date.year, |
4273 | misc_info_.time_zone.daylight_date.month, |
4274 | misc_info_.time_zone.daylight_date.day, |
4275 | misc_info_.time_zone.daylight_date.day_of_week, |
4276 | misc_info_.time_zone.daylight_date.hour, |
4277 | misc_info_.time_zone.daylight_date.minute, |
4278 | misc_info_.time_zone.daylight_date.second, |
4279 | misc_info_.time_zone.daylight_date.milliseconds); |
4280 | printf(" time_zone.daylight_bias = %d\n" , |
4281 | misc_info_.time_zone.daylight_bias); |
4282 | } else { |
4283 | printf(" time_zone.bias = (invalid)\n" ); |
4284 | printf(" time_zone.standard_name = (invalid)\n" ); |
4285 | printf(" time_zone.standard_date = (invalid)\n" ); |
4286 | printf(" time_zone.standard_bias = (invalid)\n" ); |
4287 | printf(" time_zone.daylight_name = (invalid)\n" ); |
4288 | printf(" time_zone.daylight_date = (invalid)\n" ); |
4289 | printf(" time_zone.daylight_bias = (invalid)\n" ); |
4290 | } |
4291 | } |
4292 | if (misc_info_.size_of_info > MD_MISCINFO3_SIZE) { |
4293 | // Print version 4 fields |
4294 | if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_BUILDSTRING) { |
4295 | printf(" build_string = %s\n" , build_string_.c_str()); |
4296 | printf(" dbg_bld_str = %s\n" , dbg_bld_str_.c_str()); |
4297 | } else { |
4298 | printf(" build_string = (invalid)\n" ); |
4299 | printf(" dbg_bld_str = (invalid)\n" ); |
4300 | } |
4301 | } |
4302 | if (misc_info_.size_of_info > MD_MISCINFO4_SIZE) { |
4303 | // Print version 5 fields |
4304 | if (misc_info_.flags1 & MD_MISCINFO_FLAGS1_PROCESS_COOKIE) { |
4305 | printf(" xstate_data.size_of_info = %d\n" , |
4306 | misc_info_.xstate_data.size_of_info); |
4307 | printf(" xstate_data.context_size = %d\n" , |
4308 | misc_info_.xstate_data.context_size); |
4309 | printf(" xstate_data.enabled_features = 0x%" PRIx64 "\n" , |
4310 | misc_info_.xstate_data.enabled_features); |
4311 | for (size_t i = 0; i < MD_MAXIMUM_XSTATE_FEATURES; i++) { |
4312 | if ((misc_info_.xstate_data.enabled_features >> i) & 1) { |
4313 | printf(" xstate_data.features[%02zu] = { %d, %d }\n" , i, |
4314 | misc_info_.xstate_data.features[i].offset, |
4315 | misc_info_.xstate_data.features[i].size); |
4316 | } |
4317 | } |
4318 | if (misc_info_.xstate_data.enabled_features == 0) { |
4319 | printf(" xstate_data.features[] = (empty)\n" ); |
4320 | } |
4321 | printf(" process_cookie = %d\n" , |
4322 | misc_info_.process_cookie); |
4323 | } else { |
4324 | printf(" xstate_data.size_of_info = (invalid)\n" ); |
4325 | printf(" xstate_data.context_size = (invalid)\n" ); |
4326 | printf(" xstate_data.enabled_features = (invalid)\n" ); |
4327 | printf(" xstate_data.features[] = (invalid)\n" ); |
4328 | printf(" process_cookie = (invalid)\n" ); |
4329 | } |
4330 | } |
4331 | printf("\n" ); |
4332 | } |
4333 | |
4334 | |
4335 | // |
4336 | // MinidumpBreakpadInfo |
4337 | // |
4338 | |
4339 | |
4340 | MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump) |
4341 | : MinidumpStream(minidump), |
4342 | breakpad_info_() { |
4343 | } |
4344 | |
4345 | |
4346 | bool MinidumpBreakpadInfo::Read(uint32_t expected_size) { |
4347 | valid_ = false; |
4348 | |
4349 | if (expected_size != sizeof(breakpad_info_)) { |
4350 | BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size << |
4351 | " != " << sizeof(breakpad_info_); |
4352 | return false; |
4353 | } |
4354 | |
4355 | if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) { |
4356 | BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info" ; |
4357 | return false; |
4358 | } |
4359 | |
4360 | if (minidump_->swap()) { |
4361 | Swap(&breakpad_info_.validity); |
4362 | Swap(&breakpad_info_.dump_thread_id); |
4363 | Swap(&breakpad_info_.requesting_thread_id); |
4364 | } |
4365 | |
4366 | valid_ = true; |
4367 | return true; |
4368 | } |
4369 | |
4370 | |
4371 | bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t* thread_id) const { |
4372 | BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID " |
4373 | "requires |thread_id|" ; |
4374 | assert(thread_id); |
4375 | *thread_id = 0; |
4376 | |
4377 | if (!valid_) { |
4378 | BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID" ; |
4379 | return false; |
4380 | } |
4381 | |
4382 | if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) { |
4383 | BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread" ; |
4384 | return false; |
4385 | } |
4386 | |
4387 | *thread_id = breakpad_info_.dump_thread_id; |
4388 | return true; |
4389 | } |
4390 | |
4391 | |
4392 | bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t* thread_id) |
4393 | const { |
4394 | BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID " |
4395 | "requires |thread_id|" ; |
4396 | assert(thread_id); |
4397 | *thread_id = 0; |
4398 | |
4399 | if (!thread_id || !valid_) { |
4400 | BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID" ; |
4401 | return false; |
4402 | } |
4403 | |
4404 | if (!(breakpad_info_.validity & |
4405 | MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) { |
4406 | BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread" ; |
4407 | return false; |
4408 | } |
4409 | |
4410 | *thread_id = breakpad_info_.requesting_thread_id; |
4411 | return true; |
4412 | } |
4413 | |
4414 | |
4415 | void MinidumpBreakpadInfo::Print() { |
4416 | if (!valid_) { |
4417 | BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data" ; |
4418 | return; |
4419 | } |
4420 | |
4421 | printf("MDRawBreakpadInfo\n" ); |
4422 | printf(" validity = 0x%x\n" , breakpad_info_.validity); |
4423 | printf(" dump_thread_id = " ); |
4424 | PrintValueOrInvalid(breakpad_info_.validity & |
4425 | MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID, |
4426 | kNumberFormatHexadecimal, breakpad_info_.dump_thread_id); |
4427 | printf(" requesting_thread_id = " ); |
4428 | PrintValueOrInvalid(breakpad_info_.validity & |
4429 | MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID, |
4430 | kNumberFormatHexadecimal, |
4431 | breakpad_info_.requesting_thread_id); |
4432 | |
4433 | printf("\n" ); |
4434 | } |
4435 | |
4436 | |
4437 | // |
4438 | // MinidumpMemoryInfo |
4439 | // |
4440 | |
4441 | |
4442 | MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump) |
4443 | : MinidumpObject(minidump), |
4444 | memory_info_() { |
4445 | } |
4446 | |
4447 | |
4448 | bool MinidumpMemoryInfo::IsExecutable() const { |
4449 | uint32_t protection = |
4450 | memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; |
4451 | return protection == MD_MEMORY_PROTECT_EXECUTE || |
4452 | protection == MD_MEMORY_PROTECT_EXECUTE_READ || |
4453 | protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE; |
4454 | } |
4455 | |
4456 | |
4457 | bool MinidumpMemoryInfo::IsWritable() const { |
4458 | uint32_t protection = |
4459 | memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; |
4460 | return protection == MD_MEMORY_PROTECT_READWRITE || |
4461 | protection == MD_MEMORY_PROTECT_WRITECOPY || |
4462 | protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE || |
4463 | protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY; |
4464 | } |
4465 | |
4466 | |
4467 | bool MinidumpMemoryInfo::Read() { |
4468 | valid_ = false; |
4469 | |
4470 | if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) { |
4471 | BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info" ; |
4472 | return false; |
4473 | } |
4474 | |
4475 | if (minidump_->swap()) { |
4476 | Swap(&memory_info_.base_address); |
4477 | Swap(&memory_info_.allocation_base); |
4478 | Swap(&memory_info_.allocation_protection); |
4479 | Swap(&memory_info_.region_size); |
4480 | Swap(&memory_info_.state); |
4481 | Swap(&memory_info_.protection); |
4482 | Swap(&memory_info_.type); |
4483 | } |
4484 | |
4485 | // Check for base + size overflow or undersize. |
4486 | if (memory_info_.region_size == 0 || |
4487 | memory_info_.region_size > numeric_limits<uint64_t>::max() - |
4488 | memory_info_.base_address) { |
4489 | BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " << |
4490 | HexString(memory_info_.base_address) << "+" << |
4491 | HexString(memory_info_.region_size); |
4492 | return false; |
4493 | } |
4494 | |
4495 | valid_ = true; |
4496 | return true; |
4497 | } |
4498 | |
4499 | |
4500 | void MinidumpMemoryInfo::Print() { |
4501 | if (!valid_) { |
4502 | BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data" ; |
4503 | return; |
4504 | } |
4505 | |
4506 | printf("MDRawMemoryInfo\n" ); |
4507 | printf(" base_address = 0x%" PRIx64 "\n" , |
4508 | memory_info_.base_address); |
4509 | printf(" allocation_base = 0x%" PRIx64 "\n" , |
4510 | memory_info_.allocation_base); |
4511 | printf(" allocation_protection = 0x%x\n" , |
4512 | memory_info_.allocation_protection); |
4513 | printf(" region_size = 0x%" PRIx64 "\n" , memory_info_.region_size); |
4514 | printf(" state = 0x%x\n" , memory_info_.state); |
4515 | printf(" protection = 0x%x\n" , memory_info_.protection); |
4516 | printf(" type = 0x%x\n" , memory_info_.type); |
4517 | } |
4518 | |
4519 | |
4520 | // |
4521 | // MinidumpMemoryInfoList |
4522 | // |
4523 | |
4524 | |
4525 | MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump) |
4526 | : MinidumpStream(minidump), |
4527 | range_map_(new RangeMap<uint64_t, unsigned int>()), |
4528 | infos_(NULL), |
4529 | info_count_(0) { |
4530 | } |
4531 | |
4532 | |
4533 | MinidumpMemoryInfoList::~MinidumpMemoryInfoList() { |
4534 | delete range_map_; |
4535 | delete infos_; |
4536 | } |
4537 | |
4538 | |
4539 | bool MinidumpMemoryInfoList::Read(uint32_t expected_size) { |
4540 | // Invalidate cached data. |
4541 | delete infos_; |
4542 | infos_ = NULL; |
4543 | range_map_->Clear(); |
4544 | info_count_ = 0; |
4545 | |
4546 | valid_ = false; |
4547 | |
4548 | MDRawMemoryInfoList ; |
4549 | if (expected_size < sizeof(MDRawMemoryInfoList)) { |
4550 | BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << |
4551 | expected_size << " < " << sizeof(MDRawMemoryInfoList); |
4552 | return false; |
4553 | } |
4554 | if (!minidump_->ReadBytes(&header, sizeof(header))) { |
4555 | BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header" ; |
4556 | return false; |
4557 | } |
4558 | |
4559 | if (minidump_->swap()) { |
4560 | Swap(&header.size_of_header); |
4561 | Swap(&header.size_of_entry); |
4562 | Swap(&header.number_of_entries); |
4563 | } |
4564 | |
4565 | // Sanity check that the header is the expected size. |
4566 | // TODO(ted): could possibly handle this more gracefully, assuming |
4567 | // that future versions of the structs would be backwards-compatible. |
4568 | if (header.size_of_header != sizeof(MDRawMemoryInfoList)) { |
4569 | BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << |
4570 | header.size_of_header << " != " << |
4571 | sizeof(MDRawMemoryInfoList); |
4572 | return false; |
4573 | } |
4574 | |
4575 | // Sanity check that the entries are the expected size. |
4576 | if (header.size_of_entry != sizeof(MDRawMemoryInfo)) { |
4577 | BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " << |
4578 | header.size_of_entry << " != " << |
4579 | sizeof(MDRawMemoryInfo); |
4580 | return false; |
4581 | } |
4582 | |
4583 | if (header.number_of_entries > |
4584 | numeric_limits<uint32_t>::max() / sizeof(MDRawMemoryInfo)) { |
4585 | BPLOG(ERROR) << "MinidumpMemoryInfoList info count " << |
4586 | header.number_of_entries << |
4587 | " would cause multiplication overflow" ; |
4588 | return false; |
4589 | } |
4590 | |
4591 | if (expected_size != sizeof(MDRawMemoryInfoList) + |
4592 | header.number_of_entries * sizeof(MDRawMemoryInfo)) { |
4593 | BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size << |
4594 | " != " << sizeof(MDRawMemoryInfoList) + |
4595 | header.number_of_entries * sizeof(MDRawMemoryInfo); |
4596 | return false; |
4597 | } |
4598 | |
4599 | // Check for data loss when converting header.number_of_entries from |
4600 | // uint64_t into MinidumpMemoryInfos::size_type (uint32_t) |
4601 | MinidumpMemoryInfos::size_type = |
4602 | static_cast<unsigned int>(header.number_of_entries); |
4603 | if (static_cast<uint64_t>(header_number_of_entries) != |
4604 | header.number_of_entries) { |
4605 | BPLOG(ERROR) << "Data loss detected when converting " |
4606 | "the header's number_of_entries" ; |
4607 | return false; |
4608 | } |
4609 | |
4610 | if (header.number_of_entries != 0) { |
4611 | scoped_ptr<MinidumpMemoryInfos> infos( |
4612 | new MinidumpMemoryInfos(header_number_of_entries, |
4613 | MinidumpMemoryInfo(minidump_))); |
4614 | |
4615 | for (unsigned int index = 0; |
4616 | index < header.number_of_entries; |
4617 | ++index) { |
4618 | MinidumpMemoryInfo* info = &(*infos)[index]; |
4619 | |
4620 | // Assume that the file offset is correct after the last read. |
4621 | if (!info->Read()) { |
4622 | BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " << |
4623 | index << "/" << header.number_of_entries; |
4624 | return false; |
4625 | } |
4626 | |
4627 | uint64_t base_address = info->GetBase(); |
4628 | uint64_t region_size = info->GetSize(); |
4629 | |
4630 | if (!range_map_->StoreRange(base_address, region_size, index)) { |
4631 | BPLOG(ERROR) << "MinidumpMemoryInfoList could not store" |
4632 | " memory region " << |
4633 | index << "/" << header.number_of_entries << ", " << |
4634 | HexString(base_address) << "+" << |
4635 | HexString(region_size); |
4636 | return false; |
4637 | } |
4638 | } |
4639 | |
4640 | infos_ = infos.release(); |
4641 | } |
4642 | |
4643 | info_count_ = static_cast<uint32_t>(header_number_of_entries); |
4644 | |
4645 | valid_ = true; |
4646 | return true; |
4647 | } |
4648 | |
4649 | |
4650 | const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex( |
4651 | unsigned int index) const { |
4652 | if (!valid_) { |
4653 | BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex" ; |
4654 | return NULL; |
4655 | } |
4656 | |
4657 | if (index >= info_count_) { |
4658 | BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " << |
4659 | index << "/" << info_count_; |
4660 | return NULL; |
4661 | } |
4662 | |
4663 | return &(*infos_)[index]; |
4664 | } |
4665 | |
4666 | |
4667 | const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress( |
4668 | uint64_t address) const { |
4669 | if (!valid_) { |
4670 | BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for" |
4671 | " GetMemoryInfoForAddress" ; |
4672 | return NULL; |
4673 | } |
4674 | |
4675 | unsigned int info_index; |
4676 | if (!range_map_->RetrieveRange(address, &info_index, NULL /* base */, |
4677 | NULL /* delta */, NULL /* size */)) { |
4678 | BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " << |
4679 | HexString(address); |
4680 | return NULL; |
4681 | } |
4682 | |
4683 | return GetMemoryInfoAtIndex(info_index); |
4684 | } |
4685 | |
4686 | |
4687 | void MinidumpMemoryInfoList::Print() { |
4688 | if (!valid_) { |
4689 | BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data" ; |
4690 | return; |
4691 | } |
4692 | |
4693 | printf("MinidumpMemoryInfoList\n" ); |
4694 | printf(" info_count = %d\n" , info_count_); |
4695 | printf("\n" ); |
4696 | |
4697 | for (unsigned int info_index = 0; |
4698 | info_index < info_count_; |
4699 | ++info_index) { |
4700 | printf("info[%d]\n" , info_index); |
4701 | (*infos_)[info_index].Print(); |
4702 | printf("\n" ); |
4703 | } |
4704 | } |
4705 | |
4706 | // |
4707 | // MinidumpLinuxMaps |
4708 | // |
4709 | |
4710 | MinidumpLinuxMaps::MinidumpLinuxMaps(Minidump* minidump) |
4711 | : MinidumpObject(minidump) { |
4712 | } |
4713 | |
4714 | void MinidumpLinuxMaps::Print() const { |
4715 | if (!valid_) { |
4716 | BPLOG(ERROR) << "MinidumpLinuxMaps cannot print invalid data" ; |
4717 | return; |
4718 | } |
4719 | std::cout << region_.line << std::endl; |
4720 | } |
4721 | |
4722 | // |
4723 | // MinidumpLinuxMapsList |
4724 | // |
4725 | |
4726 | MinidumpLinuxMapsList::MinidumpLinuxMapsList(Minidump* minidump) |
4727 | : MinidumpStream(minidump), |
4728 | maps_(NULL), |
4729 | maps_count_(0) { |
4730 | } |
4731 | |
4732 | MinidumpLinuxMapsList::~MinidumpLinuxMapsList() { |
4733 | if (maps_) { |
4734 | for (unsigned int i = 0; i < maps_->size(); i++) { |
4735 | delete (*maps_)[i]; |
4736 | } |
4737 | delete maps_; |
4738 | } |
4739 | } |
4740 | |
4741 | const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsForAddress( |
4742 | uint64_t address) const { |
4743 | if (!valid_ || (maps_ == NULL)) { |
4744 | BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsForAddress" ; |
4745 | return NULL; |
4746 | } |
4747 | |
4748 | // Search every memory mapping. |
4749 | for (unsigned int index = 0; index < maps_count_; index++) { |
4750 | // Check if address is within bounds of the current memory region. |
4751 | if ((*maps_)[index]->GetBase() <= address && |
4752 | (*maps_)[index]->GetBase() + (*maps_)[index]->GetSize() > address) { |
4753 | return (*maps_)[index]; |
4754 | } |
4755 | } |
4756 | |
4757 | // No mapping encloses the memory address. |
4758 | BPLOG(ERROR) << "MinidumpLinuxMapsList has no mapping at " |
4759 | << HexString(address); |
4760 | return NULL; |
4761 | } |
4762 | |
4763 | const MinidumpLinuxMaps* MinidumpLinuxMapsList::GetLinuxMapsAtIndex( |
4764 | unsigned int index) const { |
4765 | if (!valid_ || (maps_ == NULL)) { |
4766 | BPLOG(ERROR) << "Invalid MinidumpLinuxMapsList for GetLinuxMapsAtIndex" ; |
4767 | return NULL; |
4768 | } |
4769 | |
4770 | // Index out of bounds. |
4771 | if (index >= maps_count_ || (maps_ == NULL)) { |
4772 | BPLOG(ERROR) << "MinidumpLinuxMapsList index of out range: " |
4773 | << index |
4774 | << "/" |
4775 | << maps_count_; |
4776 | return NULL; |
4777 | } |
4778 | return (*maps_)[index]; |
4779 | } |
4780 | |
4781 | bool MinidumpLinuxMapsList::Read(uint32_t expected_size) { |
4782 | // Invalidate cached data. |
4783 | if (maps_) { |
4784 | for (unsigned int i = 0; i < maps_->size(); i++) { |
4785 | delete (*maps_)[i]; |
4786 | } |
4787 | delete maps_; |
4788 | } |
4789 | maps_ = NULL; |
4790 | maps_count_ = 0; |
4791 | |
4792 | valid_ = false; |
4793 | |
4794 | // Load and check expected stream length. |
4795 | uint32_t length = 0; |
4796 | if (!minidump_->SeekToStreamType(MD_LINUX_MAPS, &length)) { |
4797 | BPLOG(ERROR) << "MinidumpLinuxMapsList stream type not found" ; |
4798 | return false; |
4799 | } |
4800 | if (expected_size != length) { |
4801 | BPLOG(ERROR) << "MinidumpLinuxMapsList size mismatch: " |
4802 | << expected_size |
4803 | << " != " |
4804 | << length; |
4805 | return false; |
4806 | } |
4807 | |
4808 | // Create a vector to read stream data. The vector needs to have |
4809 | // at least enough capacity to read all the data. |
4810 | vector<char> mapping_bytes(length); |
4811 | if (!minidump_->ReadBytes(&mapping_bytes[0], length)) { |
4812 | BPLOG(ERROR) << "MinidumpLinuxMapsList failed to read bytes" ; |
4813 | return false; |
4814 | } |
4815 | string map_string(mapping_bytes.begin(), mapping_bytes.end()); |
4816 | vector<MappedMemoryRegion> all_regions; |
4817 | |
4818 | // Parse string into mapping data. |
4819 | if (!ParseProcMaps(map_string, &all_regions)) { |
4820 | return false; |
4821 | } |
4822 | |
4823 | scoped_ptr<MinidumpLinuxMappings> maps(new MinidumpLinuxMappings()); |
4824 | |
4825 | // Push mapping data into wrapper classes. |
4826 | for (size_t i = 0; i < all_regions.size(); i++) { |
4827 | scoped_ptr<MinidumpLinuxMaps> ele(new MinidumpLinuxMaps(minidump_)); |
4828 | ele->region_ = all_regions[i]; |
4829 | ele->valid_ = true; |
4830 | maps->push_back(ele.release()); |
4831 | } |
4832 | |
4833 | // Set instance variables. |
4834 | maps_ = maps.release(); |
4835 | maps_count_ = static_cast<uint32_t>(maps_->size()); |
4836 | valid_ = true; |
4837 | return true; |
4838 | } |
4839 | |
4840 | void MinidumpLinuxMapsList::Print() const { |
4841 | if (!valid_ || (maps_ == NULL)) { |
4842 | BPLOG(ERROR) << "MinidumpLinuxMapsList cannot print valid data" ; |
4843 | return; |
4844 | } |
4845 | for (size_t i = 0; i < maps_->size(); i++) { |
4846 | (*maps_)[i]->Print(); |
4847 | } |
4848 | } |
4849 | |
4850 | // |
4851 | // MinidumpCrashpadInfo |
4852 | // |
4853 | |
4854 | |
4855 | MinidumpCrashpadInfo::MinidumpCrashpadInfo(Minidump* minidump) |
4856 | : MinidumpStream(minidump), |
4857 | crashpad_info_(), |
4858 | module_crashpad_info_links_(), |
4859 | module_crashpad_info_(), |
4860 | module_crashpad_info_list_annotations_(), |
4861 | module_crashpad_info_simple_annotations_(), |
4862 | simple_annotations_() { |
4863 | } |
4864 | |
4865 | |
4866 | bool MinidumpCrashpadInfo::Read(uint32_t expected_size) { |
4867 | valid_ = false; |
4868 | |
4869 | if (expected_size != sizeof(crashpad_info_)) { |
4870 | BPLOG(ERROR) << "MinidumpCrashpadInfo size mismatch, " << expected_size << |
4871 | " != " << sizeof(crashpad_info_); |
4872 | return false; |
4873 | } |
4874 | |
4875 | if (!minidump_->ReadBytes(&crashpad_info_, sizeof(crashpad_info_))) { |
4876 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad info" ; |
4877 | return false; |
4878 | } |
4879 | |
4880 | if (minidump_->swap()) { |
4881 | Swap(&crashpad_info_.version); |
4882 | Swap(&crashpad_info_.report_id); |
4883 | Swap(&crashpad_info_.client_id); |
4884 | Swap(&crashpad_info_.simple_annotations); |
4885 | Swap(&crashpad_info_.module_list); |
4886 | } |
4887 | |
4888 | if (crashpad_info_.simple_annotations.data_size) { |
4889 | if (!minidump_->ReadSimpleStringDictionary( |
4890 | crashpad_info_.simple_annotations.rva, |
4891 | &simple_annotations_)) { |
4892 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read simple_annotations" ; |
4893 | return false; |
4894 | } |
4895 | } |
4896 | |
4897 | if (crashpad_info_.module_list.data_size) { |
4898 | if (!minidump_->SeekSet(crashpad_info_.module_list.rva)) { |
4899 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot seek to module_list" ; |
4900 | return false; |
4901 | } |
4902 | |
4903 | uint32_t count; |
4904 | if (!minidump_->ReadBytes(&count, sizeof(count))) { |
4905 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read module_list count" ; |
4906 | return false; |
4907 | } |
4908 | |
4909 | if (minidump_->swap()) { |
4910 | Swap(&count); |
4911 | } |
4912 | |
4913 | scoped_array<MDRawModuleCrashpadInfoLink> module_crashpad_info_links( |
4914 | new MDRawModuleCrashpadInfoLink[count]); |
4915 | |
4916 | // Read the entire array in one fell swoop, instead of reading one entry |
4917 | // at a time in the loop. |
4918 | if (!minidump_->ReadBytes( |
4919 | &module_crashpad_info_links[0], |
4920 | sizeof(MDRawModuleCrashpadInfoLink) * count)) { |
4921 | BPLOG(ERROR) |
4922 | << "MinidumpCrashpadInfo could not read Crashpad module links" ; |
4923 | return false; |
4924 | } |
4925 | |
4926 | for (uint32_t index = 0; index < count; ++index) { |
4927 | if (minidump_->swap()) { |
4928 | Swap(&module_crashpad_info_links[index].minidump_module_list_index); |
4929 | Swap(&module_crashpad_info_links[index].location); |
4930 | } |
4931 | |
4932 | if (!minidump_->SeekSet(module_crashpad_info_links[index].location.rva)) { |
4933 | BPLOG(ERROR) |
4934 | << "MinidumpCrashpadInfo cannot seek to Crashpad module info" ; |
4935 | return false; |
4936 | } |
4937 | |
4938 | MDRawModuleCrashpadInfo module_crashpad_info; |
4939 | if (!minidump_->ReadBytes(&module_crashpad_info, |
4940 | sizeof(module_crashpad_info))) { |
4941 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module info" ; |
4942 | return false; |
4943 | } |
4944 | |
4945 | if (minidump_->swap()) { |
4946 | Swap(&module_crashpad_info.version); |
4947 | Swap(&module_crashpad_info.list_annotations); |
4948 | Swap(&module_crashpad_info.simple_annotations); |
4949 | } |
4950 | |
4951 | std::vector<std::string> list_annotations; |
4952 | if (module_crashpad_info.list_annotations.data_size) { |
4953 | if (!minidump_->ReadStringList( |
4954 | module_crashpad_info.list_annotations.rva, |
4955 | &list_annotations)) { |
4956 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module " |
4957 | "info list annotations" ; |
4958 | return false; |
4959 | } |
4960 | } |
4961 | |
4962 | std::map<std::string, std::string> simple_annotations; |
4963 | if (module_crashpad_info.simple_annotations.data_size) { |
4964 | if (!minidump_->ReadSimpleStringDictionary( |
4965 | module_crashpad_info.simple_annotations.rva, |
4966 | &simple_annotations)) { |
4967 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot read Crashpad module " |
4968 | "info simple annotations" ; |
4969 | return false; |
4970 | } |
4971 | } |
4972 | |
4973 | module_crashpad_info_links_.push_back( |
4974 | module_crashpad_info_links[index].minidump_module_list_index); |
4975 | module_crashpad_info_.push_back(module_crashpad_info); |
4976 | module_crashpad_info_list_annotations_.push_back(list_annotations); |
4977 | module_crashpad_info_simple_annotations_.push_back(simple_annotations); |
4978 | } |
4979 | } |
4980 | |
4981 | valid_ = true; |
4982 | return true; |
4983 | } |
4984 | |
4985 | |
4986 | void MinidumpCrashpadInfo::Print() { |
4987 | if (!valid_) { |
4988 | BPLOG(ERROR) << "MinidumpCrashpadInfo cannot print invalid data" ; |
4989 | return; |
4990 | } |
4991 | |
4992 | printf("MDRawCrashpadInfo\n" ); |
4993 | printf(" version = %d\n" , crashpad_info_.version); |
4994 | printf(" report_id = %s\n" , |
4995 | MDGUIDToString(crashpad_info_.report_id).c_str()); |
4996 | printf(" client_id = %s\n" , |
4997 | MDGUIDToString(crashpad_info_.client_id).c_str()); |
4998 | for (const auto& annot : simple_annotations_) { |
4999 | printf(" simple_annotations[\"%s\"] = %s\n" , annot.first.c_str(), |
5000 | annot.second.c_str()); |
5001 | } |
5002 | for (uint32_t module_index = 0; |
5003 | module_index < module_crashpad_info_links_.size(); |
5004 | ++module_index) { |
5005 | printf(" module_list[%d].minidump_module_list_index = %d\n" , |
5006 | module_index, module_crashpad_info_links_[module_index]); |
5007 | printf(" module_list[%d].version = %d\n" , |
5008 | module_index, module_crashpad_info_[module_index].version); |
5009 | const auto& list_annots = |
5010 | module_crashpad_info_list_annotations_[module_index]; |
5011 | for (uint32_t annotation_index = 0; annotation_index < list_annots.size(); |
5012 | ++annotation_index) { |
5013 | printf(" module_list[%d].list_annotations[%d] = %s\n" , module_index, |
5014 | annotation_index, list_annots[annotation_index].c_str()); |
5015 | } |
5016 | const auto& simple_annots = |
5017 | module_crashpad_info_simple_annotations_[module_index]; |
5018 | for (const auto& annot : simple_annots) { |
5019 | printf(" module_list[%d].simple_annotations[\"%s\"] = %s\n" , |
5020 | module_index, annot.first.c_str(), annot.second.c_str()); |
5021 | } |
5022 | } |
5023 | |
5024 | printf("\n" ); |
5025 | } |
5026 | |
5027 | |
5028 | // |
5029 | // Minidump |
5030 | // |
5031 | |
5032 | |
5033 | uint32_t Minidump::max_streams_ = 128; |
5034 | unsigned int Minidump::max_string_length_ = 1024; |
5035 | |
5036 | |
5037 | Minidump::Minidump(const string& path, bool hexdump, unsigned int hexdump_width) |
5038 | : header_(), |
5039 | directory_(NULL), |
5040 | stream_map_(new MinidumpStreamMap()), |
5041 | path_(path), |
5042 | stream_(NULL), |
5043 | swap_(false), |
5044 | is_big_endian_(false), |
5045 | valid_(false), |
5046 | hexdump_(hexdump), |
5047 | hexdump_width_(hexdump_width) { |
5048 | } |
5049 | |
5050 | Minidump::Minidump(istream& stream) |
5051 | : header_(), |
5052 | directory_(NULL), |
5053 | stream_map_(new MinidumpStreamMap()), |
5054 | path_(), |
5055 | stream_(&stream), |
5056 | swap_(false), |
5057 | is_big_endian_(false), |
5058 | valid_(false), |
5059 | hexdump_(false), |
5060 | hexdump_width_(0) { |
5061 | } |
5062 | |
5063 | Minidump::~Minidump() { |
5064 | if (stream_) { |
5065 | BPLOG(INFO) << "Minidump closing minidump" ; |
5066 | } |
5067 | if (!path_.empty()) { |
5068 | delete stream_; |
5069 | } |
5070 | delete directory_; |
5071 | delete stream_map_; |
5072 | } |
5073 | |
5074 | |
5075 | bool Minidump::Open() { |
5076 | if (stream_ != NULL) { |
5077 | BPLOG(INFO) << "Minidump reopening minidump " << path_; |
5078 | |
5079 | // The file is already open. Seek to the beginning, which is the position |
5080 | // the file would be at if it were opened anew. |
5081 | return SeekSet(0); |
5082 | } |
5083 | |
5084 | stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary); |
5085 | if (!stream_ || !stream_->good()) { |
5086 | string error_string; |
5087 | int error_code = ErrnoString(&error_string); |
5088 | BPLOG(ERROR) << "Minidump could not open minidump " << path_ << |
5089 | ", error " << error_code << ": " << error_string; |
5090 | return false; |
5091 | } |
5092 | |
5093 | BPLOG(INFO) << "Minidump opened minidump " << path_; |
5094 | return true; |
5095 | } |
5096 | |
5097 | bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags) { |
5098 | // Initialize output parameters |
5099 | *context_cpu_flags = 0; |
5100 | |
5101 | // Save the current stream position |
5102 | off_t saved_position = Tell(); |
5103 | if (saved_position == -1) { |
5104 | // Failed to save the current stream position. |
5105 | // Returns true because the current position of the stream is preserved. |
5106 | return true; |
5107 | } |
5108 | |
5109 | const MDRawSystemInfo* system_info = |
5110 | GetSystemInfo() ? GetSystemInfo()->system_info() : NULL; |
5111 | |
5112 | if (system_info != NULL) { |
5113 | switch (system_info->processor_architecture) { |
5114 | case MD_CPU_ARCHITECTURE_X86: |
5115 | *context_cpu_flags = MD_CONTEXT_X86; |
5116 | break; |
5117 | case MD_CPU_ARCHITECTURE_MIPS: |
5118 | *context_cpu_flags = MD_CONTEXT_MIPS; |
5119 | break; |
5120 | case MD_CPU_ARCHITECTURE_MIPS64: |
5121 | *context_cpu_flags = MD_CONTEXT_MIPS64; |
5122 | break; |
5123 | case MD_CPU_ARCHITECTURE_ALPHA: |
5124 | *context_cpu_flags = MD_CONTEXT_ALPHA; |
5125 | break; |
5126 | case MD_CPU_ARCHITECTURE_PPC: |
5127 | *context_cpu_flags = MD_CONTEXT_PPC; |
5128 | break; |
5129 | case MD_CPU_ARCHITECTURE_PPC64: |
5130 | *context_cpu_flags = MD_CONTEXT_PPC64; |
5131 | break; |
5132 | case MD_CPU_ARCHITECTURE_SHX: |
5133 | *context_cpu_flags = MD_CONTEXT_SHX; |
5134 | break; |
5135 | case MD_CPU_ARCHITECTURE_ARM: |
5136 | *context_cpu_flags = MD_CONTEXT_ARM; |
5137 | break; |
5138 | case MD_CPU_ARCHITECTURE_ARM64: |
5139 | *context_cpu_flags = MD_CONTEXT_ARM64; |
5140 | break; |
5141 | case MD_CPU_ARCHITECTURE_ARM64_OLD: |
5142 | *context_cpu_flags = MD_CONTEXT_ARM64_OLD; |
5143 | break; |
5144 | case MD_CPU_ARCHITECTURE_IA64: |
5145 | *context_cpu_flags = MD_CONTEXT_IA64; |
5146 | break; |
5147 | case MD_CPU_ARCHITECTURE_ALPHA64: |
5148 | *context_cpu_flags = 0; |
5149 | break; |
5150 | case MD_CPU_ARCHITECTURE_MSIL: |
5151 | *context_cpu_flags = 0; |
5152 | break; |
5153 | case MD_CPU_ARCHITECTURE_AMD64: |
5154 | *context_cpu_flags = MD_CONTEXT_AMD64; |
5155 | break; |
5156 | case MD_CPU_ARCHITECTURE_X86_WIN64: |
5157 | *context_cpu_flags = 0; |
5158 | break; |
5159 | case MD_CPU_ARCHITECTURE_SPARC: |
5160 | *context_cpu_flags = MD_CONTEXT_SPARC; |
5161 | break; |
5162 | case MD_CPU_ARCHITECTURE_UNKNOWN: |
5163 | *context_cpu_flags = 0; |
5164 | break; |
5165 | default: |
5166 | *context_cpu_flags = 0; |
5167 | break; |
5168 | } |
5169 | } |
5170 | |
5171 | // Restore position and return |
5172 | return SeekSet(saved_position); |
5173 | } |
5174 | |
5175 | |
5176 | bool Minidump::Read() { |
5177 | // Invalidate cached data. |
5178 | delete directory_; |
5179 | directory_ = NULL; |
5180 | stream_map_->clear(); |
5181 | |
5182 | valid_ = false; |
5183 | |
5184 | if (!Open()) { |
5185 | BPLOG(ERROR) << "Minidump cannot open minidump" ; |
5186 | return false; |
5187 | } |
5188 | |
5189 | if (!ReadBytes(&header_, sizeof(MDRawHeader))) { |
5190 | BPLOG(ERROR) << "Minidump cannot read header" ; |
5191 | return false; |
5192 | } |
5193 | |
5194 | if (header_.signature != MD_HEADER_SIGNATURE) { |
5195 | // The file may be byte-swapped. Under the present architecture, these |
5196 | // classes don't know or need to know what CPU (or endianness) the |
5197 | // minidump was produced on in order to parse it. Use the signature as |
5198 | // a byte order marker. |
5199 | uint32_t signature_swapped = header_.signature; |
5200 | Swap(&signature_swapped); |
5201 | if (signature_swapped != MD_HEADER_SIGNATURE) { |
5202 | // This isn't a minidump or a byte-swapped minidump. |
5203 | BPLOG(ERROR) << "Minidump header signature mismatch: (" << |
5204 | HexString(header_.signature) << ", " << |
5205 | HexString(signature_swapped) << ") != " << |
5206 | HexString(MD_HEADER_SIGNATURE); |
5207 | return false; |
5208 | } |
5209 | swap_ = true; |
5210 | } else { |
5211 | // The file is not byte-swapped. Set swap_ false (it may have been true |
5212 | // if the object is being reused?) |
5213 | swap_ = false; |
5214 | } |
5215 | |
5216 | #if defined(__BIG_ENDIAN__) || \ |
5217 | (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) |
5218 | is_big_endian_ = !swap_; |
5219 | #else |
5220 | is_big_endian_ = swap_; |
5221 | #endif |
5222 | |
5223 | BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not " ) << |
5224 | "byte-swapping minidump" ; |
5225 | |
5226 | if (swap_) { |
5227 | Swap(&header_.signature); |
5228 | Swap(&header_.version); |
5229 | Swap(&header_.stream_count); |
5230 | Swap(&header_.stream_directory_rva); |
5231 | Swap(&header_.checksum); |
5232 | Swap(&header_.time_date_stamp); |
5233 | Swap(&header_.flags); |
5234 | } |
5235 | |
5236 | // Version check. The high 16 bits of header_.version contain something |
5237 | // else "implementation specific." |
5238 | if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) { |
5239 | BPLOG(ERROR) << "Minidump version mismatch: " << |
5240 | HexString(header_.version & 0x0000ffff) << " != " << |
5241 | HexString(MD_HEADER_VERSION); |
5242 | return false; |
5243 | } |
5244 | |
5245 | if (!SeekSet(header_.stream_directory_rva)) { |
5246 | BPLOG(ERROR) << "Minidump cannot seek to stream directory" ; |
5247 | return false; |
5248 | } |
5249 | |
5250 | if (header_.stream_count > max_streams_) { |
5251 | BPLOG(ERROR) << "Minidump stream count " << header_.stream_count << |
5252 | " exceeds maximum " << max_streams_; |
5253 | return false; |
5254 | } |
5255 | |
5256 | if (header_.stream_count != 0) { |
5257 | scoped_ptr<MinidumpDirectoryEntries> directory( |
5258 | new MinidumpDirectoryEntries(header_.stream_count)); |
5259 | |
5260 | // Read the entire array in one fell swoop, instead of reading one entry |
5261 | // at a time in the loop. |
5262 | if (!ReadBytes(&(*directory)[0], |
5263 | sizeof(MDRawDirectory) * header_.stream_count)) { |
5264 | BPLOG(ERROR) << "Minidump cannot read stream directory" ; |
5265 | return false; |
5266 | } |
5267 | |
5268 | for (unsigned int stream_index = 0; |
5269 | stream_index < header_.stream_count; |
5270 | ++stream_index) { |
5271 | MDRawDirectory* directory_entry = &(*directory)[stream_index]; |
5272 | |
5273 | if (swap_) { |
5274 | Swap(&directory_entry->stream_type); |
5275 | Swap(&directory_entry->location); |
5276 | } |
5277 | |
5278 | // Initialize the stream_map_ map, which speeds locating a stream by |
5279 | // type. |
5280 | unsigned int stream_type = directory_entry->stream_type; |
5281 | switch (stream_type) { |
5282 | case MD_THREAD_LIST_STREAM: |
5283 | case MD_MODULE_LIST_STREAM: |
5284 | case MD_MEMORY_LIST_STREAM: |
5285 | case MD_EXCEPTION_STREAM: |
5286 | case MD_SYSTEM_INFO_STREAM: |
5287 | case MD_MISC_INFO_STREAM: |
5288 | case MD_BREAKPAD_INFO_STREAM: |
5289 | case MD_CRASHPAD_INFO_STREAM: { |
5290 | if (stream_map_->find(stream_type) != stream_map_->end()) { |
5291 | // Another stream with this type was already found. A minidump |
5292 | // file should contain at most one of each of these stream types. |
5293 | BPLOG(ERROR) << "Minidump found multiple streams of type " << |
5294 | stream_type << ", but can only deal with one" ; |
5295 | return false; |
5296 | } |
5297 | BP_FALLTHROUGH; |
5298 | } |
5299 | |
5300 | default: { |
5301 | // Overwrites for stream types other than those above, but it's |
5302 | // expected to be the user's burden in that case. |
5303 | (*stream_map_)[stream_type].stream_index = stream_index; |
5304 | } |
5305 | } |
5306 | } |
5307 | |
5308 | directory_ = directory.release(); |
5309 | } |
5310 | |
5311 | valid_ = true; |
5312 | return true; |
5313 | } |
5314 | |
5315 | |
5316 | MinidumpThreadList* Minidump::GetThreadList() { |
5317 | MinidumpThreadList* thread_list; |
5318 | return GetStream(&thread_list); |
5319 | } |
5320 | |
5321 | |
5322 | MinidumpModuleList* Minidump::GetModuleList() { |
5323 | MinidumpModuleList* module_list; |
5324 | return GetStream(&module_list); |
5325 | } |
5326 | |
5327 | |
5328 | MinidumpMemoryList* Minidump::GetMemoryList() { |
5329 | MinidumpMemoryList* memory_list; |
5330 | return GetStream(&memory_list); |
5331 | } |
5332 | |
5333 | |
5334 | MinidumpException* Minidump::GetException() { |
5335 | MinidumpException* exception; |
5336 | return GetStream(&exception); |
5337 | } |
5338 | |
5339 | MinidumpAssertion* Minidump::GetAssertion() { |
5340 | MinidumpAssertion* assertion; |
5341 | return GetStream(&assertion); |
5342 | } |
5343 | |
5344 | |
5345 | MinidumpSystemInfo* Minidump::GetSystemInfo() { |
5346 | MinidumpSystemInfo* system_info; |
5347 | return GetStream(&system_info); |
5348 | } |
5349 | |
5350 | |
5351 | MinidumpUnloadedModuleList* Minidump::GetUnloadedModuleList() { |
5352 | MinidumpUnloadedModuleList* unloaded_module_list; |
5353 | return GetStream(&unloaded_module_list); |
5354 | } |
5355 | |
5356 | |
5357 | MinidumpMiscInfo* Minidump::GetMiscInfo() { |
5358 | MinidumpMiscInfo* misc_info; |
5359 | return GetStream(&misc_info); |
5360 | } |
5361 | |
5362 | |
5363 | MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() { |
5364 | MinidumpBreakpadInfo* breakpad_info; |
5365 | return GetStream(&breakpad_info); |
5366 | } |
5367 | |
5368 | MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() { |
5369 | MinidumpMemoryInfoList* memory_info_list; |
5370 | return GetStream(&memory_info_list); |
5371 | } |
5372 | |
5373 | MinidumpLinuxMapsList* Minidump::GetLinuxMapsList() { |
5374 | MinidumpLinuxMapsList* linux_maps_list; |
5375 | return GetStream(&linux_maps_list); |
5376 | } |
5377 | |
5378 | bool Minidump::IsAndroid() { |
5379 | MDOSPlatform platform; |
5380 | return GetPlatform(&platform) && platform == MD_OS_ANDROID; |
5381 | } |
5382 | |
5383 | bool Minidump::GetPlatform(MDOSPlatform* platform) { |
5384 | // Save the current stream position |
5385 | off_t saved_position = Tell(); |
5386 | if (saved_position == -1) { |
5387 | return false; |
5388 | } |
5389 | const MDRawSystemInfo* system_info = |
5390 | GetSystemInfo() ? GetSystemInfo()->system_info() : NULL; |
5391 | |
5392 | // Restore position and return |
5393 | if (!SeekSet(saved_position)) { |
5394 | BPLOG(ERROR) << "Couldn't seek back to saved position" ; |
5395 | return false; |
5396 | } |
5397 | |
5398 | if (!system_info) { |
5399 | return false; |
5400 | } |
5401 | *platform = static_cast<MDOSPlatform>(system_info->platform_id); |
5402 | return true; |
5403 | } |
5404 | |
5405 | MinidumpCrashpadInfo* Minidump::GetCrashpadInfo() { |
5406 | MinidumpCrashpadInfo* crashpad_info; |
5407 | return GetStream(&crashpad_info); |
5408 | } |
5409 | |
5410 | static const char* get_stream_name(uint32_t stream_type) { |
5411 | switch (stream_type) { |
5412 | case MD_UNUSED_STREAM: |
5413 | return "MD_UNUSED_STREAM" ; |
5414 | case MD_RESERVED_STREAM_0: |
5415 | return "MD_RESERVED_STREAM_0" ; |
5416 | case MD_RESERVED_STREAM_1: |
5417 | return "MD_RESERVED_STREAM_1" ; |
5418 | case MD_THREAD_LIST_STREAM: |
5419 | return "MD_THREAD_LIST_STREAM" ; |
5420 | case MD_MODULE_LIST_STREAM: |
5421 | return "MD_MODULE_LIST_STREAM" ; |
5422 | case MD_MEMORY_LIST_STREAM: |
5423 | return "MD_MEMORY_LIST_STREAM" ; |
5424 | case MD_EXCEPTION_STREAM: |
5425 | return "MD_EXCEPTION_STREAM" ; |
5426 | case MD_SYSTEM_INFO_STREAM: |
5427 | return "MD_SYSTEM_INFO_STREAM" ; |
5428 | case MD_THREAD_EX_LIST_STREAM: |
5429 | return "MD_THREAD_EX_LIST_STREAM" ; |
5430 | case MD_MEMORY_64_LIST_STREAM: |
5431 | return "MD_MEMORY_64_LIST_STREAM" ; |
5432 | case MD_COMMENT_STREAM_A: |
5433 | return "MD_COMMENT_STREAM_A" ; |
5434 | case MD_COMMENT_STREAM_W: |
5435 | return "MD_COMMENT_STREAM_W" ; |
5436 | case MD_HANDLE_DATA_STREAM: |
5437 | return "MD_HANDLE_DATA_STREAM" ; |
5438 | case MD_FUNCTION_TABLE_STREAM: |
5439 | return "MD_FUNCTION_TABLE_STREAM" ; |
5440 | case MD_UNLOADED_MODULE_LIST_STREAM: |
5441 | return "MD_UNLOADED_MODULE_LIST_STREAM" ; |
5442 | case MD_MISC_INFO_STREAM: |
5443 | return "MD_MISC_INFO_STREAM" ; |
5444 | case MD_MEMORY_INFO_LIST_STREAM: |
5445 | return "MD_MEMORY_INFO_LIST_STREAM" ; |
5446 | case MD_THREAD_INFO_LIST_STREAM: |
5447 | return "MD_THREAD_INFO_LIST_STREAM" ; |
5448 | case MD_HANDLE_OPERATION_LIST_STREAM: |
5449 | return "MD_HANDLE_OPERATION_LIST_STREAM" ; |
5450 | case MD_TOKEN_STREAM: |
5451 | return "MD_TOKEN_STREAM" ; |
5452 | case MD_JAVASCRIPT_DATA_STREAM: |
5453 | return "MD_JAVASCRIPT_DATA_STREAM" ; |
5454 | case MD_SYSTEM_MEMORY_INFO_STREAM: |
5455 | return "MD_SYSTEM_MEMORY_INFO_STREAM" ; |
5456 | case MD_PROCESS_VM_COUNTERS_STREAM: |
5457 | return "MD_PROCESS_VM_COUNTERS_STREAM" ; |
5458 | case MD_LAST_RESERVED_STREAM: |
5459 | return "MD_LAST_RESERVED_STREAM" ; |
5460 | case MD_BREAKPAD_INFO_STREAM: |
5461 | return "MD_BREAKPAD_INFO_STREAM" ; |
5462 | case MD_ASSERTION_INFO_STREAM: |
5463 | return "MD_ASSERTION_INFO_STREAM" ; |
5464 | case MD_LINUX_CPU_INFO: |
5465 | return "MD_LINUX_CPU_INFO" ; |
5466 | case MD_LINUX_PROC_STATUS: |
5467 | return "MD_LINUX_PROC_STATUS" ; |
5468 | case MD_LINUX_LSB_RELEASE: |
5469 | return "MD_LINUX_LSB_RELEASE" ; |
5470 | case MD_LINUX_CMD_LINE: |
5471 | return "MD_LINUX_CMD_LINE" ; |
5472 | case MD_LINUX_ENVIRON: |
5473 | return "MD_LINUX_ENVIRON" ; |
5474 | case MD_LINUX_AUXV: |
5475 | return "MD_LINUX_AUXV" ; |
5476 | case MD_LINUX_MAPS: |
5477 | return "MD_LINUX_MAPS" ; |
5478 | case MD_LINUX_DSO_DEBUG: |
5479 | return "MD_LINUX_DSO_DEBUG" ; |
5480 | case MD_CRASHPAD_INFO_STREAM: |
5481 | return "MD_CRASHPAD_INFO_STREAM" ; |
5482 | default: |
5483 | return "unknown" ; |
5484 | } |
5485 | } |
5486 | |
5487 | void Minidump::Print() { |
5488 | if (!valid_) { |
5489 | BPLOG(ERROR) << "Minidump cannot print invalid data" ; |
5490 | return; |
5491 | } |
5492 | |
5493 | printf("MDRawHeader\n" ); |
5494 | printf(" signature = 0x%x\n" , header_.signature); |
5495 | printf(" version = 0x%x\n" , header_.version); |
5496 | printf(" stream_count = %d\n" , header_.stream_count); |
5497 | printf(" stream_directory_rva = 0x%x\n" , header_.stream_directory_rva); |
5498 | printf(" checksum = 0x%x\n" , header_.checksum); |
5499 | printf(" time_date_stamp = 0x%x %s\n" , |
5500 | header_.time_date_stamp, |
5501 | TimeTToUTCString(header_.time_date_stamp).c_str()); |
5502 | printf(" flags = 0x%" PRIx64 "\n" , header_.flags); |
5503 | printf("\n" ); |
5504 | |
5505 | for (unsigned int stream_index = 0; |
5506 | stream_index < header_.stream_count; |
5507 | ++stream_index) { |
5508 | MDRawDirectory* directory_entry = &(*directory_)[stream_index]; |
5509 | |
5510 | printf("mDirectory[%d]\n" , stream_index); |
5511 | printf("MDRawDirectory\n" ); |
5512 | printf(" stream_type = 0x%x (%s)\n" , directory_entry->stream_type, |
5513 | get_stream_name(directory_entry->stream_type)); |
5514 | printf(" location.data_size = %d\n" , |
5515 | directory_entry->location.data_size); |
5516 | printf(" location.rva = 0x%x\n" , directory_entry->location.rva); |
5517 | printf("\n" ); |
5518 | } |
5519 | |
5520 | printf("Streams:\n" ); |
5521 | for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin(); |
5522 | iterator != stream_map_->end(); |
5523 | ++iterator) { |
5524 | uint32_t stream_type = iterator->first; |
5525 | const MinidumpStreamInfo& info = iterator->second; |
5526 | printf(" stream type 0x%x (%s) at index %d\n" , stream_type, |
5527 | get_stream_name(stream_type), |
5528 | info.stream_index); |
5529 | } |
5530 | printf("\n" ); |
5531 | } |
5532 | |
5533 | |
5534 | const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index) |
5535 | const { |
5536 | if (!valid_) { |
5537 | BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex" ; |
5538 | return NULL; |
5539 | } |
5540 | |
5541 | if (index >= header_.stream_count) { |
5542 | BPLOG(ERROR) << "Minidump stream directory index out of range: " << |
5543 | index << "/" << header_.stream_count; |
5544 | return NULL; |
5545 | } |
5546 | |
5547 | return &(*directory_)[index]; |
5548 | } |
5549 | |
5550 | |
5551 | bool Minidump::ReadBytes(void* bytes, size_t count) { |
5552 | // Can't check valid_ because Read needs to call this method before |
5553 | // validity can be determined. |
5554 | if (!stream_) { |
5555 | return false; |
5556 | } |
5557 | stream_->read(static_cast<char*>(bytes), count); |
5558 | std::streamsize bytes_read = stream_->gcount(); |
5559 | if (bytes_read == -1) { |
5560 | string error_string; |
5561 | int error_code = ErrnoString(&error_string); |
5562 | BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string; |
5563 | return false; |
5564 | } |
5565 | |
5566 | // Convert to size_t and check for data loss |
5567 | size_t bytes_read_converted = static_cast<size_t>(bytes_read); |
5568 | if (static_cast<std::streamsize>(bytes_read_converted) != bytes_read) { |
5569 | BPLOG(ERROR) << "ReadBytes: conversion data loss detected when converting " |
5570 | << bytes_read << " to " << bytes_read_converted; |
5571 | return false; |
5572 | } |
5573 | |
5574 | if (bytes_read_converted != count) { |
5575 | BPLOG(ERROR) << "ReadBytes: read " << bytes_read_converted << "/" << count; |
5576 | return false; |
5577 | } |
5578 | |
5579 | return true; |
5580 | } |
5581 | |
5582 | |
5583 | bool Minidump::SeekSet(off_t offset) { |
5584 | // Can't check valid_ because Read needs to call this method before |
5585 | // validity can be determined. |
5586 | if (!stream_) { |
5587 | return false; |
5588 | } |
5589 | stream_->seekg(offset, std::ios_base::beg); |
5590 | if (!stream_->good()) { |
5591 | string error_string; |
5592 | int error_code = ErrnoString(&error_string); |
5593 | BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; |
5594 | return false; |
5595 | } |
5596 | return true; |
5597 | } |
5598 | |
5599 | off_t Minidump::Tell() { |
5600 | if (!valid_ || !stream_) { |
5601 | return (off_t)-1; |
5602 | } |
5603 | |
5604 | // Check for conversion data loss |
5605 | std::streamoff std_streamoff = stream_->tellg(); |
5606 | off_t rv = static_cast<off_t>(std_streamoff); |
5607 | if (static_cast<std::streamoff>(rv) == std_streamoff) { |
5608 | return rv; |
5609 | } else { |
5610 | BPLOG(ERROR) << "Data loss detected" ; |
5611 | return (off_t)-1; |
5612 | } |
5613 | } |
5614 | |
5615 | |
5616 | string* Minidump::ReadString(off_t offset) { |
5617 | if (!valid_) { |
5618 | BPLOG(ERROR) << "Invalid Minidump for ReadString" ; |
5619 | return NULL; |
5620 | } |
5621 | if (!SeekSet(offset)) { |
5622 | BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset; |
5623 | return NULL; |
5624 | } |
5625 | |
5626 | uint32_t bytes; |
5627 | if (!ReadBytes(&bytes, sizeof(bytes))) { |
5628 | BPLOG(ERROR) << "ReadString could not read string size at offset " << |
5629 | offset; |
5630 | return NULL; |
5631 | } |
5632 | if (swap_) |
5633 | Swap(&bytes); |
5634 | |
5635 | if (bytes % 2 != 0) { |
5636 | BPLOG(ERROR) << "ReadString found odd-sized " << bytes << |
5637 | "-byte string at offset " << offset; |
5638 | return NULL; |
5639 | } |
5640 | unsigned int utf16_words = bytes / 2; |
5641 | |
5642 | if (utf16_words > max_string_length_) { |
5643 | BPLOG(ERROR) << "ReadString string length " << utf16_words << |
5644 | " exceeds maximum " << max_string_length_ << |
5645 | " at offset " << offset; |
5646 | return NULL; |
5647 | } |
5648 | |
5649 | vector<uint16_t> string_utf16(utf16_words); |
5650 | |
5651 | if (utf16_words) { |
5652 | if (!ReadBytes(&string_utf16[0], bytes)) { |
5653 | BPLOG(ERROR) << "ReadString could not read " << bytes << |
5654 | "-byte string at offset " << offset; |
5655 | return NULL; |
5656 | } |
5657 | } |
5658 | |
5659 | return UTF16ToUTF8(string_utf16, swap_); |
5660 | } |
5661 | |
5662 | |
5663 | bool Minidump::ReadUTF8String(off_t offset, string* string_utf8) { |
5664 | if (!valid_) { |
5665 | BPLOG(ERROR) << "Invalid Minidump for ReadString" ; |
5666 | return false; |
5667 | } |
5668 | if (!SeekSet(offset)) { |
5669 | BPLOG(ERROR) << "ReadUTF8String could not seek to string at offset " |
5670 | << offset; |
5671 | return false; |
5672 | } |
5673 | |
5674 | uint32_t bytes; |
5675 | if (!ReadBytes(&bytes, sizeof(bytes))) { |
5676 | BPLOG(ERROR) << "ReadUTF8String could not read string size at offset " << |
5677 | offset; |
5678 | return false; |
5679 | } |
5680 | |
5681 | if (swap_) { |
5682 | Swap(&bytes); |
5683 | } |
5684 | |
5685 | if (bytes > max_string_length_) { |
5686 | BPLOG(ERROR) << "ReadUTF8String string length " << bytes << |
5687 | " exceeds maximum " << max_string_length_ << |
5688 | " at offset " << offset; |
5689 | return false; |
5690 | } |
5691 | |
5692 | string_utf8->resize(bytes); |
5693 | |
5694 | if (!ReadBytes(&(*string_utf8)[0], bytes)) { |
5695 | BPLOG(ERROR) << "ReadUTF8String could not read " << bytes << |
5696 | "-byte string at offset " << offset; |
5697 | return false; |
5698 | } |
5699 | |
5700 | return true; |
5701 | } |
5702 | |
5703 | |
5704 | bool Minidump::ReadStringList( |
5705 | off_t offset, |
5706 | std::vector<std::string>* string_list) { |
5707 | string_list->clear(); |
5708 | |
5709 | if (!SeekSet(offset)) { |
5710 | BPLOG(ERROR) << "Minidump cannot seek to string_list" ; |
5711 | return false; |
5712 | } |
5713 | |
5714 | uint32_t count; |
5715 | if (!ReadBytes(&count, sizeof(count))) { |
5716 | BPLOG(ERROR) << "Minidump cannot read string_list count" ; |
5717 | return false; |
5718 | } |
5719 | |
5720 | if (swap_) { |
5721 | Swap(&count); |
5722 | } |
5723 | |
5724 | scoped_array<MDRVA> rvas(new MDRVA[count]); |
5725 | |
5726 | // Read the entire array in one fell swoop, instead of reading one entry |
5727 | // at a time in the loop. |
5728 | if (!ReadBytes(&rvas[0], sizeof(MDRVA) * count)) { |
5729 | BPLOG(ERROR) << "Minidump could not read string_list" ; |
5730 | return false; |
5731 | } |
5732 | |
5733 | for (uint32_t index = 0; index < count; ++index) { |
5734 | if (swap()) { |
5735 | Swap(&rvas[index]); |
5736 | } |
5737 | |
5738 | string entry; |
5739 | if (!ReadUTF8String(rvas[index], &entry)) { |
5740 | BPLOG(ERROR) << "Minidump could not read string_list entry" ; |
5741 | return false; |
5742 | } |
5743 | |
5744 | string_list->push_back(entry); |
5745 | } |
5746 | |
5747 | return true; |
5748 | } |
5749 | |
5750 | |
5751 | bool Minidump::ReadSimpleStringDictionary( |
5752 | off_t offset, |
5753 | std::map<std::string, std::string>* simple_string_dictionary) { |
5754 | simple_string_dictionary->clear(); |
5755 | |
5756 | if (!SeekSet(offset)) { |
5757 | BPLOG(ERROR) << "Minidump cannot seek to simple_string_dictionary" ; |
5758 | return false; |
5759 | } |
5760 | |
5761 | uint32_t count; |
5762 | if (!ReadBytes(&count, sizeof(count))) { |
5763 | BPLOG(ERROR) |
5764 | << "Minidump cannot read simple_string_dictionary count" ; |
5765 | return false; |
5766 | } |
5767 | |
5768 | if (swap()) { |
5769 | Swap(&count); |
5770 | } |
5771 | |
5772 | scoped_array<MDRawSimpleStringDictionaryEntry> entries( |
5773 | new MDRawSimpleStringDictionaryEntry[count]); |
5774 | |
5775 | // Read the entire array in one fell swoop, instead of reading one entry |
5776 | // at a time in the loop. |
5777 | if (!ReadBytes( |
5778 | &entries[0], |
5779 | sizeof(MDRawSimpleStringDictionaryEntry) * count)) { |
5780 | BPLOG(ERROR) << "Minidump could not read simple_string_dictionary" ; |
5781 | return false; |
5782 | } |
5783 | |
5784 | for (uint32_t index = 0; index < count; ++index) { |
5785 | if (swap()) { |
5786 | Swap(&entries[index]); |
5787 | } |
5788 | |
5789 | string key; |
5790 | if (!ReadUTF8String(entries[index].key, &key)) { |
5791 | BPLOG(ERROR) << "Minidump could not read simple_string_dictionary key" ; |
5792 | return false; |
5793 | } |
5794 | |
5795 | string value; |
5796 | if (!ReadUTF8String(entries[index].value, &value)) { |
5797 | BPLOG(ERROR) << "Minidump could not read simple_string_dictionary value" ; |
5798 | return false; |
5799 | } |
5800 | |
5801 | if (simple_string_dictionary->find(key) != |
5802 | simple_string_dictionary->end()) { |
5803 | BPLOG(ERROR) |
5804 | << "Minidump: discarding duplicate simple_string_dictionary value " |
5805 | << value << " for key " << key; |
5806 | } else { |
5807 | simple_string_dictionary->insert(std::make_pair(key, value)); |
5808 | } |
5809 | } |
5810 | |
5811 | return true; |
5812 | } |
5813 | |
5814 | |
5815 | bool Minidump::SeekToStreamType(uint32_t stream_type, |
5816 | uint32_t* stream_length) { |
5817 | BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires " |
5818 | "|stream_length|" ; |
5819 | assert(stream_length); |
5820 | *stream_length = 0; |
5821 | |
5822 | if (!valid_) { |
5823 | BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType" ; |
5824 | return false; |
5825 | } |
5826 | |
5827 | MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type); |
5828 | if (iterator == stream_map_->end()) { |
5829 | // This stream type didn't exist in the directory. |
5830 | BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present" ; |
5831 | return false; |
5832 | } |
5833 | |
5834 | const MinidumpStreamInfo& info = iterator->second; |
5835 | if (info.stream_index >= header_.stream_count) { |
5836 | BPLOG(ERROR) << "SeekToStreamType: type " << stream_type << |
5837 | " out of range: " << |
5838 | info.stream_index << "/" << header_.stream_count; |
5839 | return false; |
5840 | } |
5841 | |
5842 | MDRawDirectory* directory_entry = &(*directory_)[info.stream_index]; |
5843 | if (!SeekSet(directory_entry->location.rva)) { |
5844 | BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " << |
5845 | stream_type; |
5846 | return false; |
5847 | } |
5848 | |
5849 | *stream_length = directory_entry->location.data_size; |
5850 | |
5851 | return true; |
5852 | } |
5853 | |
5854 | |
5855 | template<typename T> |
5856 | T* Minidump::GetStream(T** stream) { |
5857 | // stream is a garbage parameter that's present only to account for C++'s |
5858 | // inability to overload a method based solely on its return type. |
5859 | |
5860 | const uint32_t stream_type = T::kStreamType; |
5861 | |
5862 | BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type << |
5863 | " requires |stream|" ; |
5864 | assert(stream); |
5865 | *stream = NULL; |
5866 | |
5867 | if (!valid_) { |
5868 | BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type; |
5869 | return NULL; |
5870 | } |
5871 | |
5872 | MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type); |
5873 | if (iterator == stream_map_->end()) { |
5874 | // This stream type didn't exist in the directory. |
5875 | BPLOG(INFO) << "GetStream: type " << stream_type << " not present" ; |
5876 | return NULL; |
5877 | } |
5878 | |
5879 | // Get a pointer so that the stored stream field can be altered. |
5880 | MinidumpStreamInfo* info = &iterator->second; |
5881 | |
5882 | if (info->stream) { |
5883 | // This cast is safe because info.stream is only populated by this |
5884 | // method, and there is a direct correlation between T and stream_type. |
5885 | *stream = static_cast<T*>(info->stream); |
5886 | return *stream; |
5887 | } |
5888 | |
5889 | uint32_t stream_length; |
5890 | if (!SeekToStreamType(stream_type, &stream_length)) { |
5891 | BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type; |
5892 | return NULL; |
5893 | } |
5894 | |
5895 | scoped_ptr<T> new_stream(new T(this)); |
5896 | |
5897 | if (!new_stream->Read(stream_length)) { |
5898 | BPLOG(ERROR) << "GetStream could not read stream type " << stream_type; |
5899 | return NULL; |
5900 | } |
5901 | |
5902 | *stream = new_stream.release(); |
5903 | info->stream = *stream; |
5904 | return *stream; |
5905 | } |
5906 | |
5907 | } // namespace google_breakpad |
5908 | |