toolkit/crashreporter/google-breakpad/src/processor/minidump.cc

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rwxr-xr-x

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial