|
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 <stdio.h> |
|
42 #include <string.h> |
|
43 #include <time.h> |
|
44 |
|
45 #ifdef _WIN32 |
|
46 #include <io.h> |
|
47 #define PRIx64 "llx" |
|
48 #define PRIx32 "lx" |
|
49 #define snprintf _snprintf |
|
50 #else // _WIN32 |
|
51 #include <unistd.h> |
|
52 #define O_BINARY 0 |
|
53 #endif // _WIN32 |
|
54 |
|
55 #include <fstream> |
|
56 #include <iostream> |
|
57 #include <limits> |
|
58 #include <map> |
|
59 #include <vector> |
|
60 |
|
61 #include "processor/range_map-inl.h" |
|
62 |
|
63 #include "common/scoped_ptr.h" |
|
64 #include "processor/basic_code_module.h" |
|
65 #include "processor/basic_code_modules.h" |
|
66 #include "common/logging.h" |
|
67 |
|
68 |
|
69 |
|
70 namespace google_breakpad { |
|
71 |
|
72 |
|
73 using std::istream; |
|
74 using std::ifstream; |
|
75 using std::numeric_limits; |
|
76 using std::vector; |
|
77 |
|
78 |
|
79 // |
|
80 // Swapping routines |
|
81 // |
|
82 // Inlining these doesn't increase code size significantly, and it saves |
|
83 // a whole lot of unnecessary jumping back and forth. |
|
84 // |
|
85 |
|
86 |
|
87 // Swapping an 8-bit quantity is a no-op. This function is only provided |
|
88 // to account for certain templatized operations that require swapping for |
|
89 // wider types but handle uint8_t too |
|
90 // (MinidumpMemoryRegion::GetMemoryAtAddressInternal). |
|
91 static inline void Swap(uint8_t* value) { |
|
92 } |
|
93 |
|
94 |
|
95 // Optimization: don't need to AND the furthest right shift, because we're |
|
96 // shifting an unsigned quantity. The standard requires zero-filling in this |
|
97 // case. If the quantities were signed, a bitmask whould be needed for this |
|
98 // right shift to avoid an arithmetic shift (which retains the sign bit). |
|
99 // The furthest left shift never needs to be ANDed bitmask. |
|
100 |
|
101 |
|
102 static inline void Swap(uint16_t* value) { |
|
103 *value = (*value >> 8) | |
|
104 (*value << 8); |
|
105 } |
|
106 |
|
107 |
|
108 static inline void Swap(uint32_t* value) { |
|
109 *value = (*value >> 24) | |
|
110 ((*value >> 8) & 0x0000ff00) | |
|
111 ((*value << 8) & 0x00ff0000) | |
|
112 (*value << 24); |
|
113 } |
|
114 |
|
115 |
|
116 static inline void Swap(uint64_t* value) { |
|
117 uint32_t* value32 = reinterpret_cast<uint32_t*>(value); |
|
118 Swap(&value32[0]); |
|
119 Swap(&value32[1]); |
|
120 uint32_t temp = value32[0]; |
|
121 value32[0] = value32[1]; |
|
122 value32[1] = temp; |
|
123 } |
|
124 |
|
125 |
|
126 // Given a pointer to a 128-bit int in the minidump data, set the "low" |
|
127 // and "high" fields appropriately. |
|
128 static void Normalize128(uint128_struct* value, bool is_big_endian) { |
|
129 // The struct format is [high, low], so if the format is big-endian, |
|
130 // the most significant bytes will already be in the high field. |
|
131 if (!is_big_endian) { |
|
132 uint64_t temp = value->low; |
|
133 value->low = value->high; |
|
134 value->high = temp; |
|
135 } |
|
136 } |
|
137 |
|
138 // This just swaps each int64 half of the 128-bit value. |
|
139 // The value should also be normalized by calling Normalize128(). |
|
140 static void Swap(uint128_struct* value) { |
|
141 Swap(&value->low); |
|
142 Swap(&value->high); |
|
143 } |
|
144 |
|
145 |
|
146 static inline void Swap(MDLocationDescriptor* location_descriptor) { |
|
147 Swap(&location_descriptor->data_size); |
|
148 Swap(&location_descriptor->rva); |
|
149 } |
|
150 |
|
151 |
|
152 static inline void Swap(MDMemoryDescriptor* memory_descriptor) { |
|
153 Swap(&memory_descriptor->start_of_memory_range); |
|
154 Swap(&memory_descriptor->memory); |
|
155 } |
|
156 |
|
157 |
|
158 static inline void Swap(MDGUID* guid) { |
|
159 Swap(&guid->data1); |
|
160 Swap(&guid->data2); |
|
161 Swap(&guid->data3); |
|
162 // Don't swap guid->data4[] because it contains 8-bit quantities. |
|
163 } |
|
164 |
|
165 |
|
166 // |
|
167 // Character conversion routines |
|
168 // |
|
169 |
|
170 |
|
171 // Standard wide-character conversion routines depend on the system's own |
|
172 // idea of what width a wide character should be: some use 16 bits, and |
|
173 // some use 32 bits. For the purposes of a minidump, wide strings are |
|
174 // always represented with 16-bit UTF-16 chracters. iconv isn't available |
|
175 // everywhere, and its interface varies where it is available. iconv also |
|
176 // deals purely with char* pointers, so in addition to considering the swap |
|
177 // parameter, a converter that uses iconv would also need to take the host |
|
178 // CPU's endianness into consideration. It doesn't seems worth the trouble |
|
179 // of making it a dependency when we don't care about anything but UTF-16. |
|
180 static string* UTF16ToUTF8(const vector<uint16_t>& in, |
|
181 bool swap) { |
|
182 scoped_ptr<string> out(new string()); |
|
183 |
|
184 // Set the string's initial capacity to the number of UTF-16 characters, |
|
185 // because the UTF-8 representation will always be at least this long. |
|
186 // If the UTF-8 representation is longer, the string will grow dynamically. |
|
187 out->reserve(in.size()); |
|
188 |
|
189 for (vector<uint16_t>::const_iterator iterator = in.begin(); |
|
190 iterator != in.end(); |
|
191 ++iterator) { |
|
192 // Get a 16-bit value from the input |
|
193 uint16_t in_word = *iterator; |
|
194 if (swap) |
|
195 Swap(&in_word); |
|
196 |
|
197 // Convert the input value (in_word) into a Unicode code point (unichar). |
|
198 uint32_t unichar; |
|
199 if (in_word >= 0xdc00 && in_word <= 0xdcff) { |
|
200 BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " << |
|
201 HexString(in_word) << " without high"; |
|
202 return NULL; |
|
203 } else if (in_word >= 0xd800 && in_word <= 0xdbff) { |
|
204 // High surrogate. |
|
205 unichar = (in_word - 0xd7c0) << 10; |
|
206 if (++iterator == in.end()) { |
|
207 BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << |
|
208 HexString(in_word) << " at end of string"; |
|
209 return NULL; |
|
210 } |
|
211 uint32_t high_word = in_word; |
|
212 in_word = *iterator; |
|
213 if (in_word < 0xdc00 || in_word > 0xdcff) { |
|
214 BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << |
|
215 HexString(high_word) << " without low " << |
|
216 HexString(in_word); |
|
217 return NULL; |
|
218 } |
|
219 unichar |= in_word & 0x03ff; |
|
220 } else { |
|
221 // The ordinary case, a single non-surrogate Unicode character encoded |
|
222 // as a single 16-bit value. |
|
223 unichar = in_word; |
|
224 } |
|
225 |
|
226 // Convert the Unicode code point (unichar) into its UTF-8 representation, |
|
227 // appending it to the out string. |
|
228 if (unichar < 0x80) { |
|
229 (*out) += unichar; |
|
230 } else if (unichar < 0x800) { |
|
231 (*out) += 0xc0 | (unichar >> 6); |
|
232 (*out) += 0x80 | (unichar & 0x3f); |
|
233 } else if (unichar < 0x10000) { |
|
234 (*out) += 0xe0 | (unichar >> 12); |
|
235 (*out) += 0x80 | ((unichar >> 6) & 0x3f); |
|
236 (*out) += 0x80 | (unichar & 0x3f); |
|
237 } else if (unichar < 0x200000) { |
|
238 (*out) += 0xf0 | (unichar >> 18); |
|
239 (*out) += 0x80 | ((unichar >> 12) & 0x3f); |
|
240 (*out) += 0x80 | ((unichar >> 6) & 0x3f); |
|
241 (*out) += 0x80 | (unichar & 0x3f); |
|
242 } else { |
|
243 BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " << |
|
244 HexString(unichar) << " in UTF-8"; |
|
245 return NULL; |
|
246 } |
|
247 } |
|
248 |
|
249 return out.release(); |
|
250 } |
|
251 |
|
252 // Return the smaller of the number of code units in the UTF-16 string, |
|
253 // not including the terminating null word, or maxlen. |
|
254 static size_t UTF16codeunits(const uint16_t *string, size_t maxlen) { |
|
255 size_t count = 0; |
|
256 while (count < maxlen && string[count] != 0) |
|
257 count++; |
|
258 return count; |
|
259 } |
|
260 |
|
261 |
|
262 // |
|
263 // MinidumpObject |
|
264 // |
|
265 |
|
266 |
|
267 MinidumpObject::MinidumpObject(Minidump* minidump) |
|
268 : minidump_(minidump), |
|
269 valid_(false) { |
|
270 } |
|
271 |
|
272 |
|
273 // |
|
274 // MinidumpStream |
|
275 // |
|
276 |
|
277 |
|
278 MinidumpStream::MinidumpStream(Minidump* minidump) |
|
279 : MinidumpObject(minidump) { |
|
280 } |
|
281 |
|
282 |
|
283 // |
|
284 // MinidumpContext |
|
285 // |
|
286 |
|
287 |
|
288 MinidumpContext::MinidumpContext(Minidump* minidump) |
|
289 : MinidumpStream(minidump), |
|
290 context_(), |
|
291 context_flags_(0) { |
|
292 } |
|
293 |
|
294 |
|
295 MinidumpContext::~MinidumpContext() { |
|
296 FreeContext(); |
|
297 } |
|
298 |
|
299 |
|
300 bool MinidumpContext::Read(uint32_t expected_size) { |
|
301 valid_ = false; |
|
302 |
|
303 FreeContext(); |
|
304 |
|
305 // First, figure out what type of CPU this context structure is for. |
|
306 // For some reason, the AMD64 Context doesn't have context_flags |
|
307 // at the beginning of the structure, so special case it here. |
|
308 if (expected_size == sizeof(MDRawContextAMD64)) { |
|
309 BPLOG(INFO) << "MinidumpContext: looks like AMD64 context"; |
|
310 |
|
311 scoped_ptr<MDRawContextAMD64> context_amd64(new MDRawContextAMD64()); |
|
312 if (!minidump_->ReadBytes(context_amd64.get(), |
|
313 sizeof(MDRawContextAMD64))) { |
|
314 BPLOG(ERROR) << "MinidumpContext could not read amd64 context"; |
|
315 return false; |
|
316 } |
|
317 |
|
318 if (minidump_->swap()) |
|
319 Swap(&context_amd64->context_flags); |
|
320 |
|
321 uint32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK; |
|
322 if (cpu_type == 0) { |
|
323 if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { |
|
324 context_amd64->context_flags |= cpu_type; |
|
325 } else { |
|
326 BPLOG(ERROR) << "Failed to preserve the current stream position"; |
|
327 return false; |
|
328 } |
|
329 } |
|
330 |
|
331 if (cpu_type != MD_CONTEXT_AMD64) { |
|
332 //TODO: fall through to switch below? |
|
333 // need a Tell method to be able to SeekSet back to beginning |
|
334 // http://code.google.com/p/google-breakpad/issues/detail?id=224 |
|
335 BPLOG(ERROR) << "MinidumpContext not actually amd64 context"; |
|
336 return false; |
|
337 } |
|
338 |
|
339 // Do this after reading the entire MDRawContext structure because |
|
340 // GetSystemInfo may seek minidump to a new position. |
|
341 if (!CheckAgainstSystemInfo(cpu_type)) { |
|
342 BPLOG(ERROR) << "MinidumpContext amd64 does not match system info"; |
|
343 return false; |
|
344 } |
|
345 |
|
346 // Normalize the 128-bit types in the dump. |
|
347 // Since this is AMD64, by definition, the values are little-endian. |
|
348 for (unsigned int vr_index = 0; |
|
349 vr_index < MD_CONTEXT_AMD64_VR_COUNT; |
|
350 ++vr_index) |
|
351 Normalize128(&context_amd64->vector_register[vr_index], false); |
|
352 |
|
353 if (minidump_->swap()) { |
|
354 Swap(&context_amd64->p1_home); |
|
355 Swap(&context_amd64->p2_home); |
|
356 Swap(&context_amd64->p3_home); |
|
357 Swap(&context_amd64->p4_home); |
|
358 Swap(&context_amd64->p5_home); |
|
359 Swap(&context_amd64->p6_home); |
|
360 // context_flags is already swapped |
|
361 Swap(&context_amd64->mx_csr); |
|
362 Swap(&context_amd64->cs); |
|
363 Swap(&context_amd64->ds); |
|
364 Swap(&context_amd64->es); |
|
365 Swap(&context_amd64->fs); |
|
366 Swap(&context_amd64->ss); |
|
367 Swap(&context_amd64->eflags); |
|
368 Swap(&context_amd64->dr0); |
|
369 Swap(&context_amd64->dr1); |
|
370 Swap(&context_amd64->dr2); |
|
371 Swap(&context_amd64->dr3); |
|
372 Swap(&context_amd64->dr6); |
|
373 Swap(&context_amd64->dr7); |
|
374 Swap(&context_amd64->rax); |
|
375 Swap(&context_amd64->rcx); |
|
376 Swap(&context_amd64->rdx); |
|
377 Swap(&context_amd64->rbx); |
|
378 Swap(&context_amd64->rsp); |
|
379 Swap(&context_amd64->rbp); |
|
380 Swap(&context_amd64->rsi); |
|
381 Swap(&context_amd64->rdi); |
|
382 Swap(&context_amd64->r8); |
|
383 Swap(&context_amd64->r9); |
|
384 Swap(&context_amd64->r10); |
|
385 Swap(&context_amd64->r11); |
|
386 Swap(&context_amd64->r12); |
|
387 Swap(&context_amd64->r13); |
|
388 Swap(&context_amd64->r14); |
|
389 Swap(&context_amd64->r15); |
|
390 Swap(&context_amd64->rip); |
|
391 //FIXME: I'm not sure what actually determines |
|
392 // which member of the union {flt_save, sse_registers} |
|
393 // is valid. We're not currently using either, |
|
394 // but it would be good to have them swapped properly. |
|
395 |
|
396 for (unsigned int vr_index = 0; |
|
397 vr_index < MD_CONTEXT_AMD64_VR_COUNT; |
|
398 ++vr_index) |
|
399 Swap(&context_amd64->vector_register[vr_index]); |
|
400 Swap(&context_amd64->vector_control); |
|
401 Swap(&context_amd64->debug_control); |
|
402 Swap(&context_amd64->last_branch_to_rip); |
|
403 Swap(&context_amd64->last_branch_from_rip); |
|
404 Swap(&context_amd64->last_exception_to_rip); |
|
405 Swap(&context_amd64->last_exception_from_rip); |
|
406 } |
|
407 |
|
408 context_flags_ = context_amd64->context_flags; |
|
409 |
|
410 context_.amd64 = context_amd64.release(); |
|
411 } |
|
412 else { |
|
413 uint32_t context_flags; |
|
414 if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { |
|
415 BPLOG(ERROR) << "MinidumpContext could not read context flags"; |
|
416 return false; |
|
417 } |
|
418 if (minidump_->swap()) |
|
419 Swap(&context_flags); |
|
420 |
|
421 uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; |
|
422 if (cpu_type == 0) { |
|
423 // Unfortunately the flag for MD_CONTEXT_ARM that was taken |
|
424 // from a Windows CE SDK header conflicts in practice with |
|
425 // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered, |
|
426 // but handle dumps with the legacy value gracefully here. |
|
427 if (context_flags & MD_CONTEXT_ARM_OLD) { |
|
428 context_flags |= MD_CONTEXT_ARM; |
|
429 context_flags &= ~MD_CONTEXT_ARM_OLD; |
|
430 cpu_type = MD_CONTEXT_ARM; |
|
431 } |
|
432 } |
|
433 |
|
434 if (cpu_type == 0) { |
|
435 if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) { |
|
436 context_flags |= cpu_type; |
|
437 } else { |
|
438 BPLOG(ERROR) << "Failed to preserve the current stream position"; |
|
439 return false; |
|
440 } |
|
441 } |
|
442 |
|
443 // Allocate the context structure for the correct CPU and fill it. The |
|
444 // casts are slightly unorthodox, but it seems better to do that than to |
|
445 // maintain a separate pointer for each type of CPU context structure |
|
446 // when only one of them will be used. |
|
447 switch (cpu_type) { |
|
448 case MD_CONTEXT_X86: { |
|
449 if (expected_size != sizeof(MDRawContextX86)) { |
|
450 BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << |
|
451 expected_size << " != " << sizeof(MDRawContextX86); |
|
452 return false; |
|
453 } |
|
454 |
|
455 scoped_ptr<MDRawContextX86> context_x86(new MDRawContextX86()); |
|
456 |
|
457 // Set the context_flags member, which has already been read, and |
|
458 // read the rest of the structure beginning with the first member |
|
459 // after context_flags. |
|
460 context_x86->context_flags = context_flags; |
|
461 |
|
462 size_t flags_size = sizeof(context_x86->context_flags); |
|
463 uint8_t* context_after_flags = |
|
464 reinterpret_cast<uint8_t*>(context_x86.get()) + flags_size; |
|
465 if (!minidump_->ReadBytes(context_after_flags, |
|
466 sizeof(MDRawContextX86) - flags_size)) { |
|
467 BPLOG(ERROR) << "MinidumpContext could not read x86 context"; |
|
468 return false; |
|
469 } |
|
470 |
|
471 // Do this after reading the entire MDRawContext structure because |
|
472 // GetSystemInfo may seek minidump to a new position. |
|
473 if (!CheckAgainstSystemInfo(cpu_type)) { |
|
474 BPLOG(ERROR) << "MinidumpContext x86 does not match system info"; |
|
475 return false; |
|
476 } |
|
477 |
|
478 if (minidump_->swap()) { |
|
479 // context_x86->context_flags was already swapped. |
|
480 Swap(&context_x86->dr0); |
|
481 Swap(&context_x86->dr1); |
|
482 Swap(&context_x86->dr2); |
|
483 Swap(&context_x86->dr3); |
|
484 Swap(&context_x86->dr6); |
|
485 Swap(&context_x86->dr7); |
|
486 Swap(&context_x86->float_save.control_word); |
|
487 Swap(&context_x86->float_save.status_word); |
|
488 Swap(&context_x86->float_save.tag_word); |
|
489 Swap(&context_x86->float_save.error_offset); |
|
490 Swap(&context_x86->float_save.error_selector); |
|
491 Swap(&context_x86->float_save.data_offset); |
|
492 Swap(&context_x86->float_save.data_selector); |
|
493 // context_x86->float_save.register_area[] contains 8-bit quantities |
|
494 // and does not need to be swapped. |
|
495 Swap(&context_x86->float_save.cr0_npx_state); |
|
496 Swap(&context_x86->gs); |
|
497 Swap(&context_x86->fs); |
|
498 Swap(&context_x86->es); |
|
499 Swap(&context_x86->ds); |
|
500 Swap(&context_x86->edi); |
|
501 Swap(&context_x86->esi); |
|
502 Swap(&context_x86->ebx); |
|
503 Swap(&context_x86->edx); |
|
504 Swap(&context_x86->ecx); |
|
505 Swap(&context_x86->eax); |
|
506 Swap(&context_x86->ebp); |
|
507 Swap(&context_x86->eip); |
|
508 Swap(&context_x86->cs); |
|
509 Swap(&context_x86->eflags); |
|
510 Swap(&context_x86->esp); |
|
511 Swap(&context_x86->ss); |
|
512 // context_x86->extended_registers[] contains 8-bit quantities and |
|
513 // does not need to be swapped. |
|
514 } |
|
515 |
|
516 context_.x86 = context_x86.release(); |
|
517 |
|
518 break; |
|
519 } |
|
520 |
|
521 case MD_CONTEXT_PPC: { |
|
522 if (expected_size != sizeof(MDRawContextPPC)) { |
|
523 BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << |
|
524 expected_size << " != " << sizeof(MDRawContextPPC); |
|
525 return false; |
|
526 } |
|
527 |
|
528 scoped_ptr<MDRawContextPPC> context_ppc(new MDRawContextPPC()); |
|
529 |
|
530 // Set the context_flags member, which has already been read, and |
|
531 // read the rest of the structure beginning with the first member |
|
532 // after context_flags. |
|
533 context_ppc->context_flags = context_flags; |
|
534 |
|
535 size_t flags_size = sizeof(context_ppc->context_flags); |
|
536 uint8_t* context_after_flags = |
|
537 reinterpret_cast<uint8_t*>(context_ppc.get()) + flags_size; |
|
538 if (!minidump_->ReadBytes(context_after_flags, |
|
539 sizeof(MDRawContextPPC) - flags_size)) { |
|
540 BPLOG(ERROR) << "MinidumpContext could not read ppc context"; |
|
541 return false; |
|
542 } |
|
543 |
|
544 // Do this after reading the entire MDRawContext structure because |
|
545 // GetSystemInfo may seek minidump to a new position. |
|
546 if (!CheckAgainstSystemInfo(cpu_type)) { |
|
547 BPLOG(ERROR) << "MinidumpContext ppc does not match system info"; |
|
548 return false; |
|
549 } |
|
550 |
|
551 // Normalize the 128-bit types in the dump. |
|
552 // Since this is PowerPC, by definition, the values are big-endian. |
|
553 for (unsigned int vr_index = 0; |
|
554 vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; |
|
555 ++vr_index) { |
|
556 Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); |
|
557 } |
|
558 |
|
559 if (minidump_->swap()) { |
|
560 // context_ppc->context_flags was already swapped. |
|
561 Swap(&context_ppc->srr0); |
|
562 Swap(&context_ppc->srr1); |
|
563 for (unsigned int gpr_index = 0; |
|
564 gpr_index < MD_CONTEXT_PPC_GPR_COUNT; |
|
565 ++gpr_index) { |
|
566 Swap(&context_ppc->gpr[gpr_index]); |
|
567 } |
|
568 Swap(&context_ppc->cr); |
|
569 Swap(&context_ppc->xer); |
|
570 Swap(&context_ppc->lr); |
|
571 Swap(&context_ppc->ctr); |
|
572 Swap(&context_ppc->mq); |
|
573 Swap(&context_ppc->vrsave); |
|
574 for (unsigned int fpr_index = 0; |
|
575 fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; |
|
576 ++fpr_index) { |
|
577 Swap(&context_ppc->float_save.fpregs[fpr_index]); |
|
578 } |
|
579 // Don't swap context_ppc->float_save.fpscr_pad because it is only |
|
580 // used for padding. |
|
581 Swap(&context_ppc->float_save.fpscr); |
|
582 for (unsigned int vr_index = 0; |
|
583 vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; |
|
584 ++vr_index) { |
|
585 Swap(&context_ppc->vector_save.save_vr[vr_index]); |
|
586 } |
|
587 Swap(&context_ppc->vector_save.save_vscr); |
|
588 // Don't swap the padding fields in vector_save. |
|
589 Swap(&context_ppc->vector_save.save_vrvalid); |
|
590 } |
|
591 |
|
592 context_.ppc = context_ppc.release(); |
|
593 |
|
594 break; |
|
595 } |
|
596 |
|
597 case MD_CONTEXT_SPARC: { |
|
598 if (expected_size != sizeof(MDRawContextSPARC)) { |
|
599 BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << |
|
600 expected_size << " != " << sizeof(MDRawContextSPARC); |
|
601 return false; |
|
602 } |
|
603 |
|
604 scoped_ptr<MDRawContextSPARC> context_sparc(new MDRawContextSPARC()); |
|
605 |
|
606 // Set the context_flags member, which has already been read, and |
|
607 // read the rest of the structure beginning with the first member |
|
608 // after context_flags. |
|
609 context_sparc->context_flags = context_flags; |
|
610 |
|
611 size_t flags_size = sizeof(context_sparc->context_flags); |
|
612 uint8_t* context_after_flags = |
|
613 reinterpret_cast<uint8_t*>(context_sparc.get()) + flags_size; |
|
614 if (!minidump_->ReadBytes(context_after_flags, |
|
615 sizeof(MDRawContextSPARC) - flags_size)) { |
|
616 BPLOG(ERROR) << "MinidumpContext could not read sparc context"; |
|
617 return false; |
|
618 } |
|
619 |
|
620 // Do this after reading the entire MDRawContext structure because |
|
621 // GetSystemInfo may seek minidump to a new position. |
|
622 if (!CheckAgainstSystemInfo(cpu_type)) { |
|
623 BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; |
|
624 return false; |
|
625 } |
|
626 |
|
627 if (minidump_->swap()) { |
|
628 // context_sparc->context_flags was already swapped. |
|
629 for (unsigned int gpr_index = 0; |
|
630 gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; |
|
631 ++gpr_index) { |
|
632 Swap(&context_sparc->g_r[gpr_index]); |
|
633 } |
|
634 Swap(&context_sparc->ccr); |
|
635 Swap(&context_sparc->pc); |
|
636 Swap(&context_sparc->npc); |
|
637 Swap(&context_sparc->y); |
|
638 Swap(&context_sparc->asi); |
|
639 Swap(&context_sparc->fprs); |
|
640 for (unsigned int fpr_index = 0; |
|
641 fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; |
|
642 ++fpr_index) { |
|
643 Swap(&context_sparc->float_save.regs[fpr_index]); |
|
644 } |
|
645 Swap(&context_sparc->float_save.filler); |
|
646 Swap(&context_sparc->float_save.fsr); |
|
647 } |
|
648 context_.ctx_sparc = context_sparc.release(); |
|
649 |
|
650 break; |
|
651 } |
|
652 |
|
653 case MD_CONTEXT_ARM: { |
|
654 if (expected_size != sizeof(MDRawContextARM)) { |
|
655 BPLOG(ERROR) << "MinidumpContext arm size mismatch, " << |
|
656 expected_size << " != " << sizeof(MDRawContextARM); |
|
657 return false; |
|
658 } |
|
659 |
|
660 scoped_ptr<MDRawContextARM> context_arm(new MDRawContextARM()); |
|
661 |
|
662 // Set the context_flags member, which has already been read, and |
|
663 // read the rest of the structure beginning with the first member |
|
664 // after context_flags. |
|
665 context_arm->context_flags = context_flags; |
|
666 |
|
667 size_t flags_size = sizeof(context_arm->context_flags); |
|
668 uint8_t* context_after_flags = |
|
669 reinterpret_cast<uint8_t*>(context_arm.get()) + flags_size; |
|
670 if (!minidump_->ReadBytes(context_after_flags, |
|
671 sizeof(MDRawContextARM) - flags_size)) { |
|
672 BPLOG(ERROR) << "MinidumpContext could not read arm context"; |
|
673 return false; |
|
674 } |
|
675 |
|
676 // Do this after reading the entire MDRawContext structure because |
|
677 // GetSystemInfo may seek minidump to a new position. |
|
678 if (!CheckAgainstSystemInfo(cpu_type)) { |
|
679 BPLOG(ERROR) << "MinidumpContext arm does not match system info"; |
|
680 return false; |
|
681 } |
|
682 |
|
683 if (minidump_->swap()) { |
|
684 // context_arm->context_flags was already swapped. |
|
685 for (unsigned int ireg_index = 0; |
|
686 ireg_index < MD_CONTEXT_ARM_GPR_COUNT; |
|
687 ++ireg_index) { |
|
688 Swap(&context_arm->iregs[ireg_index]); |
|
689 } |
|
690 Swap(&context_arm->cpsr); |
|
691 Swap(&context_arm->float_save.fpscr); |
|
692 for (unsigned int fpr_index = 0; |
|
693 fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; |
|
694 ++fpr_index) { |
|
695 Swap(&context_arm->float_save.regs[fpr_index]); |
|
696 } |
|
697 for (unsigned int fpe_index = 0; |
|
698 fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; |
|
699 ++fpe_index) { |
|
700 Swap(&context_arm->float_save.extra[fpe_index]); |
|
701 } |
|
702 } |
|
703 context_.arm = context_arm.release(); |
|
704 |
|
705 break; |
|
706 } |
|
707 |
|
708 default: { |
|
709 // Unknown context type - Don't log as an error yet. Let the |
|
710 // caller work that out. |
|
711 BPLOG(INFO) << "MinidumpContext unknown context type " << |
|
712 HexString(cpu_type); |
|
713 return false; |
|
714 break; |
|
715 } |
|
716 } |
|
717 context_flags_ = context_flags; |
|
718 } |
|
719 |
|
720 valid_ = true; |
|
721 return true; |
|
722 } |
|
723 |
|
724 |
|
725 uint32_t MinidumpContext::GetContextCPU() const { |
|
726 if (!valid_) { |
|
727 // Don't log a message, GetContextCPU can be legitimately called with |
|
728 // valid_ false by FreeContext, which is called by Read. |
|
729 return 0; |
|
730 } |
|
731 |
|
732 return context_flags_ & MD_CONTEXT_CPU_MASK; |
|
733 } |
|
734 |
|
735 bool MinidumpContext::GetInstructionPointer(uint64_t* ip) const { |
|
736 BPLOG_IF(ERROR, !ip) << "MinidumpContext::GetInstructionPointer " |
|
737 "requires |ip|"; |
|
738 assert(ip); |
|
739 *ip = 0; |
|
740 |
|
741 if (!valid_) { |
|
742 BPLOG(ERROR) << "Invalid MinidumpContext for GetInstructionPointer"; |
|
743 return false; |
|
744 } |
|
745 |
|
746 switch (context_flags_ & MD_CONTEXT_CPU_MASK) { |
|
747 case MD_CONTEXT_AMD64: |
|
748 *ip = context_.amd64->rip; |
|
749 break; |
|
750 case MD_CONTEXT_ARM: |
|
751 *ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC]; |
|
752 break; |
|
753 case MD_CONTEXT_PPC: |
|
754 *ip = context_.ppc->srr0; |
|
755 break; |
|
756 case MD_CONTEXT_SPARC: |
|
757 *ip = context_.ctx_sparc->pc; |
|
758 break; |
|
759 case MD_CONTEXT_X86: |
|
760 *ip = context_.x86->eip; |
|
761 break; |
|
762 default: |
|
763 // This should never happen. |
|
764 BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer"; |
|
765 return false; |
|
766 } |
|
767 return true; |
|
768 } |
|
769 |
|
770 |
|
771 const MDRawContextX86* MinidumpContext::GetContextX86() const { |
|
772 if (GetContextCPU() != MD_CONTEXT_X86) { |
|
773 BPLOG(ERROR) << "MinidumpContext cannot get x86 context"; |
|
774 return NULL; |
|
775 } |
|
776 |
|
777 return context_.x86; |
|
778 } |
|
779 |
|
780 |
|
781 const MDRawContextPPC* MinidumpContext::GetContextPPC() const { |
|
782 if (GetContextCPU() != MD_CONTEXT_PPC) { |
|
783 BPLOG(ERROR) << "MinidumpContext cannot get ppc context"; |
|
784 return NULL; |
|
785 } |
|
786 |
|
787 return context_.ppc; |
|
788 } |
|
789 |
|
790 const MDRawContextAMD64* MinidumpContext::GetContextAMD64() const { |
|
791 if (GetContextCPU() != MD_CONTEXT_AMD64) { |
|
792 BPLOG(ERROR) << "MinidumpContext cannot get amd64 context"; |
|
793 return NULL; |
|
794 } |
|
795 |
|
796 return context_.amd64; |
|
797 } |
|
798 |
|
799 const MDRawContextSPARC* MinidumpContext::GetContextSPARC() const { |
|
800 if (GetContextCPU() != MD_CONTEXT_SPARC) { |
|
801 BPLOG(ERROR) << "MinidumpContext cannot get sparc context"; |
|
802 return NULL; |
|
803 } |
|
804 |
|
805 return context_.ctx_sparc; |
|
806 } |
|
807 |
|
808 const MDRawContextARM* MinidumpContext::GetContextARM() const { |
|
809 if (GetContextCPU() != MD_CONTEXT_ARM) { |
|
810 BPLOG(ERROR) << "MinidumpContext cannot get arm context"; |
|
811 return NULL; |
|
812 } |
|
813 |
|
814 return context_.arm; |
|
815 } |
|
816 |
|
817 void MinidumpContext::FreeContext() { |
|
818 switch (GetContextCPU()) { |
|
819 case MD_CONTEXT_X86: |
|
820 delete context_.x86; |
|
821 break; |
|
822 |
|
823 case MD_CONTEXT_PPC: |
|
824 delete context_.ppc; |
|
825 break; |
|
826 |
|
827 case MD_CONTEXT_AMD64: |
|
828 delete context_.amd64; |
|
829 break; |
|
830 |
|
831 case MD_CONTEXT_SPARC: |
|
832 delete context_.ctx_sparc; |
|
833 break; |
|
834 |
|
835 case MD_CONTEXT_ARM: |
|
836 delete context_.arm; |
|
837 break; |
|
838 |
|
839 default: |
|
840 // There is no context record (valid_ is false) or there's a |
|
841 // context record for an unknown CPU (shouldn't happen, only known |
|
842 // records are stored by Read). |
|
843 break; |
|
844 } |
|
845 |
|
846 context_flags_ = 0; |
|
847 context_.base = NULL; |
|
848 } |
|
849 |
|
850 |
|
851 bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) { |
|
852 // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM, |
|
853 // as this function just implements a sanity check. |
|
854 MinidumpSystemInfo* system_info = minidump_->GetSystemInfo(); |
|
855 if (!system_info) { |
|
856 BPLOG(INFO) << "MinidumpContext could not be compared against " |
|
857 "MinidumpSystemInfo"; |
|
858 return true; |
|
859 } |
|
860 |
|
861 // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info. |
|
862 const MDRawSystemInfo* raw_system_info = system_info->system_info(); |
|
863 if (!raw_system_info) { |
|
864 BPLOG(INFO) << "MinidumpContext could not be compared against " |
|
865 "MDRawSystemInfo"; |
|
866 return false; |
|
867 } |
|
868 |
|
869 MDCPUArchitecture system_info_cpu_type = static_cast<MDCPUArchitecture>( |
|
870 raw_system_info->processor_architecture); |
|
871 |
|
872 // Compare the CPU type of the context record to the CPU type in the |
|
873 // minidump's system info stream. |
|
874 bool return_value = false; |
|
875 switch (context_cpu_type) { |
|
876 case MD_CONTEXT_X86: |
|
877 if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 || |
|
878 system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 || |
|
879 system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) { |
|
880 return_value = true; |
|
881 } |
|
882 break; |
|
883 |
|
884 case MD_CONTEXT_PPC: |
|
885 if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC) |
|
886 return_value = true; |
|
887 break; |
|
888 |
|
889 case MD_CONTEXT_AMD64: |
|
890 if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) |
|
891 return_value = true; |
|
892 break; |
|
893 |
|
894 case MD_CONTEXT_SPARC: |
|
895 if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) |
|
896 return_value = true; |
|
897 break; |
|
898 |
|
899 case MD_CONTEXT_ARM: |
|
900 if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM) |
|
901 return_value = true; |
|
902 break; |
|
903 } |
|
904 |
|
905 BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << |
|
906 HexString(context_cpu_type) << |
|
907 " wrong for MinidumpSystemInfo CPU " << |
|
908 HexString(system_info_cpu_type); |
|
909 |
|
910 return return_value; |
|
911 } |
|
912 |
|
913 |
|
914 void MinidumpContext::Print() { |
|
915 if (!valid_) { |
|
916 BPLOG(ERROR) << "MinidumpContext cannot print invalid data"; |
|
917 return; |
|
918 } |
|
919 |
|
920 switch (GetContextCPU()) { |
|
921 case MD_CONTEXT_X86: { |
|
922 const MDRawContextX86* context_x86 = GetContextX86(); |
|
923 printf("MDRawContextX86\n"); |
|
924 printf(" context_flags = 0x%x\n", |
|
925 context_x86->context_flags); |
|
926 printf(" dr0 = 0x%x\n", context_x86->dr0); |
|
927 printf(" dr1 = 0x%x\n", context_x86->dr1); |
|
928 printf(" dr2 = 0x%x\n", context_x86->dr2); |
|
929 printf(" dr3 = 0x%x\n", context_x86->dr3); |
|
930 printf(" dr6 = 0x%x\n", context_x86->dr6); |
|
931 printf(" dr7 = 0x%x\n", context_x86->dr7); |
|
932 printf(" float_save.control_word = 0x%x\n", |
|
933 context_x86->float_save.control_word); |
|
934 printf(" float_save.status_word = 0x%x\n", |
|
935 context_x86->float_save.status_word); |
|
936 printf(" float_save.tag_word = 0x%x\n", |
|
937 context_x86->float_save.tag_word); |
|
938 printf(" float_save.error_offset = 0x%x\n", |
|
939 context_x86->float_save.error_offset); |
|
940 printf(" float_save.error_selector = 0x%x\n", |
|
941 context_x86->float_save.error_selector); |
|
942 printf(" float_save.data_offset = 0x%x\n", |
|
943 context_x86->float_save.data_offset); |
|
944 printf(" float_save.data_selector = 0x%x\n", |
|
945 context_x86->float_save.data_selector); |
|
946 printf(" float_save.register_area[%2d] = 0x", |
|
947 MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE); |
|
948 for (unsigned int register_index = 0; |
|
949 register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE; |
|
950 ++register_index) { |
|
951 printf("%02x", context_x86->float_save.register_area[register_index]); |
|
952 } |
|
953 printf("\n"); |
|
954 printf(" float_save.cr0_npx_state = 0x%x\n", |
|
955 context_x86->float_save.cr0_npx_state); |
|
956 printf(" gs = 0x%x\n", context_x86->gs); |
|
957 printf(" fs = 0x%x\n", context_x86->fs); |
|
958 printf(" es = 0x%x\n", context_x86->es); |
|
959 printf(" ds = 0x%x\n", context_x86->ds); |
|
960 printf(" edi = 0x%x\n", context_x86->edi); |
|
961 printf(" esi = 0x%x\n", context_x86->esi); |
|
962 printf(" ebx = 0x%x\n", context_x86->ebx); |
|
963 printf(" edx = 0x%x\n", context_x86->edx); |
|
964 printf(" ecx = 0x%x\n", context_x86->ecx); |
|
965 printf(" eax = 0x%x\n", context_x86->eax); |
|
966 printf(" ebp = 0x%x\n", context_x86->ebp); |
|
967 printf(" eip = 0x%x\n", context_x86->eip); |
|
968 printf(" cs = 0x%x\n", context_x86->cs); |
|
969 printf(" eflags = 0x%x\n", context_x86->eflags); |
|
970 printf(" esp = 0x%x\n", context_x86->esp); |
|
971 printf(" ss = 0x%x\n", context_x86->ss); |
|
972 printf(" extended_registers[%3d] = 0x", |
|
973 MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE); |
|
974 for (unsigned int register_index = 0; |
|
975 register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE; |
|
976 ++register_index) { |
|
977 printf("%02x", context_x86->extended_registers[register_index]); |
|
978 } |
|
979 printf("\n\n"); |
|
980 |
|
981 break; |
|
982 } |
|
983 |
|
984 case MD_CONTEXT_PPC: { |
|
985 const MDRawContextPPC* context_ppc = GetContextPPC(); |
|
986 printf("MDRawContextPPC\n"); |
|
987 printf(" context_flags = 0x%x\n", |
|
988 context_ppc->context_flags); |
|
989 printf(" srr0 = 0x%x\n", context_ppc->srr0); |
|
990 printf(" srr1 = 0x%x\n", context_ppc->srr1); |
|
991 for (unsigned int gpr_index = 0; |
|
992 gpr_index < MD_CONTEXT_PPC_GPR_COUNT; |
|
993 ++gpr_index) { |
|
994 printf(" gpr[%2d] = 0x%x\n", |
|
995 gpr_index, context_ppc->gpr[gpr_index]); |
|
996 } |
|
997 printf(" cr = 0x%x\n", context_ppc->cr); |
|
998 printf(" xer = 0x%x\n", context_ppc->xer); |
|
999 printf(" lr = 0x%x\n", context_ppc->lr); |
|
1000 printf(" ctr = 0x%x\n", context_ppc->ctr); |
|
1001 printf(" mq = 0x%x\n", context_ppc->mq); |
|
1002 printf(" vrsave = 0x%x\n", context_ppc->vrsave); |
|
1003 for (unsigned int fpr_index = 0; |
|
1004 fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; |
|
1005 ++fpr_index) { |
|
1006 printf(" float_save.fpregs[%2d] = 0x%" PRIx64 "\n", |
|
1007 fpr_index, context_ppc->float_save.fpregs[fpr_index]); |
|
1008 } |
|
1009 printf(" float_save.fpscr = 0x%x\n", |
|
1010 context_ppc->float_save.fpscr); |
|
1011 // TODO(mmentovai): print the 128-bit quantities in |
|
1012 // context_ppc->vector_save. This isn't done yet because printf |
|
1013 // doesn't support 128-bit quantities, and printing them using |
|
1014 // PRIx64 as two 64-bit quantities requires knowledge of the CPU's |
|
1015 // byte ordering. |
|
1016 printf(" vector_save.save_vrvalid = 0x%x\n", |
|
1017 context_ppc->vector_save.save_vrvalid); |
|
1018 printf("\n"); |
|
1019 |
|
1020 break; |
|
1021 } |
|
1022 |
|
1023 case MD_CONTEXT_AMD64: { |
|
1024 const MDRawContextAMD64* context_amd64 = GetContextAMD64(); |
|
1025 printf("MDRawContextAMD64\n"); |
|
1026 printf(" p1_home = 0x%" PRIx64 "\n", |
|
1027 context_amd64->p1_home); |
|
1028 printf(" p2_home = 0x%" PRIx64 "\n", |
|
1029 context_amd64->p2_home); |
|
1030 printf(" p3_home = 0x%" PRIx64 "\n", |
|
1031 context_amd64->p3_home); |
|
1032 printf(" p4_home = 0x%" PRIx64 "\n", |
|
1033 context_amd64->p4_home); |
|
1034 printf(" p5_home = 0x%" PRIx64 "\n", |
|
1035 context_amd64->p5_home); |
|
1036 printf(" p6_home = 0x%" PRIx64 "\n", |
|
1037 context_amd64->p6_home); |
|
1038 printf(" context_flags = 0x%x\n", |
|
1039 context_amd64->context_flags); |
|
1040 printf(" mx_csr = 0x%x\n", |
|
1041 context_amd64->mx_csr); |
|
1042 printf(" cs = 0x%x\n", context_amd64->cs); |
|
1043 printf(" ds = 0x%x\n", context_amd64->ds); |
|
1044 printf(" es = 0x%x\n", context_amd64->es); |
|
1045 printf(" fs = 0x%x\n", context_amd64->fs); |
|
1046 printf(" gs = 0x%x\n", context_amd64->gs); |
|
1047 printf(" ss = 0x%x\n", context_amd64->ss); |
|
1048 printf(" eflags = 0x%x\n", context_amd64->eflags); |
|
1049 printf(" dr0 = 0x%" PRIx64 "\n", context_amd64->dr0); |
|
1050 printf(" dr1 = 0x%" PRIx64 "\n", context_amd64->dr1); |
|
1051 printf(" dr2 = 0x%" PRIx64 "\n", context_amd64->dr2); |
|
1052 printf(" dr3 = 0x%" PRIx64 "\n", context_amd64->dr3); |
|
1053 printf(" dr6 = 0x%" PRIx64 "\n", context_amd64->dr6); |
|
1054 printf(" dr7 = 0x%" PRIx64 "\n", context_amd64->dr7); |
|
1055 printf(" rax = 0x%" PRIx64 "\n", context_amd64->rax); |
|
1056 printf(" rcx = 0x%" PRIx64 "\n", context_amd64->rcx); |
|
1057 printf(" rdx = 0x%" PRIx64 "\n", context_amd64->rdx); |
|
1058 printf(" rbx = 0x%" PRIx64 "\n", context_amd64->rbx); |
|
1059 printf(" rsp = 0x%" PRIx64 "\n", context_amd64->rsp); |
|
1060 printf(" rbp = 0x%" PRIx64 "\n", context_amd64->rbp); |
|
1061 printf(" rsi = 0x%" PRIx64 "\n", context_amd64->rsi); |
|
1062 printf(" rdi = 0x%" PRIx64 "\n", context_amd64->rdi); |
|
1063 printf(" r8 = 0x%" PRIx64 "\n", context_amd64->r8); |
|
1064 printf(" r9 = 0x%" PRIx64 "\n", context_amd64->r9); |
|
1065 printf(" r10 = 0x%" PRIx64 "\n", context_amd64->r10); |
|
1066 printf(" r11 = 0x%" PRIx64 "\n", context_amd64->r11); |
|
1067 printf(" r12 = 0x%" PRIx64 "\n", context_amd64->r12); |
|
1068 printf(" r13 = 0x%" PRIx64 "\n", context_amd64->r13); |
|
1069 printf(" r14 = 0x%" PRIx64 "\n", context_amd64->r14); |
|
1070 printf(" r15 = 0x%" PRIx64 "\n", context_amd64->r15); |
|
1071 printf(" rip = 0x%" PRIx64 "\n", context_amd64->rip); |
|
1072 //TODO: print xmm, vector, debug registers |
|
1073 printf("\n"); |
|
1074 break; |
|
1075 } |
|
1076 |
|
1077 case MD_CONTEXT_SPARC: { |
|
1078 const MDRawContextSPARC* context_sparc = GetContextSPARC(); |
|
1079 printf("MDRawContextSPARC\n"); |
|
1080 printf(" context_flags = 0x%x\n", |
|
1081 context_sparc->context_flags); |
|
1082 for (unsigned int g_r_index = 0; |
|
1083 g_r_index < MD_CONTEXT_SPARC_GPR_COUNT; |
|
1084 ++g_r_index) { |
|
1085 printf(" g_r[%2d] = 0x%" PRIx64 "\n", |
|
1086 g_r_index, context_sparc->g_r[g_r_index]); |
|
1087 } |
|
1088 printf(" ccr = 0x%" PRIx64 "\n", context_sparc->ccr); |
|
1089 printf(" pc = 0x%" PRIx64 "\n", context_sparc->pc); |
|
1090 printf(" npc = 0x%" PRIx64 "\n", context_sparc->npc); |
|
1091 printf(" y = 0x%" PRIx64 "\n", context_sparc->y); |
|
1092 printf(" asi = 0x%" PRIx64 "\n", context_sparc->asi); |
|
1093 printf(" fprs = 0x%" PRIx64 "\n", context_sparc->fprs); |
|
1094 |
|
1095 for (unsigned int fpr_index = 0; |
|
1096 fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; |
|
1097 ++fpr_index) { |
|
1098 printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", |
|
1099 fpr_index, context_sparc->float_save.regs[fpr_index]); |
|
1100 } |
|
1101 printf(" float_save.filler = 0x%" PRIx64 "\n", |
|
1102 context_sparc->float_save.filler); |
|
1103 printf(" float_save.fsr = 0x%" PRIx64 "\n", |
|
1104 context_sparc->float_save.fsr); |
|
1105 break; |
|
1106 } |
|
1107 |
|
1108 case MD_CONTEXT_ARM: { |
|
1109 const MDRawContextARM* context_arm = GetContextARM(); |
|
1110 printf("MDRawContextARM\n"); |
|
1111 printf(" context_flags = 0x%x\n", |
|
1112 context_arm->context_flags); |
|
1113 for (unsigned int ireg_index = 0; |
|
1114 ireg_index < MD_CONTEXT_ARM_GPR_COUNT; |
|
1115 ++ireg_index) { |
|
1116 printf(" iregs[%2d] = 0x%x\n", |
|
1117 ireg_index, context_arm->iregs[ireg_index]); |
|
1118 } |
|
1119 printf(" cpsr = 0x%x\n", context_arm->cpsr); |
|
1120 printf(" float_save.fpscr = 0x%" PRIx64 "\n", |
|
1121 context_arm->float_save.fpscr); |
|
1122 for (unsigned int fpr_index = 0; |
|
1123 fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; |
|
1124 ++fpr_index) { |
|
1125 printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", |
|
1126 fpr_index, context_arm->float_save.regs[fpr_index]); |
|
1127 } |
|
1128 for (unsigned int fpe_index = 0; |
|
1129 fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; |
|
1130 ++fpe_index) { |
|
1131 printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n", |
|
1132 fpe_index, context_arm->float_save.extra[fpe_index]); |
|
1133 } |
|
1134 |
|
1135 break; |
|
1136 } |
|
1137 |
|
1138 default: { |
|
1139 break; |
|
1140 } |
|
1141 } |
|
1142 } |
|
1143 |
|
1144 |
|
1145 // |
|
1146 // MinidumpMemoryRegion |
|
1147 // |
|
1148 |
|
1149 |
|
1150 uint32_t MinidumpMemoryRegion::max_bytes_ = 1024 * 1024; // 1MB |
|
1151 |
|
1152 |
|
1153 MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) |
|
1154 : MinidumpObject(minidump), |
|
1155 descriptor_(NULL), |
|
1156 memory_(NULL) { |
|
1157 } |
|
1158 |
|
1159 |
|
1160 MinidumpMemoryRegion::~MinidumpMemoryRegion() { |
|
1161 delete memory_; |
|
1162 } |
|
1163 |
|
1164 |
|
1165 void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) { |
|
1166 descriptor_ = descriptor; |
|
1167 valid_ = descriptor && |
|
1168 descriptor_->memory.data_size <= |
|
1169 numeric_limits<uint64_t>::max() - |
|
1170 descriptor_->start_of_memory_range; |
|
1171 } |
|
1172 |
|
1173 |
|
1174 const uint8_t* MinidumpMemoryRegion::GetMemory() const { |
|
1175 if (!valid_) { |
|
1176 BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory"; |
|
1177 return NULL; |
|
1178 } |
|
1179 |
|
1180 if (!memory_) { |
|
1181 if (descriptor_->memory.data_size == 0) { |
|
1182 BPLOG(ERROR) << "MinidumpMemoryRegion is empty"; |
|
1183 return NULL; |
|
1184 } |
|
1185 |
|
1186 if (!minidump_->SeekSet(descriptor_->memory.rva)) { |
|
1187 BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region"; |
|
1188 return NULL; |
|
1189 } |
|
1190 |
|
1191 if (descriptor_->memory.data_size > max_bytes_) { |
|
1192 BPLOG(ERROR) << "MinidumpMemoryRegion size " << |
|
1193 descriptor_->memory.data_size << " exceeds maximum " << |
|
1194 max_bytes_; |
|
1195 return NULL; |
|
1196 } |
|
1197 |
|
1198 scoped_ptr< vector<uint8_t> > memory( |
|
1199 new vector<uint8_t>(descriptor_->memory.data_size)); |
|
1200 |
|
1201 if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) { |
|
1202 BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region"; |
|
1203 return NULL; |
|
1204 } |
|
1205 |
|
1206 memory_ = memory.release(); |
|
1207 } |
|
1208 |
|
1209 return &(*memory_)[0]; |
|
1210 } |
|
1211 |
|
1212 |
|
1213 uint64_t MinidumpMemoryRegion::GetBase() const { |
|
1214 if (!valid_) { |
|
1215 BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase"; |
|
1216 return static_cast<uint64_t>(-1); |
|
1217 } |
|
1218 |
|
1219 return descriptor_->start_of_memory_range; |
|
1220 } |
|
1221 |
|
1222 |
|
1223 uint32_t MinidumpMemoryRegion::GetSize() const { |
|
1224 if (!valid_) { |
|
1225 BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize"; |
|
1226 return 0; |
|
1227 } |
|
1228 |
|
1229 return descriptor_->memory.data_size; |
|
1230 } |
|
1231 |
|
1232 |
|
1233 void MinidumpMemoryRegion::FreeMemory() { |
|
1234 delete memory_; |
|
1235 memory_ = NULL; |
|
1236 } |
|
1237 |
|
1238 |
|
1239 template<typename T> |
|
1240 bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(uint64_t address, |
|
1241 T* value) const { |
|
1242 BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal " |
|
1243 "requires |value|"; |
|
1244 assert(value); |
|
1245 *value = 0; |
|
1246 |
|
1247 if (!valid_) { |
|
1248 BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for " |
|
1249 "GetMemoryAtAddressInternal"; |
|
1250 return false; |
|
1251 } |
|
1252 |
|
1253 // Common failure case |
|
1254 if (address < descriptor_->start_of_memory_range || |
|
1255 sizeof(T) > numeric_limits<uint64_t>::max() - address || |
|
1256 address + sizeof(T) > descriptor_->start_of_memory_range + |
|
1257 descriptor_->memory.data_size) { |
|
1258 BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " << |
|
1259 HexString(address) << "+" << sizeof(T) << "/" << |
|
1260 HexString(descriptor_->start_of_memory_range) << "+" << |
|
1261 HexString(descriptor_->memory.data_size); |
|
1262 return false; |
|
1263 } |
|
1264 |
|
1265 const uint8_t* memory = GetMemory(); |
|
1266 if (!memory) { |
|
1267 // GetMemory already logged a perfectly good message. |
|
1268 return false; |
|
1269 } |
|
1270 |
|
1271 // If the CPU requires memory accesses to be aligned, this can crash. |
|
1272 // x86 and ppc are able to cope, though. |
|
1273 *value = *reinterpret_cast<const T*>( |
|
1274 &memory[address - descriptor_->start_of_memory_range]); |
|
1275 |
|
1276 if (minidump_->swap()) |
|
1277 Swap(value); |
|
1278 |
|
1279 return true; |
|
1280 } |
|
1281 |
|
1282 |
|
1283 bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
|
1284 uint8_t* value) const { |
|
1285 return GetMemoryAtAddressInternal(address, value); |
|
1286 } |
|
1287 |
|
1288 |
|
1289 bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
|
1290 uint16_t* value) const { |
|
1291 return GetMemoryAtAddressInternal(address, value); |
|
1292 } |
|
1293 |
|
1294 |
|
1295 bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
|
1296 uint32_t* value) const { |
|
1297 return GetMemoryAtAddressInternal(address, value); |
|
1298 } |
|
1299 |
|
1300 |
|
1301 bool MinidumpMemoryRegion::GetMemoryAtAddress(uint64_t address, |
|
1302 uint64_t* value) const { |
|
1303 return GetMemoryAtAddressInternal(address, value); |
|
1304 } |
|
1305 |
|
1306 |
|
1307 void MinidumpMemoryRegion::Print() { |
|
1308 if (!valid_) { |
|
1309 BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data"; |
|
1310 return; |
|
1311 } |
|
1312 |
|
1313 const uint8_t* memory = GetMemory(); |
|
1314 if (memory) { |
|
1315 printf("0x"); |
|
1316 for (unsigned int byte_index = 0; |
|
1317 byte_index < descriptor_->memory.data_size; |
|
1318 byte_index++) { |
|
1319 printf("%02x", memory[byte_index]); |
|
1320 } |
|
1321 printf("\n"); |
|
1322 } else { |
|
1323 printf("No memory\n"); |
|
1324 } |
|
1325 } |
|
1326 |
|
1327 |
|
1328 // |
|
1329 // MinidumpThread |
|
1330 // |
|
1331 |
|
1332 |
|
1333 MinidumpThread::MinidumpThread(Minidump* minidump) |
|
1334 : MinidumpObject(minidump), |
|
1335 thread_(), |
|
1336 memory_(NULL), |
|
1337 context_(NULL) { |
|
1338 } |
|
1339 |
|
1340 |
|
1341 MinidumpThread::~MinidumpThread() { |
|
1342 delete memory_; |
|
1343 delete context_; |
|
1344 } |
|
1345 |
|
1346 |
|
1347 bool MinidumpThread::Read() { |
|
1348 // Invalidate cached data. |
|
1349 delete memory_; |
|
1350 memory_ = NULL; |
|
1351 delete context_; |
|
1352 context_ = NULL; |
|
1353 |
|
1354 valid_ = false; |
|
1355 |
|
1356 if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) { |
|
1357 BPLOG(ERROR) << "MinidumpThread cannot read thread"; |
|
1358 return false; |
|
1359 } |
|
1360 |
|
1361 if (minidump_->swap()) { |
|
1362 Swap(&thread_.thread_id); |
|
1363 Swap(&thread_.suspend_count); |
|
1364 Swap(&thread_.priority_class); |
|
1365 Swap(&thread_.priority); |
|
1366 Swap(&thread_.teb); |
|
1367 Swap(&thread_.stack); |
|
1368 Swap(&thread_.thread_context); |
|
1369 } |
|
1370 |
|
1371 // Check for base + size overflow or undersize. |
|
1372 if (thread_.stack.memory.data_size == 0 || |
|
1373 thread_.stack.memory.data_size > numeric_limits<uint64_t>::max() - |
|
1374 thread_.stack.start_of_memory_range) { |
|
1375 // This is ok, but log an error anyway. |
|
1376 BPLOG(ERROR) << "MinidumpThread has a memory region problem, " << |
|
1377 HexString(thread_.stack.start_of_memory_range) << "+" << |
|
1378 HexString(thread_.stack.memory.data_size); |
|
1379 } else { |
|
1380 memory_ = new MinidumpMemoryRegion(minidump_); |
|
1381 memory_->SetDescriptor(&thread_.stack); |
|
1382 } |
|
1383 |
|
1384 valid_ = true; |
|
1385 return true; |
|
1386 } |
|
1387 |
|
1388 |
|
1389 MinidumpMemoryRegion* MinidumpThread::GetMemory() { |
|
1390 if (!valid_) { |
|
1391 BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory"; |
|
1392 return NULL; |
|
1393 } |
|
1394 |
|
1395 return memory_; |
|
1396 } |
|
1397 |
|
1398 |
|
1399 MinidumpContext* MinidumpThread::GetContext() { |
|
1400 if (!valid_) { |
|
1401 BPLOG(ERROR) << "Invalid MinidumpThread for GetContext"; |
|
1402 return NULL; |
|
1403 } |
|
1404 |
|
1405 if (!context_) { |
|
1406 if (!minidump_->SeekSet(thread_.thread_context.rva)) { |
|
1407 BPLOG(ERROR) << "MinidumpThread cannot seek to context"; |
|
1408 return NULL; |
|
1409 } |
|
1410 |
|
1411 scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_)); |
|
1412 |
|
1413 if (!context->Read(thread_.thread_context.data_size)) { |
|
1414 BPLOG(ERROR) << "MinidumpThread cannot read context"; |
|
1415 return NULL; |
|
1416 } |
|
1417 |
|
1418 context_ = context.release(); |
|
1419 } |
|
1420 |
|
1421 return context_; |
|
1422 } |
|
1423 |
|
1424 |
|
1425 bool MinidumpThread::GetThreadID(uint32_t *thread_id) const { |
|
1426 BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires " |
|
1427 "|thread_id|"; |
|
1428 assert(thread_id); |
|
1429 *thread_id = 0; |
|
1430 |
|
1431 if (!valid_) { |
|
1432 BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID"; |
|
1433 return false; |
|
1434 } |
|
1435 |
|
1436 *thread_id = thread_.thread_id; |
|
1437 return true; |
|
1438 } |
|
1439 |
|
1440 |
|
1441 void MinidumpThread::Print() { |
|
1442 if (!valid_) { |
|
1443 BPLOG(ERROR) << "MinidumpThread cannot print invalid data"; |
|
1444 return; |
|
1445 } |
|
1446 |
|
1447 printf("MDRawThread\n"); |
|
1448 printf(" thread_id = 0x%x\n", thread_.thread_id); |
|
1449 printf(" suspend_count = %d\n", thread_.suspend_count); |
|
1450 printf(" priority_class = 0x%x\n", thread_.priority_class); |
|
1451 printf(" priority = 0x%x\n", thread_.priority); |
|
1452 printf(" teb = 0x%" PRIx64 "\n", thread_.teb); |
|
1453 printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n", |
|
1454 thread_.stack.start_of_memory_range); |
|
1455 printf(" stack.memory.data_size = 0x%x\n", |
|
1456 thread_.stack.memory.data_size); |
|
1457 printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva); |
|
1458 printf(" thread_context.data_size = 0x%x\n", |
|
1459 thread_.thread_context.data_size); |
|
1460 printf(" thread_context.rva = 0x%x\n", |
|
1461 thread_.thread_context.rva); |
|
1462 |
|
1463 MinidumpContext* context = GetContext(); |
|
1464 if (context) { |
|
1465 printf("\n"); |
|
1466 context->Print(); |
|
1467 } else { |
|
1468 printf(" (no context)\n"); |
|
1469 printf("\n"); |
|
1470 } |
|
1471 |
|
1472 MinidumpMemoryRegion* memory = GetMemory(); |
|
1473 if (memory) { |
|
1474 printf("Stack\n"); |
|
1475 memory->Print(); |
|
1476 } else { |
|
1477 printf("No stack\n"); |
|
1478 } |
|
1479 printf("\n"); |
|
1480 } |
|
1481 |
|
1482 |
|
1483 // |
|
1484 // MinidumpThreadList |
|
1485 // |
|
1486 |
|
1487 |
|
1488 uint32_t MinidumpThreadList::max_threads_ = 4096; |
|
1489 |
|
1490 |
|
1491 MinidumpThreadList::MinidumpThreadList(Minidump* minidump) |
|
1492 : MinidumpStream(minidump), |
|
1493 id_to_thread_map_(), |
|
1494 threads_(NULL), |
|
1495 thread_count_(0) { |
|
1496 } |
|
1497 |
|
1498 |
|
1499 MinidumpThreadList::~MinidumpThreadList() { |
|
1500 delete threads_; |
|
1501 } |
|
1502 |
|
1503 |
|
1504 bool MinidumpThreadList::Read(uint32_t expected_size) { |
|
1505 // Invalidate cached data. |
|
1506 id_to_thread_map_.clear(); |
|
1507 delete threads_; |
|
1508 threads_ = NULL; |
|
1509 thread_count_ = 0; |
|
1510 |
|
1511 valid_ = false; |
|
1512 |
|
1513 uint32_t thread_count; |
|
1514 if (expected_size < sizeof(thread_count)) { |
|
1515 BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " << |
|
1516 expected_size << " < " << sizeof(thread_count); |
|
1517 return false; |
|
1518 } |
|
1519 if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) { |
|
1520 BPLOG(ERROR) << "MinidumpThreadList cannot read thread count"; |
|
1521 return false; |
|
1522 } |
|
1523 |
|
1524 if (minidump_->swap()) |
|
1525 Swap(&thread_count); |
|
1526 |
|
1527 if (thread_count > numeric_limits<uint32_t>::max() / sizeof(MDRawThread)) { |
|
1528 BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count << |
|
1529 " would cause multiplication overflow"; |
|
1530 return false; |
|
1531 } |
|
1532 |
|
1533 if (expected_size != sizeof(thread_count) + |
|
1534 thread_count * sizeof(MDRawThread)) { |
|
1535 // may be padded with 4 bytes on 64bit ABIs for alignment |
|
1536 if (expected_size == sizeof(thread_count) + 4 + |
|
1537 thread_count * sizeof(MDRawThread)) { |
|
1538 uint32_t useless; |
|
1539 if (!minidump_->ReadBytes(&useless, 4)) { |
|
1540 BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded bytes"; |
|
1541 return false; |
|
1542 } |
|
1543 } else { |
|
1544 BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << |
|
1545 " != " << sizeof(thread_count) + |
|
1546 thread_count * sizeof(MDRawThread); |
|
1547 return false; |
|
1548 } |
|
1549 } |
|
1550 |
|
1551 |
|
1552 if (thread_count > max_threads_) { |
|
1553 BPLOG(ERROR) << "MinidumpThreadList count " << thread_count << |
|
1554 " exceeds maximum " << max_threads_; |
|
1555 return false; |
|
1556 } |
|
1557 |
|
1558 if (thread_count != 0) { |
|
1559 scoped_ptr<MinidumpThreads> threads( |
|
1560 new MinidumpThreads(thread_count, MinidumpThread(minidump_))); |
|
1561 |
|
1562 for (unsigned int thread_index = 0; |
|
1563 thread_index < thread_count; |
|
1564 ++thread_index) { |
|
1565 MinidumpThread* thread = &(*threads)[thread_index]; |
|
1566 |
|
1567 // Assume that the file offset is correct after the last read. |
|
1568 if (!thread->Read()) { |
|
1569 BPLOG(ERROR) << "MinidumpThreadList cannot read thread " << |
|
1570 thread_index << "/" << thread_count; |
|
1571 return false; |
|
1572 } |
|
1573 |
|
1574 uint32_t thread_id; |
|
1575 if (!thread->GetThreadID(&thread_id)) { |
|
1576 BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " << |
|
1577 thread_index << "/" << thread_count; |
|
1578 return false; |
|
1579 } |
|
1580 |
|
1581 if (GetThreadByID(thread_id)) { |
|
1582 // Another thread with this ID is already in the list. Data error. |
|
1583 BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " << |
|
1584 HexString(thread_id) << " at thread " << |
|
1585 thread_index << "/" << thread_count; |
|
1586 return false; |
|
1587 } |
|
1588 id_to_thread_map_[thread_id] = thread; |
|
1589 } |
|
1590 |
|
1591 threads_ = threads.release(); |
|
1592 } |
|
1593 |
|
1594 thread_count_ = thread_count; |
|
1595 |
|
1596 valid_ = true; |
|
1597 return true; |
|
1598 } |
|
1599 |
|
1600 |
|
1601 MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index) |
|
1602 const { |
|
1603 if (!valid_) { |
|
1604 BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex"; |
|
1605 return NULL; |
|
1606 } |
|
1607 |
|
1608 if (index >= thread_count_) { |
|
1609 BPLOG(ERROR) << "MinidumpThreadList index out of range: " << |
|
1610 index << "/" << thread_count_; |
|
1611 return NULL; |
|
1612 } |
|
1613 |
|
1614 return &(*threads_)[index]; |
|
1615 } |
|
1616 |
|
1617 |
|
1618 MinidumpThread* MinidumpThreadList::GetThreadByID(uint32_t thread_id) { |
|
1619 // Don't check valid_. Read calls this method before everything is |
|
1620 // validated. It is safe to not check valid_ here. |
|
1621 return id_to_thread_map_[thread_id]; |
|
1622 } |
|
1623 |
|
1624 |
|
1625 void MinidumpThreadList::Print() { |
|
1626 if (!valid_) { |
|
1627 BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data"; |
|
1628 return; |
|
1629 } |
|
1630 |
|
1631 printf("MinidumpThreadList\n"); |
|
1632 printf(" thread_count = %d\n", thread_count_); |
|
1633 printf("\n"); |
|
1634 |
|
1635 for (unsigned int thread_index = 0; |
|
1636 thread_index < thread_count_; |
|
1637 ++thread_index) { |
|
1638 printf("thread[%d]\n", thread_index); |
|
1639 |
|
1640 (*threads_)[thread_index].Print(); |
|
1641 } |
|
1642 } |
|
1643 |
|
1644 |
|
1645 // |
|
1646 // MinidumpModule |
|
1647 // |
|
1648 |
|
1649 |
|
1650 uint32_t MinidumpModule::max_cv_bytes_ = 32768; |
|
1651 uint32_t MinidumpModule::max_misc_bytes_ = 32768; |
|
1652 |
|
1653 |
|
1654 MinidumpModule::MinidumpModule(Minidump* minidump) |
|
1655 : MinidumpObject(minidump), |
|
1656 module_valid_(false), |
|
1657 has_debug_info_(false), |
|
1658 module_(), |
|
1659 name_(NULL), |
|
1660 cv_record_(NULL), |
|
1661 cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE), |
|
1662 misc_record_(NULL) { |
|
1663 } |
|
1664 |
|
1665 |
|
1666 MinidumpModule::~MinidumpModule() { |
|
1667 delete name_; |
|
1668 delete cv_record_; |
|
1669 delete misc_record_; |
|
1670 } |
|
1671 |
|
1672 |
|
1673 bool MinidumpModule::Read() { |
|
1674 // Invalidate cached data. |
|
1675 delete name_; |
|
1676 name_ = NULL; |
|
1677 delete cv_record_; |
|
1678 cv_record_ = NULL; |
|
1679 cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE; |
|
1680 delete misc_record_; |
|
1681 misc_record_ = NULL; |
|
1682 |
|
1683 module_valid_ = false; |
|
1684 has_debug_info_ = false; |
|
1685 valid_ = false; |
|
1686 |
|
1687 if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) { |
|
1688 BPLOG(ERROR) << "MinidumpModule cannot read module"; |
|
1689 return false; |
|
1690 } |
|
1691 |
|
1692 if (minidump_->swap()) { |
|
1693 Swap(&module_.base_of_image); |
|
1694 Swap(&module_.size_of_image); |
|
1695 Swap(&module_.checksum); |
|
1696 Swap(&module_.time_date_stamp); |
|
1697 Swap(&module_.module_name_rva); |
|
1698 Swap(&module_.version_info.signature); |
|
1699 Swap(&module_.version_info.struct_version); |
|
1700 Swap(&module_.version_info.file_version_hi); |
|
1701 Swap(&module_.version_info.file_version_lo); |
|
1702 Swap(&module_.version_info.product_version_hi); |
|
1703 Swap(&module_.version_info.product_version_lo); |
|
1704 Swap(&module_.version_info.file_flags_mask); |
|
1705 Swap(&module_.version_info.file_flags); |
|
1706 Swap(&module_.version_info.file_os); |
|
1707 Swap(&module_.version_info.file_type); |
|
1708 Swap(&module_.version_info.file_subtype); |
|
1709 Swap(&module_.version_info.file_date_hi); |
|
1710 Swap(&module_.version_info.file_date_lo); |
|
1711 Swap(&module_.cv_record); |
|
1712 Swap(&module_.misc_record); |
|
1713 // Don't swap reserved fields because their contents are unknown (as |
|
1714 // are their proper widths). |
|
1715 } |
|
1716 |
|
1717 // Check for base + size overflow or undersize. |
|
1718 if (module_.size_of_image == 0 || |
|
1719 module_.size_of_image > |
|
1720 numeric_limits<uint64_t>::max() - module_.base_of_image) { |
|
1721 BPLOG(ERROR) << "MinidumpModule has a module problem, " << |
|
1722 HexString(module_.base_of_image) << "+" << |
|
1723 HexString(module_.size_of_image); |
|
1724 return false; |
|
1725 } |
|
1726 |
|
1727 module_valid_ = true; |
|
1728 return true; |
|
1729 } |
|
1730 |
|
1731 |
|
1732 bool MinidumpModule::ReadAuxiliaryData() { |
|
1733 if (!module_valid_) { |
|
1734 BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData"; |
|
1735 return false; |
|
1736 } |
|
1737 |
|
1738 // Each module must have a name. |
|
1739 name_ = minidump_->ReadString(module_.module_name_rva); |
|
1740 if (!name_) { |
|
1741 BPLOG(ERROR) << "MinidumpModule could not read name"; |
|
1742 return false; |
|
1743 } |
|
1744 |
|
1745 // At this point, we have enough info for the module to be valid. |
|
1746 valid_ = true; |
|
1747 |
|
1748 // CodeView and miscellaneous debug records are only required if the |
|
1749 // module indicates that they exist. |
|
1750 if (module_.cv_record.data_size && !GetCVRecord(NULL)) { |
|
1751 BPLOG(ERROR) << "MinidumpModule has no CodeView record, " |
|
1752 "but one was expected"; |
|
1753 return false; |
|
1754 } |
|
1755 |
|
1756 if (module_.misc_record.data_size && !GetMiscRecord(NULL)) { |
|
1757 BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, " |
|
1758 "but one was expected"; |
|
1759 return false; |
|
1760 } |
|
1761 |
|
1762 has_debug_info_ = true; |
|
1763 return true; |
|
1764 } |
|
1765 |
|
1766 |
|
1767 string MinidumpModule::code_file() const { |
|
1768 if (!valid_) { |
|
1769 BPLOG(ERROR) << "Invalid MinidumpModule for code_file"; |
|
1770 return ""; |
|
1771 } |
|
1772 |
|
1773 return *name_; |
|
1774 } |
|
1775 |
|
1776 |
|
1777 string MinidumpModule::code_identifier() const { |
|
1778 if (!valid_) { |
|
1779 BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier"; |
|
1780 return ""; |
|
1781 } |
|
1782 |
|
1783 if (!has_debug_info_) |
|
1784 return ""; |
|
1785 |
|
1786 MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo(); |
|
1787 if (!minidump_system_info) { |
|
1788 BPLOG(ERROR) << "MinidumpModule code_identifier requires " |
|
1789 "MinidumpSystemInfo"; |
|
1790 return ""; |
|
1791 } |
|
1792 |
|
1793 const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info(); |
|
1794 if (!raw_system_info) { |
|
1795 BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo"; |
|
1796 return ""; |
|
1797 } |
|
1798 |
|
1799 string identifier; |
|
1800 |
|
1801 switch (raw_system_info->platform_id) { |
|
1802 case MD_OS_WIN32_NT: |
|
1803 case MD_OS_WIN32_WINDOWS: { |
|
1804 // Use the same format that the MS symbol server uses in filesystem |
|
1805 // hierarchies. |
|
1806 char identifier_string[17]; |
|
1807 snprintf(identifier_string, sizeof(identifier_string), "%08X%x", |
|
1808 module_.time_date_stamp, module_.size_of_image); |
|
1809 identifier = identifier_string; |
|
1810 break; |
|
1811 } |
|
1812 |
|
1813 case MD_OS_MAC_OS_X: |
|
1814 case MD_OS_IOS: |
|
1815 case MD_OS_SOLARIS: |
|
1816 case MD_OS_ANDROID: |
|
1817 case MD_OS_LINUX: { |
|
1818 // TODO(mmentovai): support uuid extension if present, otherwise fall |
|
1819 // back to version (from LC_ID_DYLIB?), otherwise fall back to something |
|
1820 // else. |
|
1821 identifier = "id"; |
|
1822 break; |
|
1823 } |
|
1824 |
|
1825 default: { |
|
1826 // Without knowing what OS generated the dump, we can't generate a good |
|
1827 // identifier. Return an empty string, signalling failure. |
|
1828 BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, " |
|
1829 "found " << HexString(raw_system_info->platform_id); |
|
1830 break; |
|
1831 } |
|
1832 } |
|
1833 |
|
1834 return identifier; |
|
1835 } |
|
1836 |
|
1837 |
|
1838 string MinidumpModule::debug_file() const { |
|
1839 if (!valid_) { |
|
1840 BPLOG(ERROR) << "Invalid MinidumpModule for debug_file"; |
|
1841 return ""; |
|
1842 } |
|
1843 |
|
1844 if (!has_debug_info_) |
|
1845 return ""; |
|
1846 |
|
1847 string file; |
|
1848 // Prefer the CodeView record if present. |
|
1849 if (cv_record_) { |
|
1850 if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
|
1851 // It's actually an MDCVInfoPDB70 structure. |
|
1852 const MDCVInfoPDB70* cv_record_70 = |
|
1853 reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); |
|
1854 assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
|
1855 |
|
1856 // GetCVRecord guarantees pdb_file_name is null-terminated. |
|
1857 file = reinterpret_cast<const char*>(cv_record_70->pdb_file_name); |
|
1858 } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
|
1859 // It's actually an MDCVInfoPDB20 structure. |
|
1860 const MDCVInfoPDB20* cv_record_20 = |
|
1861 reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); |
|
1862 assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
|
1863 |
|
1864 // GetCVRecord guarantees pdb_file_name is null-terminated. |
|
1865 file = reinterpret_cast<const char*>(cv_record_20->pdb_file_name); |
|
1866 } |
|
1867 |
|
1868 // If there's a CodeView record but it doesn't match a known signature, |
|
1869 // try the miscellaneous record. |
|
1870 } |
|
1871 |
|
1872 if (file.empty()) { |
|
1873 // No usable CodeView record. Try the miscellaneous debug record. |
|
1874 if (misc_record_) { |
|
1875 const MDImageDebugMisc* misc_record = |
|
1876 reinterpret_cast<const MDImageDebugMisc *>(&(*misc_record_)[0]); |
|
1877 if (!misc_record->unicode) { |
|
1878 // If it's not Unicode, just stuff it into the string. It's unclear |
|
1879 // if misc_record->data is 0-terminated, so use an explicit size. |
|
1880 file = string( |
|
1881 reinterpret_cast<const char*>(misc_record->data), |
|
1882 module_.misc_record.data_size - MDImageDebugMisc_minsize); |
|
1883 } else { |
|
1884 // There's a misc_record but it encodes the debug filename in UTF-16. |
|
1885 // (Actually, because miscellaneous records are so old, it's probably |
|
1886 // UCS-2.) Convert it to UTF-8 for congruity with the other strings |
|
1887 // that this method (and all other methods in the Minidump family) |
|
1888 // return. |
|
1889 |
|
1890 unsigned int bytes = |
|
1891 module_.misc_record.data_size - MDImageDebugMisc_minsize; |
|
1892 if (bytes % 2 == 0) { |
|
1893 unsigned int utf16_words = bytes / 2; |
|
1894 |
|
1895 // UTF16ToUTF8 expects a vector<uint16_t>, so create a temporary one |
|
1896 // and copy the UTF-16 data into it. |
|
1897 vector<uint16_t> string_utf16(utf16_words); |
|
1898 if (utf16_words) |
|
1899 memcpy(&string_utf16[0], &misc_record->data, bytes); |
|
1900 |
|
1901 // GetMiscRecord already byte-swapped the data[] field if it contains |
|
1902 // UTF-16, so pass false as the swap argument. |
|
1903 scoped_ptr<string> new_file(UTF16ToUTF8(string_utf16, false)); |
|
1904 file = *new_file; |
|
1905 } |
|
1906 } |
|
1907 } |
|
1908 } |
|
1909 |
|
1910 // Relatively common case |
|
1911 BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine " |
|
1912 "debug_file for " << *name_; |
|
1913 |
|
1914 return file; |
|
1915 } |
|
1916 |
|
1917 |
|
1918 string MinidumpModule::debug_identifier() const { |
|
1919 if (!valid_) { |
|
1920 BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier"; |
|
1921 return ""; |
|
1922 } |
|
1923 |
|
1924 if (!has_debug_info_) |
|
1925 return ""; |
|
1926 |
|
1927 string identifier; |
|
1928 |
|
1929 // Use the CodeView record if present. |
|
1930 if (cv_record_) { |
|
1931 if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
|
1932 // It's actually an MDCVInfoPDB70 structure. |
|
1933 const MDCVInfoPDB70* cv_record_70 = |
|
1934 reinterpret_cast<const MDCVInfoPDB70*>(&(*cv_record_)[0]); |
|
1935 assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
|
1936 |
|
1937 // Use the same format that the MS symbol server uses in filesystem |
|
1938 // hierarchies. |
|
1939 char identifier_string[41]; |
|
1940 snprintf(identifier_string, sizeof(identifier_string), |
|
1941 "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", |
|
1942 cv_record_70->signature.data1, |
|
1943 cv_record_70->signature.data2, |
|
1944 cv_record_70->signature.data3, |
|
1945 cv_record_70->signature.data4[0], |
|
1946 cv_record_70->signature.data4[1], |
|
1947 cv_record_70->signature.data4[2], |
|
1948 cv_record_70->signature.data4[3], |
|
1949 cv_record_70->signature.data4[4], |
|
1950 cv_record_70->signature.data4[5], |
|
1951 cv_record_70->signature.data4[6], |
|
1952 cv_record_70->signature.data4[7], |
|
1953 cv_record_70->age); |
|
1954 identifier = identifier_string; |
|
1955 } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
|
1956 // It's actually an MDCVInfoPDB20 structure. |
|
1957 const MDCVInfoPDB20* cv_record_20 = |
|
1958 reinterpret_cast<const MDCVInfoPDB20*>(&(*cv_record_)[0]); |
|
1959 assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
|
1960 |
|
1961 // Use the same format that the MS symbol server uses in filesystem |
|
1962 // hierarchies. |
|
1963 char identifier_string[17]; |
|
1964 snprintf(identifier_string, sizeof(identifier_string), |
|
1965 "%08X%x", cv_record_20->signature, cv_record_20->age); |
|
1966 identifier = identifier_string; |
|
1967 } |
|
1968 } |
|
1969 |
|
1970 // TODO(mmentovai): if there's no usable CodeView record, there might be a |
|
1971 // miscellaneous debug record. It only carries a filename, though, and no |
|
1972 // identifier. I'm not sure what the right thing to do for the identifier |
|
1973 // is in that case, but I don't expect to find many modules without a |
|
1974 // CodeView record (or some other Breakpad extension structure in place of |
|
1975 // a CodeView record). Treat it as an error (empty identifier) for now. |
|
1976 |
|
1977 // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). |
|
1978 |
|
1979 // Relatively common case |
|
1980 BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine " |
|
1981 "debug_identifier for " << *name_; |
|
1982 |
|
1983 return identifier; |
|
1984 } |
|
1985 |
|
1986 |
|
1987 string MinidumpModule::version() const { |
|
1988 if (!valid_) { |
|
1989 BPLOG(ERROR) << "Invalid MinidumpModule for version"; |
|
1990 return ""; |
|
1991 } |
|
1992 |
|
1993 string version; |
|
1994 |
|
1995 if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && |
|
1996 module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { |
|
1997 char version_string[24]; |
|
1998 snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u", |
|
1999 module_.version_info.file_version_hi >> 16, |
|
2000 module_.version_info.file_version_hi & 0xffff, |
|
2001 module_.version_info.file_version_lo >> 16, |
|
2002 module_.version_info.file_version_lo & 0xffff); |
|
2003 version = version_string; |
|
2004 } |
|
2005 |
|
2006 // TODO(mmentovai): possibly support other struct types in place of |
|
2007 // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use |
|
2008 // a different structure that better represents versioning facilities on |
|
2009 // Mac OS X and Linux, instead of forcing them to adhere to the dotted |
|
2010 // quad of 16-bit ints that Windows uses. |
|
2011 |
|
2012 BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine " |
|
2013 "version for " << *name_; |
|
2014 |
|
2015 return version; |
|
2016 } |
|
2017 |
|
2018 |
|
2019 const CodeModule* MinidumpModule::Copy() const { |
|
2020 return new BasicCodeModule(this); |
|
2021 } |
|
2022 |
|
2023 |
|
2024 const uint8_t* MinidumpModule::GetCVRecord(uint32_t* size) { |
|
2025 if (!module_valid_) { |
|
2026 BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord"; |
|
2027 return NULL; |
|
2028 } |
|
2029 |
|
2030 if (!cv_record_) { |
|
2031 // This just guards against 0-sized CodeView records; more specific checks |
|
2032 // are used when the signature is checked against various structure types. |
|
2033 if (module_.cv_record.data_size == 0) { |
|
2034 return NULL; |
|
2035 } |
|
2036 |
|
2037 if (!minidump_->SeekSet(module_.cv_record.rva)) { |
|
2038 BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record"; |
|
2039 return NULL; |
|
2040 } |
|
2041 |
|
2042 if (module_.cv_record.data_size > max_cv_bytes_) { |
|
2043 BPLOG(ERROR) << "MinidumpModule CodeView record size " << |
|
2044 module_.cv_record.data_size << " exceeds maximum " << |
|
2045 max_cv_bytes_; |
|
2046 return NULL; |
|
2047 } |
|
2048 |
|
2049 // Allocating something that will be accessed as MDCVInfoPDB70 or |
|
2050 // MDCVInfoPDB20 but is allocated as uint8_t[] can cause alignment |
|
2051 // problems. x86 and ppc are able to cope, though. This allocation |
|
2052 // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are |
|
2053 // variable-sized due to their pdb_file_name fields; these structures |
|
2054 // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating |
|
2055 // them as such would result in incomplete structures or overruns. |
|
2056 scoped_ptr< vector<uint8_t> > cv_record( |
|
2057 new vector<uint8_t>(module_.cv_record.data_size)); |
|
2058 |
|
2059 if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) { |
|
2060 BPLOG(ERROR) << "MinidumpModule could not read CodeView record"; |
|
2061 return NULL; |
|
2062 } |
|
2063 |
|
2064 uint32_t signature = MD_CVINFOUNKNOWN_SIGNATURE; |
|
2065 if (module_.cv_record.data_size > sizeof(signature)) { |
|
2066 MDCVInfoPDB70* cv_record_signature = |
|
2067 reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]); |
|
2068 signature = cv_record_signature->cv_signature; |
|
2069 if (minidump_->swap()) |
|
2070 Swap(&signature); |
|
2071 } |
|
2072 |
|
2073 if (signature == MD_CVINFOPDB70_SIGNATURE) { |
|
2074 // Now that the structure type is known, recheck the size. |
|
2075 if (MDCVInfoPDB70_minsize > module_.cv_record.data_size) { |
|
2076 BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " << |
|
2077 MDCVInfoPDB70_minsize << " > " << |
|
2078 module_.cv_record.data_size; |
|
2079 return NULL; |
|
2080 } |
|
2081 |
|
2082 if (minidump_->swap()) { |
|
2083 MDCVInfoPDB70* cv_record_70 = |
|
2084 reinterpret_cast<MDCVInfoPDB70*>(&(*cv_record)[0]); |
|
2085 Swap(&cv_record_70->cv_signature); |
|
2086 Swap(&cv_record_70->signature); |
|
2087 Swap(&cv_record_70->age); |
|
2088 // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit |
|
2089 // quantities. (It's a path, is it UTF-8?) |
|
2090 } |
|
2091 |
|
2092 // The last field of either structure is null-terminated 8-bit character |
|
2093 // data. Ensure that it's null-terminated. |
|
2094 if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { |
|
2095 BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not " |
|
2096 "0-terminated"; |
|
2097 return NULL; |
|
2098 } |
|
2099 } else if (signature == MD_CVINFOPDB20_SIGNATURE) { |
|
2100 // Now that the structure type is known, recheck the size. |
|
2101 if (MDCVInfoPDB20_minsize > module_.cv_record.data_size) { |
|
2102 BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " << |
|
2103 MDCVInfoPDB20_minsize << " > " << |
|
2104 module_.cv_record.data_size; |
|
2105 return NULL; |
|
2106 } |
|
2107 if (minidump_->swap()) { |
|
2108 MDCVInfoPDB20* cv_record_20 = |
|
2109 reinterpret_cast<MDCVInfoPDB20*>(&(*cv_record)[0]); |
|
2110 Swap(&cv_record_20->cv_header.signature); |
|
2111 Swap(&cv_record_20->cv_header.offset); |
|
2112 Swap(&cv_record_20->signature); |
|
2113 Swap(&cv_record_20->age); |
|
2114 // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit |
|
2115 // quantities. (It's a path, is it UTF-8?) |
|
2116 } |
|
2117 |
|
2118 // The last field of either structure is null-terminated 8-bit character |
|
2119 // data. Ensure that it's null-terminated. |
|
2120 if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { |
|
2121 BPLOG(ERROR) << "MindumpModule CodeView2 record string is not " |
|
2122 "0-terminated"; |
|
2123 return NULL; |
|
2124 } |
|
2125 } |
|
2126 |
|
2127 // If the signature doesn't match something above, it's not something |
|
2128 // that Breakpad can presently handle directly. Because some modules in |
|
2129 // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE, |
|
2130 // don't bail out here - allow the data to be returned to the user, |
|
2131 // although byte-swapping can't be done. |
|
2132 |
|
2133 // Store the vector type because that's how storage was allocated, but |
|
2134 // return it casted to uint8_t*. |
|
2135 cv_record_ = cv_record.release(); |
|
2136 cv_record_signature_ = signature; |
|
2137 } |
|
2138 |
|
2139 if (size) |
|
2140 *size = module_.cv_record.data_size; |
|
2141 |
|
2142 return &(*cv_record_)[0]; |
|
2143 } |
|
2144 |
|
2145 |
|
2146 const MDImageDebugMisc* MinidumpModule::GetMiscRecord(uint32_t* size) { |
|
2147 if (!module_valid_) { |
|
2148 BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord"; |
|
2149 return NULL; |
|
2150 } |
|
2151 |
|
2152 if (!misc_record_) { |
|
2153 if (module_.misc_record.data_size == 0) { |
|
2154 return NULL; |
|
2155 } |
|
2156 |
|
2157 if (MDImageDebugMisc_minsize > module_.misc_record.data_size) { |
|
2158 BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record " |
|
2159 "size mismatch, " << MDImageDebugMisc_minsize << " > " << |
|
2160 module_.misc_record.data_size; |
|
2161 return NULL; |
|
2162 } |
|
2163 |
|
2164 if (!minidump_->SeekSet(module_.misc_record.rva)) { |
|
2165 BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous " |
|
2166 "debugging record"; |
|
2167 return NULL; |
|
2168 } |
|
2169 |
|
2170 if (module_.misc_record.data_size > max_misc_bytes_) { |
|
2171 BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " << |
|
2172 module_.misc_record.data_size << " exceeds maximum " << |
|
2173 max_misc_bytes_; |
|
2174 return NULL; |
|
2175 } |
|
2176 |
|
2177 // Allocating something that will be accessed as MDImageDebugMisc but |
|
2178 // is allocated as uint8_t[] can cause alignment problems. x86 and |
|
2179 // ppc are able to cope, though. This allocation style is needed |
|
2180 // because the MDImageDebugMisc is variable-sized due to its data field; |
|
2181 // this structure is not MDImageDebugMisc_minsize and treating it as such |
|
2182 // would result in an incomplete structure or an overrun. |
|
2183 scoped_ptr< vector<uint8_t> > misc_record_mem( |
|
2184 new vector<uint8_t>(module_.misc_record.data_size)); |
|
2185 MDImageDebugMisc* misc_record = |
|
2186 reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]); |
|
2187 |
|
2188 if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) { |
|
2189 BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging " |
|
2190 "record"; |
|
2191 return NULL; |
|
2192 } |
|
2193 |
|
2194 if (minidump_->swap()) { |
|
2195 Swap(&misc_record->data_type); |
|
2196 Swap(&misc_record->length); |
|
2197 // Don't swap misc_record.unicode because it's an 8-bit quantity. |
|
2198 // Don't swap the reserved fields for the same reason, and because |
|
2199 // they don't contain any valid data. |
|
2200 if (misc_record->unicode) { |
|
2201 // There is a potential alignment problem, but shouldn't be a problem |
|
2202 // in practice due to the layout of MDImageDebugMisc. |
|
2203 uint16_t* data16 = reinterpret_cast<uint16_t*>(&(misc_record->data)); |
|
2204 unsigned int dataBytes = module_.misc_record.data_size - |
|
2205 MDImageDebugMisc_minsize; |
|
2206 unsigned int dataLength = dataBytes / 2; |
|
2207 for (unsigned int characterIndex = 0; |
|
2208 characterIndex < dataLength; |
|
2209 ++characterIndex) { |
|
2210 Swap(&data16[characterIndex]); |
|
2211 } |
|
2212 } |
|
2213 } |
|
2214 |
|
2215 if (module_.misc_record.data_size != misc_record->length) { |
|
2216 BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data " |
|
2217 "size mismatch, " << module_.misc_record.data_size << |
|
2218 " != " << misc_record->length; |
|
2219 return NULL; |
|
2220 } |
|
2221 |
|
2222 // Store the vector type because that's how storage was allocated, but |
|
2223 // return it casted to MDImageDebugMisc*. |
|
2224 misc_record_ = misc_record_mem.release(); |
|
2225 } |
|
2226 |
|
2227 if (size) |
|
2228 *size = module_.misc_record.data_size; |
|
2229 |
|
2230 return reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_)[0]); |
|
2231 } |
|
2232 |
|
2233 |
|
2234 void MinidumpModule::Print() { |
|
2235 if (!valid_) { |
|
2236 BPLOG(ERROR) << "MinidumpModule cannot print invalid data"; |
|
2237 return; |
|
2238 } |
|
2239 |
|
2240 printf("MDRawModule\n"); |
|
2241 printf(" base_of_image = 0x%" PRIx64 "\n", |
|
2242 module_.base_of_image); |
|
2243 printf(" size_of_image = 0x%x\n", |
|
2244 module_.size_of_image); |
|
2245 printf(" checksum = 0x%x\n", |
|
2246 module_.checksum); |
|
2247 printf(" time_date_stamp = 0x%x\n", |
|
2248 module_.time_date_stamp); |
|
2249 printf(" module_name_rva = 0x%x\n", |
|
2250 module_.module_name_rva); |
|
2251 printf(" version_info.signature = 0x%x\n", |
|
2252 module_.version_info.signature); |
|
2253 printf(" version_info.struct_version = 0x%x\n", |
|
2254 module_.version_info.struct_version); |
|
2255 printf(" version_info.file_version = 0x%x:0x%x\n", |
|
2256 module_.version_info.file_version_hi, |
|
2257 module_.version_info.file_version_lo); |
|
2258 printf(" version_info.product_version = 0x%x:0x%x\n", |
|
2259 module_.version_info.product_version_hi, |
|
2260 module_.version_info.product_version_lo); |
|
2261 printf(" version_info.file_flags_mask = 0x%x\n", |
|
2262 module_.version_info.file_flags_mask); |
|
2263 printf(" version_info.file_flags = 0x%x\n", |
|
2264 module_.version_info.file_flags); |
|
2265 printf(" version_info.file_os = 0x%x\n", |
|
2266 module_.version_info.file_os); |
|
2267 printf(" version_info.file_type = 0x%x\n", |
|
2268 module_.version_info.file_type); |
|
2269 printf(" version_info.file_subtype = 0x%x\n", |
|
2270 module_.version_info.file_subtype); |
|
2271 printf(" version_info.file_date = 0x%x:0x%x\n", |
|
2272 module_.version_info.file_date_hi, |
|
2273 module_.version_info.file_date_lo); |
|
2274 printf(" cv_record.data_size = %d\n", |
|
2275 module_.cv_record.data_size); |
|
2276 printf(" cv_record.rva = 0x%x\n", |
|
2277 module_.cv_record.rva); |
|
2278 printf(" misc_record.data_size = %d\n", |
|
2279 module_.misc_record.data_size); |
|
2280 printf(" misc_record.rva = 0x%x\n", |
|
2281 module_.misc_record.rva); |
|
2282 |
|
2283 printf(" (code_file) = \"%s\"\n", code_file().c_str()); |
|
2284 printf(" (code_identifier) = \"%s\"\n", |
|
2285 code_identifier().c_str()); |
|
2286 |
|
2287 uint32_t cv_record_size; |
|
2288 const uint8_t *cv_record = GetCVRecord(&cv_record_size); |
|
2289 if (cv_record) { |
|
2290 if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { |
|
2291 const MDCVInfoPDB70* cv_record_70 = |
|
2292 reinterpret_cast<const MDCVInfoPDB70*>(cv_record); |
|
2293 assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); |
|
2294 |
|
2295 printf(" (cv_record).cv_signature = 0x%x\n", |
|
2296 cv_record_70->cv_signature); |
|
2297 printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-", |
|
2298 cv_record_70->signature.data1, |
|
2299 cv_record_70->signature.data2, |
|
2300 cv_record_70->signature.data3, |
|
2301 cv_record_70->signature.data4[0], |
|
2302 cv_record_70->signature.data4[1]); |
|
2303 for (unsigned int guidIndex = 2; |
|
2304 guidIndex < 8; |
|
2305 ++guidIndex) { |
|
2306 printf("%02x", cv_record_70->signature.data4[guidIndex]); |
|
2307 } |
|
2308 printf("\n"); |
|
2309 printf(" (cv_record).age = %d\n", |
|
2310 cv_record_70->age); |
|
2311 printf(" (cv_record).pdb_file_name = \"%s\"\n", |
|
2312 cv_record_70->pdb_file_name); |
|
2313 } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { |
|
2314 const MDCVInfoPDB20* cv_record_20 = |
|
2315 reinterpret_cast<const MDCVInfoPDB20*>(cv_record); |
|
2316 assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); |
|
2317 |
|
2318 printf(" (cv_record).cv_header.signature = 0x%x\n", |
|
2319 cv_record_20->cv_header.signature); |
|
2320 printf(" (cv_record).cv_header.offset = 0x%x\n", |
|
2321 cv_record_20->cv_header.offset); |
|
2322 printf(" (cv_record).signature = 0x%x\n", |
|
2323 cv_record_20->signature); |
|
2324 printf(" (cv_record).age = %d\n", |
|
2325 cv_record_20->age); |
|
2326 printf(" (cv_record).pdb_file_name = \"%s\"\n", |
|
2327 cv_record_20->pdb_file_name); |
|
2328 } else { |
|
2329 printf(" (cv_record) = "); |
|
2330 for (unsigned int cv_byte_index = 0; |
|
2331 cv_byte_index < cv_record_size; |
|
2332 ++cv_byte_index) { |
|
2333 printf("%02x", cv_record[cv_byte_index]); |
|
2334 } |
|
2335 printf("\n"); |
|
2336 } |
|
2337 } else { |
|
2338 printf(" (cv_record) = (null)\n"); |
|
2339 } |
|
2340 |
|
2341 const MDImageDebugMisc* misc_record = GetMiscRecord(NULL); |
|
2342 if (misc_record) { |
|
2343 printf(" (misc_record).data_type = 0x%x\n", |
|
2344 misc_record->data_type); |
|
2345 printf(" (misc_record).length = 0x%x\n", |
|
2346 misc_record->length); |
|
2347 printf(" (misc_record).unicode = %d\n", |
|
2348 misc_record->unicode); |
|
2349 // Don't bother printing the UTF-16, we don't really even expect to ever |
|
2350 // see this misc_record anyway. |
|
2351 if (misc_record->unicode) |
|
2352 printf(" (misc_record).data = \"%s\"\n", |
|
2353 misc_record->data); |
|
2354 else |
|
2355 printf(" (misc_record).data = (UTF-16)\n"); |
|
2356 } else { |
|
2357 printf(" (misc_record) = (null)\n"); |
|
2358 } |
|
2359 |
|
2360 printf(" (debug_file) = \"%s\"\n", debug_file().c_str()); |
|
2361 printf(" (debug_identifier) = \"%s\"\n", |
|
2362 debug_identifier().c_str()); |
|
2363 printf(" (version) = \"%s\"\n", version().c_str()); |
|
2364 printf("\n"); |
|
2365 } |
|
2366 |
|
2367 |
|
2368 // |
|
2369 // MinidumpModuleList |
|
2370 // |
|
2371 |
|
2372 |
|
2373 uint32_t MinidumpModuleList::max_modules_ = 1024; |
|
2374 |
|
2375 |
|
2376 MinidumpModuleList::MinidumpModuleList(Minidump* minidump) |
|
2377 : MinidumpStream(minidump), |
|
2378 range_map_(new RangeMap<uint64_t, unsigned int>()), |
|
2379 modules_(NULL), |
|
2380 module_count_(0) { |
|
2381 } |
|
2382 |
|
2383 |
|
2384 MinidumpModuleList::~MinidumpModuleList() { |
|
2385 delete range_map_; |
|
2386 delete modules_; |
|
2387 } |
|
2388 |
|
2389 |
|
2390 bool MinidumpModuleList::Read(uint32_t expected_size) { |
|
2391 // Invalidate cached data. |
|
2392 range_map_->Clear(); |
|
2393 delete modules_; |
|
2394 modules_ = NULL; |
|
2395 module_count_ = 0; |
|
2396 |
|
2397 valid_ = false; |
|
2398 |
|
2399 uint32_t module_count; |
|
2400 if (expected_size < sizeof(module_count)) { |
|
2401 BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " << |
|
2402 expected_size << " < " << sizeof(module_count); |
|
2403 return false; |
|
2404 } |
|
2405 if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) { |
|
2406 BPLOG(ERROR) << "MinidumpModuleList could not read module count"; |
|
2407 return false; |
|
2408 } |
|
2409 |
|
2410 if (minidump_->swap()) |
|
2411 Swap(&module_count); |
|
2412 |
|
2413 if (module_count > numeric_limits<uint32_t>::max() / MD_MODULE_SIZE) { |
|
2414 BPLOG(ERROR) << "MinidumpModuleList module count " << module_count << |
|
2415 " would cause multiplication overflow"; |
|
2416 return false; |
|
2417 } |
|
2418 |
|
2419 if (expected_size != sizeof(module_count) + |
|
2420 module_count * MD_MODULE_SIZE) { |
|
2421 // may be padded with 4 bytes on 64bit ABIs for alignment |
|
2422 if (expected_size == sizeof(module_count) + 4 + |
|
2423 module_count * MD_MODULE_SIZE) { |
|
2424 uint32_t useless; |
|
2425 if (!minidump_->ReadBytes(&useless, 4)) { |
|
2426 BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded bytes"; |
|
2427 return false; |
|
2428 } |
|
2429 } else { |
|
2430 BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << |
|
2431 " != " << sizeof(module_count) + |
|
2432 module_count * MD_MODULE_SIZE; |
|
2433 return false; |
|
2434 } |
|
2435 } |
|
2436 |
|
2437 if (module_count > max_modules_) { |
|
2438 BPLOG(ERROR) << "MinidumpModuleList count " << module_count_ << |
|
2439 " exceeds maximum " << max_modules_; |
|
2440 return false; |
|
2441 } |
|
2442 |
|
2443 if (module_count != 0) { |
|
2444 scoped_ptr<MinidumpModules> modules( |
|
2445 new MinidumpModules(module_count, MinidumpModule(minidump_))); |
|
2446 |
|
2447 for (unsigned int module_index = 0; |
|
2448 module_index < module_count; |
|
2449 ++module_index) { |
|
2450 MinidumpModule* module = &(*modules)[module_index]; |
|
2451 |
|
2452 // Assume that the file offset is correct after the last read. |
|
2453 if (!module->Read()) { |
|
2454 BPLOG(ERROR) << "MinidumpModuleList could not read module " << |
|
2455 module_index << "/" << module_count; |
|
2456 return false; |
|
2457 } |
|
2458 } |
|
2459 |
|
2460 // Loop through the module list once more to read additional data and |
|
2461 // build the range map. This is done in a second pass because |
|
2462 // MinidumpModule::ReadAuxiliaryData seeks around, and if it were |
|
2463 // included in the loop above, additional seeks would be needed where |
|
2464 // none are now to read contiguous data. |
|
2465 for (unsigned int module_index = 0; |
|
2466 module_index < module_count; |
|
2467 ++module_index) { |
|
2468 MinidumpModule* module = &(*modules)[module_index]; |
|
2469 |
|
2470 // ReadAuxiliaryData fails if any data that the module indicates should |
|
2471 // exist is missing, but we treat some such cases as valid anyway. See |
|
2472 // issue #222: if a debugging record is of a format that's too large to |
|
2473 // handle, it shouldn't render the entire dump invalid. Check module |
|
2474 // validity before giving up. |
|
2475 if (!module->ReadAuxiliaryData() && !module->valid()) { |
|
2476 BPLOG(ERROR) << "MinidumpModuleList could not read required module " |
|
2477 "auxiliary data for module " << |
|
2478 module_index << "/" << module_count; |
|
2479 return false; |
|
2480 } |
|
2481 |
|
2482 // It is safe to use module->code_file() after successfully calling |
|
2483 // module->ReadAuxiliaryData or noting that the module is valid. |
|
2484 |
|
2485 uint64_t base_address = module->base_address(); |
|
2486 uint64_t module_size = module->size(); |
|
2487 if (base_address == static_cast<uint64_t>(-1)) { |
|
2488 BPLOG(ERROR) << "MinidumpModuleList found bad base address " |
|
2489 "for module " << module_index << "/" << module_count << |
|
2490 ", " << module->code_file(); |
|
2491 return false; |
|
2492 } |
|
2493 |
|
2494 if (!range_map_->StoreRange(base_address, module_size, module_index)) { |
|
2495 BPLOG(ERROR) << "MinidumpModuleList could not store module " << |
|
2496 module_index << "/" << module_count << ", " << |
|
2497 module->code_file() << ", " << |
|
2498 HexString(base_address) << "+" << |
|
2499 HexString(module_size); |
|
2500 return false; |
|
2501 } |
|
2502 } |
|
2503 |
|
2504 modules_ = modules.release(); |
|
2505 } |
|
2506 |
|
2507 module_count_ = module_count; |
|
2508 |
|
2509 valid_ = true; |
|
2510 return true; |
|
2511 } |
|
2512 |
|
2513 |
|
2514 const MinidumpModule* MinidumpModuleList::GetModuleForAddress( |
|
2515 uint64_t address) const { |
|
2516 if (!valid_) { |
|
2517 BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress"; |
|
2518 return NULL; |
|
2519 } |
|
2520 |
|
2521 unsigned int module_index; |
|
2522 if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) { |
|
2523 BPLOG(INFO) << "MinidumpModuleList has no module at " << |
|
2524 HexString(address); |
|
2525 return NULL; |
|
2526 } |
|
2527 |
|
2528 return GetModuleAtIndex(module_index); |
|
2529 } |
|
2530 |
|
2531 |
|
2532 const MinidumpModule* MinidumpModuleList::GetMainModule() const { |
|
2533 if (!valid_) { |
|
2534 BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule"; |
|
2535 return NULL; |
|
2536 } |
|
2537 |
|
2538 // The main code module is the first one present in a minidump file's |
|
2539 // MDRawModuleList. |
|
2540 return GetModuleAtIndex(0); |
|
2541 } |
|
2542 |
|
2543 |
|
2544 const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( |
|
2545 unsigned int sequence) const { |
|
2546 if (!valid_) { |
|
2547 BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence"; |
|
2548 return NULL; |
|
2549 } |
|
2550 |
|
2551 if (sequence >= module_count_) { |
|
2552 BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " << |
|
2553 sequence << "/" << module_count_; |
|
2554 return NULL; |
|
2555 } |
|
2556 |
|
2557 unsigned int module_index; |
|
2558 if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) { |
|
2559 BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence; |
|
2560 return NULL; |
|
2561 } |
|
2562 |
|
2563 return GetModuleAtIndex(module_index); |
|
2564 } |
|
2565 |
|
2566 |
|
2567 const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( |
|
2568 unsigned int index) const { |
|
2569 if (!valid_) { |
|
2570 BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex"; |
|
2571 return NULL; |
|
2572 } |
|
2573 |
|
2574 if (index >= module_count_) { |
|
2575 BPLOG(ERROR) << "MinidumpModuleList index out of range: " << |
|
2576 index << "/" << module_count_; |
|
2577 return NULL; |
|
2578 } |
|
2579 |
|
2580 return &(*modules_)[index]; |
|
2581 } |
|
2582 |
|
2583 |
|
2584 const CodeModules* MinidumpModuleList::Copy() const { |
|
2585 return new BasicCodeModules(this); |
|
2586 } |
|
2587 |
|
2588 |
|
2589 void MinidumpModuleList::Print() { |
|
2590 if (!valid_) { |
|
2591 BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data"; |
|
2592 return; |
|
2593 } |
|
2594 |
|
2595 printf("MinidumpModuleList\n"); |
|
2596 printf(" module_count = %d\n", module_count_); |
|
2597 printf("\n"); |
|
2598 |
|
2599 for (unsigned int module_index = 0; |
|
2600 module_index < module_count_; |
|
2601 ++module_index) { |
|
2602 printf("module[%d]\n", module_index); |
|
2603 |
|
2604 (*modules_)[module_index].Print(); |
|
2605 } |
|
2606 } |
|
2607 |
|
2608 |
|
2609 // |
|
2610 // MinidumpMemoryList |
|
2611 // |
|
2612 |
|
2613 |
|
2614 uint32_t MinidumpMemoryList::max_regions_ = 4096; |
|
2615 |
|
2616 |
|
2617 MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) |
|
2618 : MinidumpStream(minidump), |
|
2619 range_map_(new RangeMap<uint64_t, unsigned int>()), |
|
2620 descriptors_(NULL), |
|
2621 regions_(NULL), |
|
2622 region_count_(0) { |
|
2623 } |
|
2624 |
|
2625 |
|
2626 MinidumpMemoryList::~MinidumpMemoryList() { |
|
2627 delete range_map_; |
|
2628 delete descriptors_; |
|
2629 delete regions_; |
|
2630 } |
|
2631 |
|
2632 |
|
2633 bool MinidumpMemoryList::Read(uint32_t expected_size) { |
|
2634 // Invalidate cached data. |
|
2635 delete descriptors_; |
|
2636 descriptors_ = NULL; |
|
2637 delete regions_; |
|
2638 regions_ = NULL; |
|
2639 range_map_->Clear(); |
|
2640 region_count_ = 0; |
|
2641 |
|
2642 valid_ = false; |
|
2643 |
|
2644 uint32_t region_count; |
|
2645 if (expected_size < sizeof(region_count)) { |
|
2646 BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " << |
|
2647 expected_size << " < " << sizeof(region_count); |
|
2648 return false; |
|
2649 } |
|
2650 if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) { |
|
2651 BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count"; |
|
2652 return false; |
|
2653 } |
|
2654 |
|
2655 if (minidump_->swap()) |
|
2656 Swap(®ion_count); |
|
2657 |
|
2658 if (region_count > |
|
2659 numeric_limits<uint32_t>::max() / sizeof(MDMemoryDescriptor)) { |
|
2660 BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count << |
|
2661 " would cause multiplication overflow"; |
|
2662 return false; |
|
2663 } |
|
2664 |
|
2665 if (expected_size != sizeof(region_count) + |
|
2666 region_count * sizeof(MDMemoryDescriptor)) { |
|
2667 // may be padded with 4 bytes on 64bit ABIs for alignment |
|
2668 if (expected_size == sizeof(region_count) + 4 + |
|
2669 region_count * sizeof(MDMemoryDescriptor)) { |
|
2670 uint32_t useless; |
|
2671 if (!minidump_->ReadBytes(&useless, 4)) { |
|
2672 BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded bytes"; |
|
2673 return false; |
|
2674 } |
|
2675 } else { |
|
2676 BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << |
|
2677 " != " << sizeof(region_count) + |
|
2678 region_count * sizeof(MDMemoryDescriptor); |
|
2679 return false; |
|
2680 } |
|
2681 } |
|
2682 |
|
2683 if (region_count > max_regions_) { |
|
2684 BPLOG(ERROR) << "MinidumpMemoryList count " << region_count << |
|
2685 " exceeds maximum " << max_regions_; |
|
2686 return false; |
|
2687 } |
|
2688 |
|
2689 if (region_count != 0) { |
|
2690 scoped_ptr<MemoryDescriptors> descriptors( |
|
2691 new MemoryDescriptors(region_count)); |
|
2692 |
|
2693 // Read the entire array in one fell swoop, instead of reading one entry |
|
2694 // at a time in the loop. |
|
2695 if (!minidump_->ReadBytes(&(*descriptors)[0], |
|
2696 sizeof(MDMemoryDescriptor) * region_count)) { |
|
2697 BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list"; |
|
2698 return false; |
|
2699 } |
|
2700 |
|
2701 scoped_ptr<MemoryRegions> regions( |
|
2702 new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_))); |
|
2703 |
|
2704 for (unsigned int region_index = 0; |
|
2705 region_index < region_count; |
|
2706 ++region_index) { |
|
2707 MDMemoryDescriptor* descriptor = &(*descriptors)[region_index]; |
|
2708 |
|
2709 if (minidump_->swap()) |
|
2710 Swap(descriptor); |
|
2711 |
|
2712 uint64_t base_address = descriptor->start_of_memory_range; |
|
2713 uint32_t region_size = descriptor->memory.data_size; |
|
2714 |
|
2715 // Check for base + size overflow or undersize. |
|
2716 if (region_size == 0 || |
|
2717 region_size > numeric_limits<uint64_t>::max() - base_address) { |
|
2718 BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " << |
|
2719 " region " << region_index << "/" << region_count << |
|
2720 ", " << HexString(base_address) << "+" << |
|
2721 HexString(region_size); |
|
2722 return false; |
|
2723 } |
|
2724 |
|
2725 if (!range_map_->StoreRange(base_address, region_size, region_index)) { |
|
2726 BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " << |
|
2727 region_index << "/" << region_count << ", " << |
|
2728 HexString(base_address) << "+" << |
|
2729 HexString(region_size); |
|
2730 return false; |
|
2731 } |
|
2732 |
|
2733 (*regions)[region_index].SetDescriptor(descriptor); |
|
2734 } |
|
2735 |
|
2736 descriptors_ = descriptors.release(); |
|
2737 regions_ = regions.release(); |
|
2738 } |
|
2739 |
|
2740 region_count_ = region_count; |
|
2741 |
|
2742 valid_ = true; |
|
2743 return true; |
|
2744 } |
|
2745 |
|
2746 |
|
2747 MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex( |
|
2748 unsigned int index) { |
|
2749 if (!valid_) { |
|
2750 BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex"; |
|
2751 return NULL; |
|
2752 } |
|
2753 |
|
2754 if (index >= region_count_) { |
|
2755 BPLOG(ERROR) << "MinidumpMemoryList index out of range: " << |
|
2756 index << "/" << region_count_; |
|
2757 return NULL; |
|
2758 } |
|
2759 |
|
2760 return &(*regions_)[index]; |
|
2761 } |
|
2762 |
|
2763 |
|
2764 MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress( |
|
2765 uint64_t address) { |
|
2766 if (!valid_) { |
|
2767 BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress"; |
|
2768 return NULL; |
|
2769 } |
|
2770 |
|
2771 unsigned int region_index; |
|
2772 if (!range_map_->RetrieveRange(address, ®ion_index, NULL, NULL)) { |
|
2773 BPLOG(INFO) << "MinidumpMemoryList has no memory region at " << |
|
2774 HexString(address); |
|
2775 return NULL; |
|
2776 } |
|
2777 |
|
2778 return GetMemoryRegionAtIndex(region_index); |
|
2779 } |
|
2780 |
|
2781 |
|
2782 void MinidumpMemoryList::Print() { |
|
2783 if (!valid_) { |
|
2784 BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data"; |
|
2785 return; |
|
2786 } |
|
2787 |
|
2788 printf("MinidumpMemoryList\n"); |
|
2789 printf(" region_count = %d\n", region_count_); |
|
2790 printf("\n"); |
|
2791 |
|
2792 for (unsigned int region_index = 0; |
|
2793 region_index < region_count_; |
|
2794 ++region_index) { |
|
2795 MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index]; |
|
2796 printf("region[%d]\n", region_index); |
|
2797 printf("MDMemoryDescriptor\n"); |
|
2798 printf(" start_of_memory_range = 0x%" PRIx64 "\n", |
|
2799 descriptor->start_of_memory_range); |
|
2800 printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size); |
|
2801 printf(" memory.rva = 0x%x\n", descriptor->memory.rva); |
|
2802 MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index); |
|
2803 if (region) { |
|
2804 printf("Memory\n"); |
|
2805 region->Print(); |
|
2806 } else { |
|
2807 printf("No memory\n"); |
|
2808 } |
|
2809 printf("\n"); |
|
2810 } |
|
2811 } |
|
2812 |
|
2813 |
|
2814 // |
|
2815 // MinidumpException |
|
2816 // |
|
2817 |
|
2818 |
|
2819 MinidumpException::MinidumpException(Minidump* minidump) |
|
2820 : MinidumpStream(minidump), |
|
2821 exception_(), |
|
2822 context_(NULL) { |
|
2823 } |
|
2824 |
|
2825 |
|
2826 MinidumpException::~MinidumpException() { |
|
2827 delete context_; |
|
2828 } |
|
2829 |
|
2830 |
|
2831 bool MinidumpException::Read(uint32_t expected_size) { |
|
2832 // Invalidate cached data. |
|
2833 delete context_; |
|
2834 context_ = NULL; |
|
2835 |
|
2836 valid_ = false; |
|
2837 |
|
2838 if (expected_size != sizeof(exception_)) { |
|
2839 BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size << |
|
2840 " != " << sizeof(exception_); |
|
2841 return false; |
|
2842 } |
|
2843 |
|
2844 if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) { |
|
2845 BPLOG(ERROR) << "MinidumpException cannot read exception"; |
|
2846 return false; |
|
2847 } |
|
2848 |
|
2849 if (minidump_->swap()) { |
|
2850 Swap(&exception_.thread_id); |
|
2851 // exception_.__align is for alignment only and does not need to be |
|
2852 // swapped. |
|
2853 Swap(&exception_.exception_record.exception_code); |
|
2854 Swap(&exception_.exception_record.exception_flags); |
|
2855 Swap(&exception_.exception_record.exception_record); |
|
2856 Swap(&exception_.exception_record.exception_address); |
|
2857 Swap(&exception_.exception_record.number_parameters); |
|
2858 // exception_.exception_record.__align is for alignment only and does not |
|
2859 // need to be swapped. |
|
2860 for (unsigned int parameter_index = 0; |
|
2861 parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS; |
|
2862 ++parameter_index) { |
|
2863 Swap(&exception_.exception_record.exception_information[parameter_index]); |
|
2864 } |
|
2865 Swap(&exception_.thread_context); |
|
2866 } |
|
2867 |
|
2868 valid_ = true; |
|
2869 return true; |
|
2870 } |
|
2871 |
|
2872 |
|
2873 bool MinidumpException::GetThreadID(uint32_t *thread_id) const { |
|
2874 BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires " |
|
2875 "|thread_id|"; |
|
2876 assert(thread_id); |
|
2877 *thread_id = 0; |
|
2878 |
|
2879 if (!valid_) { |
|
2880 BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID"; |
|
2881 return false; |
|
2882 } |
|
2883 |
|
2884 *thread_id = exception_.thread_id; |
|
2885 return true; |
|
2886 } |
|
2887 |
|
2888 |
|
2889 MinidumpContext* MinidumpException::GetContext() { |
|
2890 if (!valid_) { |
|
2891 BPLOG(ERROR) << "Invalid MinidumpException for GetContext"; |
|
2892 return NULL; |
|
2893 } |
|
2894 |
|
2895 if (!context_) { |
|
2896 if (!minidump_->SeekSet(exception_.thread_context.rva)) { |
|
2897 BPLOG(ERROR) << "MinidumpException cannot seek to context"; |
|
2898 return NULL; |
|
2899 } |
|
2900 |
|
2901 scoped_ptr<MinidumpContext> context(new MinidumpContext(minidump_)); |
|
2902 |
|
2903 // Don't log as an error if we can still fall back on the thread's context |
|
2904 // (which must be possible if we got this far.) |
|
2905 if (!context->Read(exception_.thread_context.data_size)) { |
|
2906 BPLOG(INFO) << "MinidumpException cannot read context"; |
|
2907 return NULL; |
|
2908 } |
|
2909 |
|
2910 context_ = context.release(); |
|
2911 } |
|
2912 |
|
2913 return context_; |
|
2914 } |
|
2915 |
|
2916 |
|
2917 void MinidumpException::Print() { |
|
2918 if (!valid_) { |
|
2919 BPLOG(ERROR) << "MinidumpException cannot print invalid data"; |
|
2920 return; |
|
2921 } |
|
2922 |
|
2923 printf("MDException\n"); |
|
2924 printf(" thread_id = 0x%x\n", |
|
2925 exception_.thread_id); |
|
2926 printf(" exception_record.exception_code = 0x%x\n", |
|
2927 exception_.exception_record.exception_code); |
|
2928 printf(" exception_record.exception_flags = 0x%x\n", |
|
2929 exception_.exception_record.exception_flags); |
|
2930 printf(" exception_record.exception_record = 0x%" PRIx64 "\n", |
|
2931 exception_.exception_record.exception_record); |
|
2932 printf(" exception_record.exception_address = 0x%" PRIx64 "\n", |
|
2933 exception_.exception_record.exception_address); |
|
2934 printf(" exception_record.number_parameters = %d\n", |
|
2935 exception_.exception_record.number_parameters); |
|
2936 for (unsigned int parameterIndex = 0; |
|
2937 parameterIndex < exception_.exception_record.number_parameters; |
|
2938 ++parameterIndex) { |
|
2939 printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n", |
|
2940 parameterIndex, |
|
2941 exception_.exception_record.exception_information[parameterIndex]); |
|
2942 } |
|
2943 printf(" thread_context.data_size = %d\n", |
|
2944 exception_.thread_context.data_size); |
|
2945 printf(" thread_context.rva = 0x%x\n", |
|
2946 exception_.thread_context.rva); |
|
2947 MinidumpContext* context = GetContext(); |
|
2948 if (context) { |
|
2949 printf("\n"); |
|
2950 context->Print(); |
|
2951 } else { |
|
2952 printf(" (no context)\n"); |
|
2953 printf("\n"); |
|
2954 } |
|
2955 } |
|
2956 |
|
2957 // |
|
2958 // MinidumpAssertion |
|
2959 // |
|
2960 |
|
2961 |
|
2962 MinidumpAssertion::MinidumpAssertion(Minidump* minidump) |
|
2963 : MinidumpStream(minidump), |
|
2964 assertion_(), |
|
2965 expression_(), |
|
2966 function_(), |
|
2967 file_() { |
|
2968 } |
|
2969 |
|
2970 |
|
2971 MinidumpAssertion::~MinidumpAssertion() { |
|
2972 } |
|
2973 |
|
2974 |
|
2975 bool MinidumpAssertion::Read(uint32_t expected_size) { |
|
2976 // Invalidate cached data. |
|
2977 valid_ = false; |
|
2978 |
|
2979 if (expected_size != sizeof(assertion_)) { |
|
2980 BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << |
|
2981 " != " << sizeof(assertion_); |
|
2982 return false; |
|
2983 } |
|
2984 |
|
2985 if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { |
|
2986 BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; |
|
2987 return false; |
|
2988 } |
|
2989 |
|
2990 // Each of {expression, function, file} is a UTF-16 string, |
|
2991 // we'll convert them to UTF-8 for ease of use. |
|
2992 // expression |
|
2993 // Since we don't have an explicit byte length for each string, |
|
2994 // we use UTF16codeunits to calculate word length, then derive byte |
|
2995 // length from that. |
|
2996 uint32_t word_length = UTF16codeunits(assertion_.expression, |
|
2997 sizeof(assertion_.expression)); |
|
2998 if (word_length > 0) { |
|
2999 uint32_t byte_length = word_length * 2; |
|
3000 vector<uint16_t> expression_utf16(word_length); |
|
3001 memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); |
|
3002 |
|
3003 scoped_ptr<string> new_expression(UTF16ToUTF8(expression_utf16, |
|
3004 minidump_->swap())); |
|
3005 if (new_expression.get()) |
|
3006 expression_ = *new_expression; |
|
3007 } |
|
3008 |
|
3009 // assertion |
|
3010 word_length = UTF16codeunits(assertion_.function, |
|
3011 sizeof(assertion_.function)); |
|
3012 if (word_length) { |
|
3013 uint32_t byte_length = word_length * 2; |
|
3014 vector<uint16_t> function_utf16(word_length); |
|
3015 memcpy(&function_utf16[0], &assertion_.function[0], byte_length); |
|
3016 scoped_ptr<string> new_function(UTF16ToUTF8(function_utf16, |
|
3017 minidump_->swap())); |
|
3018 if (new_function.get()) |
|
3019 function_ = *new_function; |
|
3020 } |
|
3021 |
|
3022 // file |
|
3023 word_length = UTF16codeunits(assertion_.file, |
|
3024 sizeof(assertion_.file)); |
|
3025 if (word_length > 0) { |
|
3026 uint32_t byte_length = word_length * 2; |
|
3027 vector<uint16_t> file_utf16(word_length); |
|
3028 memcpy(&file_utf16[0], &assertion_.file[0], byte_length); |
|
3029 scoped_ptr<string> new_file(UTF16ToUTF8(file_utf16, |
|
3030 minidump_->swap())); |
|
3031 if (new_file.get()) |
|
3032 file_ = *new_file; |
|
3033 } |
|
3034 |
|
3035 if (minidump_->swap()) { |
|
3036 Swap(&assertion_.line); |
|
3037 Swap(&assertion_.type); |
|
3038 } |
|
3039 |
|
3040 valid_ = true; |
|
3041 return true; |
|
3042 } |
|
3043 |
|
3044 void MinidumpAssertion::Print() { |
|
3045 if (!valid_) { |
|
3046 BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; |
|
3047 return; |
|
3048 } |
|
3049 |
|
3050 printf("MDAssertion\n"); |
|
3051 printf(" expression = %s\n", |
|
3052 expression_.c_str()); |
|
3053 printf(" function = %s\n", |
|
3054 function_.c_str()); |
|
3055 printf(" file = %s\n", |
|
3056 file_.c_str()); |
|
3057 printf(" line = %u\n", |
|
3058 assertion_.line); |
|
3059 printf(" type = %u\n", |
|
3060 assertion_.type); |
|
3061 printf("\n"); |
|
3062 } |
|
3063 |
|
3064 // |
|
3065 // MinidumpSystemInfo |
|
3066 // |
|
3067 |
|
3068 |
|
3069 MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) |
|
3070 : MinidumpStream(minidump), |
|
3071 system_info_(), |
|
3072 csd_version_(NULL), |
|
3073 cpu_vendor_(NULL) { |
|
3074 } |
|
3075 |
|
3076 |
|
3077 MinidumpSystemInfo::~MinidumpSystemInfo() { |
|
3078 delete csd_version_; |
|
3079 delete cpu_vendor_; |
|
3080 } |
|
3081 |
|
3082 |
|
3083 bool MinidumpSystemInfo::Read(uint32_t expected_size) { |
|
3084 // Invalidate cached data. |
|
3085 delete csd_version_; |
|
3086 csd_version_ = NULL; |
|
3087 delete cpu_vendor_; |
|
3088 cpu_vendor_ = NULL; |
|
3089 |
|
3090 valid_ = false; |
|
3091 |
|
3092 if (expected_size != sizeof(system_info_)) { |
|
3093 BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size << |
|
3094 " != " << sizeof(system_info_); |
|
3095 return false; |
|
3096 } |
|
3097 |
|
3098 if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) { |
|
3099 BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info"; |
|
3100 return false; |
|
3101 } |
|
3102 |
|
3103 if (minidump_->swap()) { |
|
3104 Swap(&system_info_.processor_architecture); |
|
3105 Swap(&system_info_.processor_level); |
|
3106 Swap(&system_info_.processor_revision); |
|
3107 // number_of_processors and product_type are 8-bit quantities and need no |
|
3108 // swapping. |
|
3109 Swap(&system_info_.major_version); |
|
3110 Swap(&system_info_.minor_version); |
|
3111 Swap(&system_info_.build_number); |
|
3112 Swap(&system_info_.platform_id); |
|
3113 Swap(&system_info_.csd_version_rva); |
|
3114 Swap(&system_info_.suite_mask); |
|
3115 // Don't swap the reserved2 field because its contents are unknown. |
|
3116 |
|
3117 if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || |
|
3118 system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) { |
|
3119 for (unsigned int i = 0; i < 3; ++i) |
|
3120 Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]); |
|
3121 Swap(&system_info_.cpu.x86_cpu_info.version_information); |
|
3122 Swap(&system_info_.cpu.x86_cpu_info.feature_information); |
|
3123 Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); |
|
3124 } else { |
|
3125 for (unsigned int i = 0; i < 2; ++i) |
|
3126 Swap(&system_info_.cpu.other_cpu_info.processor_features[i]); |
|
3127 } |
|
3128 } |
|
3129 |
|
3130 valid_ = true; |
|
3131 return true; |
|
3132 } |
|
3133 |
|
3134 |
|
3135 string MinidumpSystemInfo::GetOS() { |
|
3136 string os; |
|
3137 |
|
3138 if (!valid_) { |
|
3139 BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS"; |
|
3140 return os; |
|
3141 } |
|
3142 |
|
3143 switch (system_info_.platform_id) { |
|
3144 case MD_OS_WIN32_NT: |
|
3145 case MD_OS_WIN32_WINDOWS: |
|
3146 os = "windows"; |
|
3147 break; |
|
3148 |
|
3149 case MD_OS_MAC_OS_X: |
|
3150 os = "mac"; |
|
3151 break; |
|
3152 |
|
3153 case MD_OS_IOS: |
|
3154 os = "ios"; |
|
3155 break; |
|
3156 |
|
3157 case MD_OS_LINUX: |
|
3158 os = "linux"; |
|
3159 break; |
|
3160 |
|
3161 case MD_OS_SOLARIS: |
|
3162 os = "solaris"; |
|
3163 break; |
|
3164 |
|
3165 case MD_OS_ANDROID: |
|
3166 os = "android"; |
|
3167 break; |
|
3168 |
|
3169 default: |
|
3170 BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " << |
|
3171 HexString(system_info_.platform_id); |
|
3172 break; |
|
3173 } |
|
3174 |
|
3175 return os; |
|
3176 } |
|
3177 |
|
3178 |
|
3179 string MinidumpSystemInfo::GetCPU() { |
|
3180 if (!valid_) { |
|
3181 BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU"; |
|
3182 return ""; |
|
3183 } |
|
3184 |
|
3185 string cpu; |
|
3186 |
|
3187 switch (system_info_.processor_architecture) { |
|
3188 case MD_CPU_ARCHITECTURE_X86: |
|
3189 case MD_CPU_ARCHITECTURE_X86_WIN64: |
|
3190 cpu = "x86"; |
|
3191 break; |
|
3192 |
|
3193 case MD_CPU_ARCHITECTURE_AMD64: |
|
3194 cpu = "x86-64"; |
|
3195 break; |
|
3196 |
|
3197 case MD_CPU_ARCHITECTURE_PPC: |
|
3198 cpu = "ppc"; |
|
3199 break; |
|
3200 |
|
3201 case MD_CPU_ARCHITECTURE_SPARC: |
|
3202 cpu = "sparc"; |
|
3203 break; |
|
3204 |
|
3205 case MD_CPU_ARCHITECTURE_ARM: |
|
3206 cpu = "arm"; |
|
3207 break; |
|
3208 |
|
3209 default: |
|
3210 BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << |
|
3211 HexString(system_info_.processor_architecture); |
|
3212 break; |
|
3213 } |
|
3214 |
|
3215 return cpu; |
|
3216 } |
|
3217 |
|
3218 |
|
3219 const string* MinidumpSystemInfo::GetCSDVersion() { |
|
3220 if (!valid_) { |
|
3221 BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion"; |
|
3222 return NULL; |
|
3223 } |
|
3224 |
|
3225 if (!csd_version_) |
|
3226 csd_version_ = minidump_->ReadString(system_info_.csd_version_rva); |
|
3227 |
|
3228 BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read " |
|
3229 "CSD version"; |
|
3230 |
|
3231 return csd_version_; |
|
3232 } |
|
3233 |
|
3234 |
|
3235 const string* MinidumpSystemInfo::GetCPUVendor() { |
|
3236 if (!valid_) { |
|
3237 BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor"; |
|
3238 return NULL; |
|
3239 } |
|
3240 |
|
3241 // CPU vendor information can only be determined from x86 minidumps. |
|
3242 if (!cpu_vendor_ && |
|
3243 (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || |
|
3244 system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) { |
|
3245 char cpu_vendor_string[13]; |
|
3246 snprintf(cpu_vendor_string, sizeof(cpu_vendor_string), |
|
3247 "%c%c%c%c%c%c%c%c%c%c%c%c", |
|
3248 system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff, |
|
3249 (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff, |
|
3250 (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff, |
|
3251 (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff, |
|
3252 system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff, |
|
3253 (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff, |
|
3254 (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff, |
|
3255 (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff, |
|
3256 system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff, |
|
3257 (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff, |
|
3258 (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff, |
|
3259 (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff); |
|
3260 cpu_vendor_ = new string(cpu_vendor_string); |
|
3261 } |
|
3262 |
|
3263 return cpu_vendor_; |
|
3264 } |
|
3265 |
|
3266 |
|
3267 void MinidumpSystemInfo::Print() { |
|
3268 if (!valid_) { |
|
3269 BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data"; |
|
3270 return; |
|
3271 } |
|
3272 |
|
3273 printf("MDRawSystemInfo\n"); |
|
3274 printf(" processor_architecture = %d\n", |
|
3275 system_info_.processor_architecture); |
|
3276 printf(" processor_level = %d\n", |
|
3277 system_info_.processor_level); |
|
3278 printf(" processor_revision = 0x%x\n", |
|
3279 system_info_.processor_revision); |
|
3280 printf(" number_of_processors = %d\n", |
|
3281 system_info_.number_of_processors); |
|
3282 printf(" product_type = %d\n", |
|
3283 system_info_.product_type); |
|
3284 printf(" major_version = %d\n", |
|
3285 system_info_.major_version); |
|
3286 printf(" minor_version = %d\n", |
|
3287 system_info_.minor_version); |
|
3288 printf(" build_number = %d\n", |
|
3289 system_info_.build_number); |
|
3290 printf(" platform_id = %d\n", |
|
3291 system_info_.platform_id); |
|
3292 printf(" csd_version_rva = 0x%x\n", |
|
3293 system_info_.csd_version_rva); |
|
3294 printf(" suite_mask = 0x%x\n", |
|
3295 system_info_.suite_mask); |
|
3296 for (unsigned int i = 0; i < 3; ++i) { |
|
3297 printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n", |
|
3298 i, system_info_.cpu.x86_cpu_info.vendor_id[i]); |
|
3299 } |
|
3300 printf(" cpu.x86_cpu_info.version_information = 0x%x\n", |
|
3301 system_info_.cpu.x86_cpu_info.version_information); |
|
3302 printf(" cpu.x86_cpu_info.feature_information = 0x%x\n", |
|
3303 system_info_.cpu.x86_cpu_info.feature_information); |
|
3304 printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n", |
|
3305 system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); |
|
3306 const string* csd_version = GetCSDVersion(); |
|
3307 if (csd_version) { |
|
3308 printf(" (csd_version) = \"%s\"\n", |
|
3309 csd_version->c_str()); |
|
3310 } else { |
|
3311 printf(" (csd_version) = (null)\n"); |
|
3312 } |
|
3313 const string* cpu_vendor = GetCPUVendor(); |
|
3314 if (cpu_vendor) { |
|
3315 printf(" (cpu_vendor) = \"%s\"\n", |
|
3316 cpu_vendor->c_str()); |
|
3317 } else { |
|
3318 printf(" (cpu_vendor) = (null)\n"); |
|
3319 } |
|
3320 printf("\n"); |
|
3321 } |
|
3322 |
|
3323 |
|
3324 // |
|
3325 // MinidumpMiscInfo |
|
3326 // |
|
3327 |
|
3328 |
|
3329 MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump) |
|
3330 : MinidumpStream(minidump), |
|
3331 misc_info_() { |
|
3332 } |
|
3333 |
|
3334 |
|
3335 bool MinidumpMiscInfo::Read(uint32_t expected_size) { |
|
3336 valid_ = false; |
|
3337 |
|
3338 if (expected_size != MD_MISCINFO_SIZE && |
|
3339 expected_size != MD_MISCINFO2_SIZE) { |
|
3340 BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size << |
|
3341 " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE << |
|
3342 ")"; |
|
3343 return false; |
|
3344 } |
|
3345 |
|
3346 if (!minidump_->ReadBytes(&misc_info_, expected_size)) { |
|
3347 BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info"; |
|
3348 return false; |
|
3349 } |
|
3350 |
|
3351 if (minidump_->swap()) { |
|
3352 Swap(&misc_info_.size_of_info); |
|
3353 Swap(&misc_info_.flags1); |
|
3354 Swap(&misc_info_.process_id); |
|
3355 Swap(&misc_info_.process_create_time); |
|
3356 Swap(&misc_info_.process_user_time); |
|
3357 Swap(&misc_info_.process_kernel_time); |
|
3358 if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { |
|
3359 Swap(&misc_info_.processor_max_mhz); |
|
3360 Swap(&misc_info_.processor_current_mhz); |
|
3361 Swap(&misc_info_.processor_mhz_limit); |
|
3362 Swap(&misc_info_.processor_max_idle_state); |
|
3363 Swap(&misc_info_.processor_current_idle_state); |
|
3364 } |
|
3365 } |
|
3366 |
|
3367 if (expected_size != misc_info_.size_of_info) { |
|
3368 BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << |
|
3369 expected_size << " != " << misc_info_.size_of_info; |
|
3370 return false; |
|
3371 } |
|
3372 |
|
3373 valid_ = true; |
|
3374 return true; |
|
3375 } |
|
3376 |
|
3377 |
|
3378 void MinidumpMiscInfo::Print() { |
|
3379 if (!valid_) { |
|
3380 BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data"; |
|
3381 return; |
|
3382 } |
|
3383 |
|
3384 printf("MDRawMiscInfo\n"); |
|
3385 printf(" size_of_info = %d\n", misc_info_.size_of_info); |
|
3386 printf(" flags1 = 0x%x\n", misc_info_.flags1); |
|
3387 printf(" process_id = 0x%x\n", misc_info_.process_id); |
|
3388 printf(" process_create_time = 0x%x\n", |
|
3389 misc_info_.process_create_time); |
|
3390 printf(" process_user_time = 0x%x\n", |
|
3391 misc_info_.process_user_time); |
|
3392 printf(" process_kernel_time = 0x%x\n", |
|
3393 misc_info_.process_kernel_time); |
|
3394 if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { |
|
3395 printf(" processor_max_mhz = %d\n", |
|
3396 misc_info_.processor_max_mhz); |
|
3397 printf(" processor_current_mhz = %d\n", |
|
3398 misc_info_.processor_current_mhz); |
|
3399 printf(" processor_mhz_limit = %d\n", |
|
3400 misc_info_.processor_mhz_limit); |
|
3401 printf(" processor_max_idle_state = 0x%x\n", |
|
3402 misc_info_.processor_max_idle_state); |
|
3403 printf(" processor_current_idle_state = 0x%x\n", |
|
3404 misc_info_.processor_current_idle_state); |
|
3405 } |
|
3406 printf("\n"); |
|
3407 } |
|
3408 |
|
3409 |
|
3410 // |
|
3411 // MinidumpBreakpadInfo |
|
3412 // |
|
3413 |
|
3414 |
|
3415 MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump) |
|
3416 : MinidumpStream(minidump), |
|
3417 breakpad_info_() { |
|
3418 } |
|
3419 |
|
3420 |
|
3421 bool MinidumpBreakpadInfo::Read(uint32_t expected_size) { |
|
3422 valid_ = false; |
|
3423 |
|
3424 if (expected_size != sizeof(breakpad_info_)) { |
|
3425 BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size << |
|
3426 " != " << sizeof(breakpad_info_); |
|
3427 return false; |
|
3428 } |
|
3429 |
|
3430 if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) { |
|
3431 BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info"; |
|
3432 return false; |
|
3433 } |
|
3434 |
|
3435 if (minidump_->swap()) { |
|
3436 Swap(&breakpad_info_.validity); |
|
3437 Swap(&breakpad_info_.dump_thread_id); |
|
3438 Swap(&breakpad_info_.requesting_thread_id); |
|
3439 } |
|
3440 |
|
3441 valid_ = true; |
|
3442 return true; |
|
3443 } |
|
3444 |
|
3445 |
|
3446 bool MinidumpBreakpadInfo::GetDumpThreadID(uint32_t *thread_id) const { |
|
3447 BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID " |
|
3448 "requires |thread_id|"; |
|
3449 assert(thread_id); |
|
3450 *thread_id = 0; |
|
3451 |
|
3452 if (!valid_) { |
|
3453 BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID"; |
|
3454 return false; |
|
3455 } |
|
3456 |
|
3457 if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) { |
|
3458 BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread"; |
|
3459 return false; |
|
3460 } |
|
3461 |
|
3462 *thread_id = breakpad_info_.dump_thread_id; |
|
3463 return true; |
|
3464 } |
|
3465 |
|
3466 |
|
3467 bool MinidumpBreakpadInfo::GetRequestingThreadID(uint32_t *thread_id) |
|
3468 const { |
|
3469 BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID " |
|
3470 "requires |thread_id|"; |
|
3471 assert(thread_id); |
|
3472 *thread_id = 0; |
|
3473 |
|
3474 if (!thread_id || !valid_) { |
|
3475 BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID"; |
|
3476 return false; |
|
3477 } |
|
3478 |
|
3479 if (!(breakpad_info_.validity & |
|
3480 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) { |
|
3481 BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread"; |
|
3482 return false; |
|
3483 } |
|
3484 |
|
3485 *thread_id = breakpad_info_.requesting_thread_id; |
|
3486 return true; |
|
3487 } |
|
3488 |
|
3489 |
|
3490 void MinidumpBreakpadInfo::Print() { |
|
3491 if (!valid_) { |
|
3492 BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data"; |
|
3493 return; |
|
3494 } |
|
3495 |
|
3496 printf("MDRawBreakpadInfo\n"); |
|
3497 printf(" validity = 0x%x\n", breakpad_info_.validity); |
|
3498 |
|
3499 if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { |
|
3500 printf(" dump_thread_id = 0x%x\n", breakpad_info_.dump_thread_id); |
|
3501 } else { |
|
3502 printf(" dump_thread_id = (invalid)\n"); |
|
3503 } |
|
3504 |
|
3505 if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { |
|
3506 printf(" requesting_thread_id = 0x%x\n", |
|
3507 breakpad_info_.requesting_thread_id); |
|
3508 } else { |
|
3509 printf(" requesting_thread_id = (invalid)\n"); |
|
3510 } |
|
3511 |
|
3512 printf("\n"); |
|
3513 } |
|
3514 |
|
3515 |
|
3516 // |
|
3517 // MinidumpMemoryInfo |
|
3518 // |
|
3519 |
|
3520 |
|
3521 MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump) |
|
3522 : MinidumpObject(minidump), |
|
3523 memory_info_() { |
|
3524 } |
|
3525 |
|
3526 |
|
3527 bool MinidumpMemoryInfo::IsExecutable() const { |
|
3528 uint32_t protection = |
|
3529 memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; |
|
3530 return protection == MD_MEMORY_PROTECT_EXECUTE || |
|
3531 protection == MD_MEMORY_PROTECT_EXECUTE_READ || |
|
3532 protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE; |
|
3533 } |
|
3534 |
|
3535 |
|
3536 bool MinidumpMemoryInfo::IsWritable() const { |
|
3537 uint32_t protection = |
|
3538 memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; |
|
3539 return protection == MD_MEMORY_PROTECT_READWRITE || |
|
3540 protection == MD_MEMORY_PROTECT_WRITECOPY || |
|
3541 protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE || |
|
3542 protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY; |
|
3543 } |
|
3544 |
|
3545 |
|
3546 bool MinidumpMemoryInfo::Read() { |
|
3547 valid_ = false; |
|
3548 |
|
3549 if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) { |
|
3550 BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info"; |
|
3551 return false; |
|
3552 } |
|
3553 |
|
3554 if (minidump_->swap()) { |
|
3555 Swap(&memory_info_.base_address); |
|
3556 Swap(&memory_info_.allocation_base); |
|
3557 Swap(&memory_info_.allocation_protection); |
|
3558 Swap(&memory_info_.region_size); |
|
3559 Swap(&memory_info_.state); |
|
3560 Swap(&memory_info_.protection); |
|
3561 Swap(&memory_info_.type); |
|
3562 } |
|
3563 |
|
3564 // Check for base + size overflow or undersize. |
|
3565 if (memory_info_.region_size == 0 || |
|
3566 memory_info_.region_size > numeric_limits<uint64_t>::max() - |
|
3567 memory_info_.base_address) { |
|
3568 BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " << |
|
3569 HexString(memory_info_.base_address) << "+" << |
|
3570 HexString(memory_info_.region_size); |
|
3571 return false; |
|
3572 } |
|
3573 |
|
3574 valid_ = true; |
|
3575 return true; |
|
3576 } |
|
3577 |
|
3578 |
|
3579 void MinidumpMemoryInfo::Print() { |
|
3580 if (!valid_) { |
|
3581 BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data"; |
|
3582 return; |
|
3583 } |
|
3584 |
|
3585 printf("MDRawMemoryInfo\n"); |
|
3586 printf(" base_address = 0x%" PRIx64 "\n", |
|
3587 memory_info_.base_address); |
|
3588 printf(" allocation_base = 0x%" PRIx64 "\n", |
|
3589 memory_info_.allocation_base); |
|
3590 printf(" allocation_protection = 0x%x\n", |
|
3591 memory_info_.allocation_protection); |
|
3592 printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size); |
|
3593 printf(" state = 0x%x\n", memory_info_.state); |
|
3594 printf(" protection = 0x%x\n", memory_info_.protection); |
|
3595 printf(" type = 0x%x\n", memory_info_.type); |
|
3596 } |
|
3597 |
|
3598 |
|
3599 // |
|
3600 // MinidumpMemoryInfoList |
|
3601 // |
|
3602 |
|
3603 |
|
3604 MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump) |
|
3605 : MinidumpStream(minidump), |
|
3606 range_map_(new RangeMap<uint64_t, unsigned int>()), |
|
3607 infos_(NULL), |
|
3608 info_count_(0) { |
|
3609 } |
|
3610 |
|
3611 |
|
3612 MinidumpMemoryInfoList::~MinidumpMemoryInfoList() { |
|
3613 delete range_map_; |
|
3614 delete infos_; |
|
3615 } |
|
3616 |
|
3617 |
|
3618 bool MinidumpMemoryInfoList::Read(uint32_t expected_size) { |
|
3619 // Invalidate cached data. |
|
3620 delete infos_; |
|
3621 infos_ = NULL; |
|
3622 range_map_->Clear(); |
|
3623 info_count_ = 0; |
|
3624 |
|
3625 valid_ = false; |
|
3626 |
|
3627 MDRawMemoryInfoList header; |
|
3628 if (expected_size < sizeof(MDRawMemoryInfoList)) { |
|
3629 BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << |
|
3630 expected_size << " < " << sizeof(MDRawMemoryInfoList); |
|
3631 return false; |
|
3632 } |
|
3633 if (!minidump_->ReadBytes(&header, sizeof(header))) { |
|
3634 BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header"; |
|
3635 return false; |
|
3636 } |
|
3637 |
|
3638 if (minidump_->swap()) { |
|
3639 Swap(&header.size_of_header); |
|
3640 Swap(&header.size_of_entry); |
|
3641 Swap(&header.number_of_entries); |
|
3642 } |
|
3643 |
|
3644 // Sanity check that the header is the expected size. |
|
3645 //TODO(ted): could possibly handle this more gracefully, assuming |
|
3646 // that future versions of the structs would be backwards-compatible. |
|
3647 if (header.size_of_header != sizeof(MDRawMemoryInfoList)) { |
|
3648 BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << |
|
3649 header.size_of_header << " != " << |
|
3650 sizeof(MDRawMemoryInfoList); |
|
3651 return false; |
|
3652 } |
|
3653 |
|
3654 // Sanity check that the entries are the expected size. |
|
3655 if (header.size_of_entry != sizeof(MDRawMemoryInfo)) { |
|
3656 BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " << |
|
3657 header.size_of_entry << " != " << |
|
3658 sizeof(MDRawMemoryInfo); |
|
3659 return false; |
|
3660 } |
|
3661 |
|
3662 if (header.number_of_entries > |
|
3663 numeric_limits<uint32_t>::max() / sizeof(MDRawMemoryInfo)) { |
|
3664 BPLOG(ERROR) << "MinidumpMemoryInfoList info count " << |
|
3665 header.number_of_entries << |
|
3666 " would cause multiplication overflow"; |
|
3667 return false; |
|
3668 } |
|
3669 |
|
3670 if (expected_size != sizeof(MDRawMemoryInfoList) + |
|
3671 header.number_of_entries * sizeof(MDRawMemoryInfo)) { |
|
3672 BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size << |
|
3673 " != " << sizeof(MDRawMemoryInfoList) + |
|
3674 header.number_of_entries * sizeof(MDRawMemoryInfo); |
|
3675 return false; |
|
3676 } |
|
3677 |
|
3678 if (header.number_of_entries != 0) { |
|
3679 scoped_ptr<MinidumpMemoryInfos> infos( |
|
3680 new MinidumpMemoryInfos(header.number_of_entries, |
|
3681 MinidumpMemoryInfo(minidump_))); |
|
3682 |
|
3683 for (unsigned int index = 0; |
|
3684 index < header.number_of_entries; |
|
3685 ++index) { |
|
3686 MinidumpMemoryInfo* info = &(*infos)[index]; |
|
3687 |
|
3688 // Assume that the file offset is correct after the last read. |
|
3689 if (!info->Read()) { |
|
3690 BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " << |
|
3691 index << "/" << header.number_of_entries; |
|
3692 return false; |
|
3693 } |
|
3694 |
|
3695 uint64_t base_address = info->GetBase(); |
|
3696 uint32_t region_size = info->GetSize(); |
|
3697 |
|
3698 if (!range_map_->StoreRange(base_address, region_size, index)) { |
|
3699 BPLOG(ERROR) << "MinidumpMemoryInfoList could not store" |
|
3700 " memory region " << |
|
3701 index << "/" << header.number_of_entries << ", " << |
|
3702 HexString(base_address) << "+" << |
|
3703 HexString(region_size); |
|
3704 return false; |
|
3705 } |
|
3706 } |
|
3707 |
|
3708 infos_ = infos.release(); |
|
3709 } |
|
3710 |
|
3711 info_count_ = header.number_of_entries; |
|
3712 |
|
3713 valid_ = true; |
|
3714 return true; |
|
3715 } |
|
3716 |
|
3717 |
|
3718 const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex( |
|
3719 unsigned int index) const { |
|
3720 if (!valid_) { |
|
3721 BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex"; |
|
3722 return NULL; |
|
3723 } |
|
3724 |
|
3725 if (index >= info_count_) { |
|
3726 BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " << |
|
3727 index << "/" << info_count_; |
|
3728 return NULL; |
|
3729 } |
|
3730 |
|
3731 return &(*infos_)[index]; |
|
3732 } |
|
3733 |
|
3734 |
|
3735 const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress( |
|
3736 uint64_t address) const { |
|
3737 if (!valid_) { |
|
3738 BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for" |
|
3739 " GetMemoryInfoForAddress"; |
|
3740 return NULL; |
|
3741 } |
|
3742 |
|
3743 unsigned int info_index; |
|
3744 if (!range_map_->RetrieveRange(address, &info_index, NULL, NULL)) { |
|
3745 BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " << |
|
3746 HexString(address); |
|
3747 return NULL; |
|
3748 } |
|
3749 |
|
3750 return GetMemoryInfoAtIndex(info_index); |
|
3751 } |
|
3752 |
|
3753 |
|
3754 void MinidumpMemoryInfoList::Print() { |
|
3755 if (!valid_) { |
|
3756 BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data"; |
|
3757 return; |
|
3758 } |
|
3759 |
|
3760 printf("MinidumpMemoryInfoList\n"); |
|
3761 printf(" info_count = %d\n", info_count_); |
|
3762 printf("\n"); |
|
3763 |
|
3764 for (unsigned int info_index = 0; |
|
3765 info_index < info_count_; |
|
3766 ++info_index) { |
|
3767 printf("info[%d]\n", info_index); |
|
3768 (*infos_)[info_index].Print(); |
|
3769 printf("\n"); |
|
3770 } |
|
3771 } |
|
3772 |
|
3773 |
|
3774 // |
|
3775 // Minidump |
|
3776 // |
|
3777 |
|
3778 |
|
3779 uint32_t Minidump::max_streams_ = 128; |
|
3780 unsigned int Minidump::max_string_length_ = 1024; |
|
3781 |
|
3782 |
|
3783 Minidump::Minidump(const string& path) |
|
3784 : header_(), |
|
3785 directory_(NULL), |
|
3786 stream_map_(new MinidumpStreamMap()), |
|
3787 path_(path), |
|
3788 stream_(NULL), |
|
3789 swap_(false), |
|
3790 valid_(false) { |
|
3791 } |
|
3792 |
|
3793 Minidump::Minidump(istream& stream) |
|
3794 : header_(), |
|
3795 directory_(NULL), |
|
3796 stream_map_(new MinidumpStreamMap()), |
|
3797 path_(), |
|
3798 stream_(&stream), |
|
3799 swap_(false), |
|
3800 valid_(false) { |
|
3801 } |
|
3802 |
|
3803 Minidump::~Minidump() { |
|
3804 if (stream_) { |
|
3805 BPLOG(INFO) << "Minidump closing minidump"; |
|
3806 } |
|
3807 if (!path_.empty()) { |
|
3808 delete stream_; |
|
3809 } |
|
3810 delete directory_; |
|
3811 delete stream_map_; |
|
3812 } |
|
3813 |
|
3814 |
|
3815 bool Minidump::Open() { |
|
3816 if (stream_ != NULL) { |
|
3817 BPLOG(INFO) << "Minidump reopening minidump " << path_; |
|
3818 |
|
3819 // The file is already open. Seek to the beginning, which is the position |
|
3820 // the file would be at if it were opened anew. |
|
3821 return SeekSet(0); |
|
3822 } |
|
3823 |
|
3824 stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary); |
|
3825 if (!stream_ || !stream_->good()) { |
|
3826 string error_string; |
|
3827 int error_code = ErrnoString(&error_string); |
|
3828 BPLOG(ERROR) << "Minidump could not open minidump " << path_ << |
|
3829 ", error " << error_code << ": " << error_string; |
|
3830 return false; |
|
3831 } |
|
3832 |
|
3833 BPLOG(INFO) << "Minidump opened minidump " << path_; |
|
3834 return true; |
|
3835 } |
|
3836 |
|
3837 bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) { |
|
3838 // Initialize output parameters |
|
3839 *context_cpu_flags = 0; |
|
3840 |
|
3841 // Save the current stream position |
|
3842 off_t saved_position = Tell(); |
|
3843 if (saved_position == -1) { |
|
3844 // Failed to save the current stream position. |
|
3845 // Returns true because the current position of the stream is preserved. |
|
3846 return true; |
|
3847 } |
|
3848 |
|
3849 const MDRawSystemInfo* system_info = |
|
3850 GetSystemInfo() ? GetSystemInfo()->system_info() : NULL; |
|
3851 |
|
3852 if (system_info != NULL) { |
|
3853 switch (system_info->processor_architecture) { |
|
3854 case MD_CPU_ARCHITECTURE_X86: |
|
3855 *context_cpu_flags = MD_CONTEXT_X86; |
|
3856 break; |
|
3857 case MD_CPU_ARCHITECTURE_MIPS: |
|
3858 *context_cpu_flags = MD_CONTEXT_MIPS; |
|
3859 break; |
|
3860 case MD_CPU_ARCHITECTURE_ALPHA: |
|
3861 *context_cpu_flags = MD_CONTEXT_ALPHA; |
|
3862 break; |
|
3863 case MD_CPU_ARCHITECTURE_PPC: |
|
3864 *context_cpu_flags = MD_CONTEXT_PPC; |
|
3865 break; |
|
3866 case MD_CPU_ARCHITECTURE_SHX: |
|
3867 *context_cpu_flags = MD_CONTEXT_SHX; |
|
3868 break; |
|
3869 case MD_CPU_ARCHITECTURE_ARM: |
|
3870 *context_cpu_flags = MD_CONTEXT_ARM; |
|
3871 break; |
|
3872 case MD_CPU_ARCHITECTURE_IA64: |
|
3873 *context_cpu_flags = MD_CONTEXT_IA64; |
|
3874 break; |
|
3875 case MD_CPU_ARCHITECTURE_ALPHA64: |
|
3876 *context_cpu_flags = 0; |
|
3877 break; |
|
3878 case MD_CPU_ARCHITECTURE_MSIL: |
|
3879 *context_cpu_flags = 0; |
|
3880 break; |
|
3881 case MD_CPU_ARCHITECTURE_AMD64: |
|
3882 *context_cpu_flags = MD_CONTEXT_AMD64; |
|
3883 break; |
|
3884 case MD_CPU_ARCHITECTURE_X86_WIN64: |
|
3885 *context_cpu_flags = 0; |
|
3886 break; |
|
3887 case MD_CPU_ARCHITECTURE_SPARC: |
|
3888 *context_cpu_flags = MD_CONTEXT_SPARC; |
|
3889 break; |
|
3890 case MD_CPU_ARCHITECTURE_UNKNOWN: |
|
3891 *context_cpu_flags = 0; |
|
3892 break; |
|
3893 default: |
|
3894 *context_cpu_flags = 0; |
|
3895 break; |
|
3896 } |
|
3897 } |
|
3898 |
|
3899 // Restore position and return |
|
3900 return SeekSet(saved_position); |
|
3901 } |
|
3902 |
|
3903 |
|
3904 bool Minidump::Read() { |
|
3905 // Invalidate cached data. |
|
3906 delete directory_; |
|
3907 directory_ = NULL; |
|
3908 stream_map_->clear(); |
|
3909 |
|
3910 valid_ = false; |
|
3911 |
|
3912 if (!Open()) { |
|
3913 BPLOG(ERROR) << "Minidump cannot open minidump"; |
|
3914 return false; |
|
3915 } |
|
3916 |
|
3917 if (!ReadBytes(&header_, sizeof(MDRawHeader))) { |
|
3918 BPLOG(ERROR) << "Minidump cannot read header"; |
|
3919 return false; |
|
3920 } |
|
3921 |
|
3922 if (header_.signature != MD_HEADER_SIGNATURE) { |
|
3923 // The file may be byte-swapped. Under the present architecture, these |
|
3924 // classes don't know or need to know what CPU (or endianness) the |
|
3925 // minidump was produced on in order to parse it. Use the signature as |
|
3926 // a byte order marker. |
|
3927 uint32_t signature_swapped = header_.signature; |
|
3928 Swap(&signature_swapped); |
|
3929 if (signature_swapped != MD_HEADER_SIGNATURE) { |
|
3930 // This isn't a minidump or a byte-swapped minidump. |
|
3931 BPLOG(ERROR) << "Minidump header signature mismatch: (" << |
|
3932 HexString(header_.signature) << ", " << |
|
3933 HexString(signature_swapped) << ") != " << |
|
3934 HexString(MD_HEADER_SIGNATURE); |
|
3935 return false; |
|
3936 } |
|
3937 swap_ = true; |
|
3938 } else { |
|
3939 // The file is not byte-swapped. Set swap_ false (it may have been true |
|
3940 // if the object is being reused?) |
|
3941 swap_ = false; |
|
3942 } |
|
3943 |
|
3944 BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") << |
|
3945 "byte-swapping minidump"; |
|
3946 |
|
3947 if (swap_) { |
|
3948 Swap(&header_.signature); |
|
3949 Swap(&header_.version); |
|
3950 Swap(&header_.stream_count); |
|
3951 Swap(&header_.stream_directory_rva); |
|
3952 Swap(&header_.checksum); |
|
3953 Swap(&header_.time_date_stamp); |
|
3954 Swap(&header_.flags); |
|
3955 } |
|
3956 |
|
3957 // Version check. The high 16 bits of header_.version contain something |
|
3958 // else "implementation specific." |
|
3959 if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) { |
|
3960 BPLOG(ERROR) << "Minidump version mismatch: " << |
|
3961 HexString(header_.version & 0x0000ffff) << " != " << |
|
3962 HexString(MD_HEADER_VERSION); |
|
3963 return false; |
|
3964 } |
|
3965 |
|
3966 if (!SeekSet(header_.stream_directory_rva)) { |
|
3967 BPLOG(ERROR) << "Minidump cannot seek to stream directory"; |
|
3968 return false; |
|
3969 } |
|
3970 |
|
3971 if (header_.stream_count > max_streams_) { |
|
3972 BPLOG(ERROR) << "Minidump stream count " << header_.stream_count << |
|
3973 " exceeds maximum " << max_streams_; |
|
3974 return false; |
|
3975 } |
|
3976 |
|
3977 if (header_.stream_count != 0) { |
|
3978 scoped_ptr<MinidumpDirectoryEntries> directory( |
|
3979 new MinidumpDirectoryEntries(header_.stream_count)); |
|
3980 |
|
3981 // Read the entire array in one fell swoop, instead of reading one entry |
|
3982 // at a time in the loop. |
|
3983 if (!ReadBytes(&(*directory)[0], |
|
3984 sizeof(MDRawDirectory) * header_.stream_count)) { |
|
3985 BPLOG(ERROR) << "Minidump cannot read stream directory"; |
|
3986 return false; |
|
3987 } |
|
3988 |
|
3989 for (unsigned int stream_index = 0; |
|
3990 stream_index < header_.stream_count; |
|
3991 ++stream_index) { |
|
3992 MDRawDirectory* directory_entry = &(*directory)[stream_index]; |
|
3993 |
|
3994 if (swap_) { |
|
3995 Swap(&directory_entry->stream_type); |
|
3996 Swap(&directory_entry->location); |
|
3997 } |
|
3998 |
|
3999 // Initialize the stream_map_ map, which speeds locating a stream by |
|
4000 // type. |
|
4001 unsigned int stream_type = directory_entry->stream_type; |
|
4002 switch (stream_type) { |
|
4003 case MD_THREAD_LIST_STREAM: |
|
4004 case MD_MODULE_LIST_STREAM: |
|
4005 case MD_MEMORY_LIST_STREAM: |
|
4006 case MD_EXCEPTION_STREAM: |
|
4007 case MD_SYSTEM_INFO_STREAM: |
|
4008 case MD_MISC_INFO_STREAM: |
|
4009 case MD_BREAKPAD_INFO_STREAM: { |
|
4010 if (stream_map_->find(stream_type) != stream_map_->end()) { |
|
4011 // Another stream with this type was already found. A minidump |
|
4012 // file should contain at most one of each of these stream types. |
|
4013 BPLOG(ERROR) << "Minidump found multiple streams of type " << |
|
4014 stream_type << ", but can only deal with one"; |
|
4015 return false; |
|
4016 } |
|
4017 // Fall through to default |
|
4018 } |
|
4019 |
|
4020 default: { |
|
4021 // Overwrites for stream types other than those above, but it's |
|
4022 // expected to be the user's burden in that case. |
|
4023 (*stream_map_)[stream_type].stream_index = stream_index; |
|
4024 } |
|
4025 } |
|
4026 } |
|
4027 |
|
4028 directory_ = directory.release(); |
|
4029 } |
|
4030 |
|
4031 valid_ = true; |
|
4032 return true; |
|
4033 } |
|
4034 |
|
4035 |
|
4036 MinidumpThreadList* Minidump::GetThreadList() { |
|
4037 MinidumpThreadList* thread_list; |
|
4038 return GetStream(&thread_list); |
|
4039 } |
|
4040 |
|
4041 |
|
4042 MinidumpModuleList* Minidump::GetModuleList() { |
|
4043 MinidumpModuleList* module_list; |
|
4044 return GetStream(&module_list); |
|
4045 } |
|
4046 |
|
4047 |
|
4048 MinidumpMemoryList* Minidump::GetMemoryList() { |
|
4049 MinidumpMemoryList* memory_list; |
|
4050 return GetStream(&memory_list); |
|
4051 } |
|
4052 |
|
4053 |
|
4054 MinidumpException* Minidump::GetException() { |
|
4055 MinidumpException* exception; |
|
4056 return GetStream(&exception); |
|
4057 } |
|
4058 |
|
4059 MinidumpAssertion* Minidump::GetAssertion() { |
|
4060 MinidumpAssertion* assertion; |
|
4061 return GetStream(&assertion); |
|
4062 } |
|
4063 |
|
4064 |
|
4065 MinidumpSystemInfo* Minidump::GetSystemInfo() { |
|
4066 MinidumpSystemInfo* system_info; |
|
4067 return GetStream(&system_info); |
|
4068 } |
|
4069 |
|
4070 |
|
4071 MinidumpMiscInfo* Minidump::GetMiscInfo() { |
|
4072 MinidumpMiscInfo* misc_info; |
|
4073 return GetStream(&misc_info); |
|
4074 } |
|
4075 |
|
4076 |
|
4077 MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() { |
|
4078 MinidumpBreakpadInfo* breakpad_info; |
|
4079 return GetStream(&breakpad_info); |
|
4080 } |
|
4081 |
|
4082 MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() { |
|
4083 MinidumpMemoryInfoList* memory_info_list; |
|
4084 return GetStream(&memory_info_list); |
|
4085 } |
|
4086 |
|
4087 |
|
4088 void Minidump::Print() { |
|
4089 if (!valid_) { |
|
4090 BPLOG(ERROR) << "Minidump cannot print invalid data"; |
|
4091 return; |
|
4092 } |
|
4093 |
|
4094 printf("MDRawHeader\n"); |
|
4095 printf(" signature = 0x%x\n", header_.signature); |
|
4096 printf(" version = 0x%x\n", header_.version); |
|
4097 printf(" stream_count = %d\n", header_.stream_count); |
|
4098 printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva); |
|
4099 printf(" checksum = 0x%x\n", header_.checksum); |
|
4100 struct tm timestruct; |
|
4101 #ifdef _WIN32 |
|
4102 gmtime_s(×truct, reinterpret_cast<time_t*>(&header_.time_date_stamp)); |
|
4103 #else |
|
4104 gmtime_r(reinterpret_cast<time_t*>(&header_.time_date_stamp), ×truct); |
|
4105 #endif |
|
4106 char timestr[20]; |
|
4107 strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); |
|
4108 printf(" time_date_stamp = 0x%x %s\n", header_.time_date_stamp, |
|
4109 timestr); |
|
4110 printf(" flags = 0x%" PRIx64 "\n", header_.flags); |
|
4111 printf("\n"); |
|
4112 |
|
4113 for (unsigned int stream_index = 0; |
|
4114 stream_index < header_.stream_count; |
|
4115 ++stream_index) { |
|
4116 MDRawDirectory* directory_entry = &(*directory_)[stream_index]; |
|
4117 |
|
4118 printf("mDirectory[%d]\n", stream_index); |
|
4119 printf("MDRawDirectory\n"); |
|
4120 printf(" stream_type = %d\n", directory_entry->stream_type); |
|
4121 printf(" location.data_size = %d\n", |
|
4122 directory_entry->location.data_size); |
|
4123 printf(" location.rva = 0x%x\n", directory_entry->location.rva); |
|
4124 printf("\n"); |
|
4125 } |
|
4126 |
|
4127 printf("Streams:\n"); |
|
4128 for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin(); |
|
4129 iterator != stream_map_->end(); |
|
4130 ++iterator) { |
|
4131 uint32_t stream_type = iterator->first; |
|
4132 MinidumpStreamInfo info = iterator->second; |
|
4133 printf(" stream type 0x%x at index %d\n", stream_type, info.stream_index); |
|
4134 } |
|
4135 printf("\n"); |
|
4136 } |
|
4137 |
|
4138 |
|
4139 const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index) |
|
4140 const { |
|
4141 if (!valid_) { |
|
4142 BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex"; |
|
4143 return NULL; |
|
4144 } |
|
4145 |
|
4146 if (index >= header_.stream_count) { |
|
4147 BPLOG(ERROR) << "Minidump stream directory index out of range: " << |
|
4148 index << "/" << header_.stream_count; |
|
4149 return NULL; |
|
4150 } |
|
4151 |
|
4152 return &(*directory_)[index]; |
|
4153 } |
|
4154 |
|
4155 |
|
4156 bool Minidump::ReadBytes(void* bytes, size_t count) { |
|
4157 // Can't check valid_ because Read needs to call this method before |
|
4158 // validity can be determined. |
|
4159 if (!stream_) { |
|
4160 return false; |
|
4161 } |
|
4162 stream_->read(static_cast<char*>(bytes), count); |
|
4163 size_t bytes_read = stream_->gcount(); |
|
4164 if (bytes_read != count) { |
|
4165 if (bytes_read == size_t(-1)) { |
|
4166 string error_string; |
|
4167 int error_code = ErrnoString(&error_string); |
|
4168 BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string; |
|
4169 } else { |
|
4170 BPLOG(ERROR) << "ReadBytes: read " << bytes_read << "/" << count; |
|
4171 } |
|
4172 return false; |
|
4173 } |
|
4174 return true; |
|
4175 } |
|
4176 |
|
4177 |
|
4178 bool Minidump::SeekSet(off_t offset) { |
|
4179 // Can't check valid_ because Read needs to call this method before |
|
4180 // validity can be determined. |
|
4181 if (!stream_) { |
|
4182 return false; |
|
4183 } |
|
4184 stream_->seekg(offset, std::ios_base::beg); |
|
4185 if (!stream_->good()) { |
|
4186 string error_string; |
|
4187 int error_code = ErrnoString(&error_string); |
|
4188 BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; |
|
4189 return false; |
|
4190 } |
|
4191 return true; |
|
4192 } |
|
4193 |
|
4194 off_t Minidump::Tell() { |
|
4195 if (!valid_ || !stream_) { |
|
4196 return (off_t)-1; |
|
4197 } |
|
4198 |
|
4199 return stream_->tellg(); |
|
4200 } |
|
4201 |
|
4202 |
|
4203 string* Minidump::ReadString(off_t offset) { |
|
4204 if (!valid_) { |
|
4205 BPLOG(ERROR) << "Invalid Minidump for ReadString"; |
|
4206 return NULL; |
|
4207 } |
|
4208 if (!SeekSet(offset)) { |
|
4209 BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset; |
|
4210 return NULL; |
|
4211 } |
|
4212 |
|
4213 uint32_t bytes; |
|
4214 if (!ReadBytes(&bytes, sizeof(bytes))) { |
|
4215 BPLOG(ERROR) << "ReadString could not read string size at offset " << |
|
4216 offset; |
|
4217 return NULL; |
|
4218 } |
|
4219 if (swap_) |
|
4220 Swap(&bytes); |
|
4221 |
|
4222 if (bytes % 2 != 0) { |
|
4223 BPLOG(ERROR) << "ReadString found odd-sized " << bytes << |
|
4224 "-byte string at offset " << offset; |
|
4225 return NULL; |
|
4226 } |
|
4227 unsigned int utf16_words = bytes / 2; |
|
4228 |
|
4229 if (utf16_words > max_string_length_) { |
|
4230 BPLOG(ERROR) << "ReadString string length " << utf16_words << |
|
4231 " exceeds maximum " << max_string_length_ << |
|
4232 " at offset " << offset; |
|
4233 return NULL; |
|
4234 } |
|
4235 |
|
4236 vector<uint16_t> string_utf16(utf16_words); |
|
4237 |
|
4238 if (utf16_words) { |
|
4239 if (!ReadBytes(&string_utf16[0], bytes)) { |
|
4240 BPLOG(ERROR) << "ReadString could not read " << bytes << |
|
4241 "-byte string at offset " << offset; |
|
4242 return NULL; |
|
4243 } |
|
4244 } |
|
4245 |
|
4246 return UTF16ToUTF8(string_utf16, swap_); |
|
4247 } |
|
4248 |
|
4249 |
|
4250 bool Minidump::SeekToStreamType(uint32_t stream_type, |
|
4251 uint32_t* stream_length) { |
|
4252 BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires " |
|
4253 "|stream_length|"; |
|
4254 assert(stream_length); |
|
4255 *stream_length = 0; |
|
4256 |
|
4257 if (!valid_) { |
|
4258 BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType"; |
|
4259 return false; |
|
4260 } |
|
4261 |
|
4262 MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type); |
|
4263 if (iterator == stream_map_->end()) { |
|
4264 // This stream type didn't exist in the directory. |
|
4265 BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present"; |
|
4266 return false; |
|
4267 } |
|
4268 |
|
4269 MinidumpStreamInfo info = iterator->second; |
|
4270 if (info.stream_index >= header_.stream_count) { |
|
4271 BPLOG(ERROR) << "SeekToStreamType: type " << stream_type << |
|
4272 " out of range: " << |
|
4273 info.stream_index << "/" << header_.stream_count; |
|
4274 return false; |
|
4275 } |
|
4276 |
|
4277 MDRawDirectory* directory_entry = &(*directory_)[info.stream_index]; |
|
4278 if (!SeekSet(directory_entry->location.rva)) { |
|
4279 BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " << |
|
4280 stream_type; |
|
4281 return false; |
|
4282 } |
|
4283 |
|
4284 *stream_length = directory_entry->location.data_size; |
|
4285 |
|
4286 return true; |
|
4287 } |
|
4288 |
|
4289 |
|
4290 template<typename T> |
|
4291 T* Minidump::GetStream(T** stream) { |
|
4292 // stream is a garbage parameter that's present only to account for C++'s |
|
4293 // inability to overload a method based solely on its return type. |
|
4294 |
|
4295 const uint32_t stream_type = T::kStreamType; |
|
4296 |
|
4297 BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type << |
|
4298 " requires |stream|"; |
|
4299 assert(stream); |
|
4300 *stream = NULL; |
|
4301 |
|
4302 if (!valid_) { |
|
4303 BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type; |
|
4304 return NULL; |
|
4305 } |
|
4306 |
|
4307 MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type); |
|
4308 if (iterator == stream_map_->end()) { |
|
4309 // This stream type didn't exist in the directory. |
|
4310 BPLOG(INFO) << "GetStream: type " << stream_type << " not present"; |
|
4311 return NULL; |
|
4312 } |
|
4313 |
|
4314 // Get a pointer so that the stored stream field can be altered. |
|
4315 MinidumpStreamInfo* info = &iterator->second; |
|
4316 |
|
4317 if (info->stream) { |
|
4318 // This cast is safe because info.stream is only populated by this |
|
4319 // method, and there is a direct correlation between T and stream_type. |
|
4320 *stream = static_cast<T*>(info->stream); |
|
4321 return *stream; |
|
4322 } |
|
4323 |
|
4324 uint32_t stream_length; |
|
4325 if (!SeekToStreamType(stream_type, &stream_length)) { |
|
4326 BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type; |
|
4327 return NULL; |
|
4328 } |
|
4329 |
|
4330 scoped_ptr<T> new_stream(new T(this)); |
|
4331 |
|
4332 if (!new_stream->Read(stream_length)) { |
|
4333 BPLOG(ERROR) << "GetStream could not read stream type " << stream_type; |
|
4334 return NULL; |
|
4335 } |
|
4336 |
|
4337 *stream = new_stream.release(); |
|
4338 info->stream = *stream; |
|
4339 return *stream; |
|
4340 } |
|
4341 |
|
4342 |
|
4343 } // namespace google_breakpad |