Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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(®ion_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(®ion_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, ®ion_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(×truct, 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), ×truct); |
michael@0 | 4105 | #endif |
michael@0 | 4106 | char timestr[20]; |
michael@0 | 4107 | strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); |
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 |