minidump_generator.cc revision 486229ba5044ee2e7f49d2f6000e6535f2fca46c
1// Copyright (c) 2006, Google Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above 11// copyright notice, this list of conditions and the following disclaimer 12// in the documentation and/or other materials provided with the 13// distribution. 14// * Neither the name of Google Inc. nor the names of its 15// contributors may be used to endorse or promote products derived from 16// this software without specific prior written permission. 17// 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30#include <algorithm> 31#include <cstdio> 32 33#include <mach/host_info.h> 34#include <mach/vm_statistics.h> 35#include <mach-o/dyld.h> 36#include <mach-o/loader.h> 37#include <sys/sysctl.h> 38#include <sys/resource.h> 39 40#include <CoreFoundation/CoreFoundation.h> 41 42#include "client/mac/handler/minidump_generator.h" 43 44#ifdef HAS_ARM_SUPPORT 45#include <mach/arm/thread_status.h> 46#endif 47#ifdef HAS_PPC_SUPPORT 48#include <mach/ppc/thread_status.h> 49#endif 50#ifdef HAS_X86_SUPPORT 51#include <mach/i386/thread_status.h> 52#endif 53 54#include "client/minidump_file_writer-inl.h" 55#include "common/mac/file_id.h" 56#include "common/mac/macho_id.h" 57#include "common/mac/string_utilities.h" 58 59using MacStringUtils::ConvertToString; 60using MacStringUtils::IntegerValueAtIndex; 61 62namespace google_breakpad { 63 64#if __LP64__ 65#define LC_SEGMENT_ARCH LC_SEGMENT_64 66#else 67#define LC_SEGMENT_ARCH LC_SEGMENT 68#endif 69 70// constructor when generating from within the crashed process 71MinidumpGenerator::MinidumpGenerator() 72 : writer_(), 73 exception_type_(0), 74 exception_code_(0), 75 exception_subcode_(0), 76 exception_thread_(0), 77 crashing_task_(mach_task_self()), 78 handler_thread_(mach_thread_self()), 79 cpu_type_(DynamicImages::GetNativeCPUType()), 80 task_context_(NULL), 81 dynamic_images_(NULL), 82 memory_blocks_(&allocator_) { 83 GatherSystemInformation(); 84} 85 86// constructor when generating from a different process than the 87// crashed process 88MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, 89 mach_port_t handler_thread) 90 : writer_(), 91 exception_type_(0), 92 exception_code_(0), 93 exception_subcode_(0), 94 exception_thread_(0), 95 crashing_task_(crashing_task), 96 handler_thread_(handler_thread), 97 cpu_type_(DynamicImages::GetNativeCPUType()), 98 task_context_(NULL), 99 dynamic_images_(NULL), 100 memory_blocks_(&allocator_) { 101 if (crashing_task != mach_task_self()) { 102 dynamic_images_ = new DynamicImages(crashing_task_); 103 cpu_type_ = dynamic_images_->GetCPUType(); 104 } else { 105 dynamic_images_ = NULL; 106 cpu_type_ = DynamicImages::GetNativeCPUType(); 107 } 108 109 GatherSystemInformation(); 110} 111 112MinidumpGenerator::~MinidumpGenerator() { 113 delete dynamic_images_; 114} 115 116char MinidumpGenerator::build_string_[16]; 117int MinidumpGenerator::os_major_version_ = 0; 118int MinidumpGenerator::os_minor_version_ = 0; 119int MinidumpGenerator::os_build_number_ = 0; 120 121// static 122void MinidumpGenerator::GatherSystemInformation() { 123 // If this is non-zero, then we've already gathered the information 124 if (os_major_version_) 125 return; 126 127 // This code extracts the version and build information from the OS 128 CFStringRef vers_path = 129 CFSTR("/System/Library/CoreServices/SystemVersion.plist"); 130 CFURLRef sys_vers = 131 CFURLCreateWithFileSystemPath(NULL, 132 vers_path, 133 kCFURLPOSIXPathStyle, 134 false); 135 CFDataRef data; 136 SInt32 error; 137 CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL, 138 &error); 139 140 if (!data) { 141 CFRelease(sys_vers); 142 return; 143 } 144 145 CFDictionaryRef list = static_cast<CFDictionaryRef> 146 (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, 147 NULL)); 148 if (!list) { 149 CFRelease(sys_vers); 150 CFRelease(data); 151 return; 152 } 153 154 CFStringRef build_version = static_cast<CFStringRef> 155 (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); 156 CFStringRef product_version = static_cast<CFStringRef> 157 (CFDictionaryGetValue(list, CFSTR("ProductVersion"))); 158 string build_str = ConvertToString(build_version); 159 string product_str = ConvertToString(product_version); 160 161 CFRelease(list); 162 CFRelease(sys_vers); 163 CFRelease(data); 164 165 strlcpy(build_string_, build_str.c_str(), sizeof(build_string_)); 166 167 // Parse the string that looks like "10.4.8" 168 os_major_version_ = IntegerValueAtIndex(product_str, 0); 169 os_minor_version_ = IntegerValueAtIndex(product_str, 1); 170 os_build_number_ = IntegerValueAtIndex(product_str, 2); 171} 172 173void MinidumpGenerator::SetTaskContext(ucontext_t *task_context) { 174 task_context_ = task_context; 175} 176 177string MinidumpGenerator::UniqueNameInDirectory(const string &dir, 178 string *unique_name) { 179 CFUUIDRef uuid = CFUUIDCreate(NULL); 180 CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid); 181 CFRelease(uuid); 182 string file_name(ConvertToString(uuid_cfstr)); 183 CFRelease(uuid_cfstr); 184 string path(dir); 185 186 // Ensure that the directory (if non-empty) has a trailing slash so that 187 // we can append the file name and have a valid pathname. 188 if (!dir.empty()) { 189 if (dir.at(dir.size() - 1) != '/') 190 path.append(1, '/'); 191 } 192 193 path.append(file_name); 194 path.append(".dmp"); 195 196 if (unique_name) 197 *unique_name = file_name; 198 199 return path; 200} 201 202bool MinidumpGenerator::Write(const char *path) { 203 WriteStreamFN writers[] = { 204 &MinidumpGenerator::WriteThreadListStream, 205 &MinidumpGenerator::WriteMemoryListStream, 206 &MinidumpGenerator::WriteSystemInfoStream, 207 &MinidumpGenerator::WriteModuleListStream, 208 &MinidumpGenerator::WriteMiscInfoStream, 209 &MinidumpGenerator::WriteBreakpadInfoStream, 210 // Exception stream needs to be the last entry in this array as it may 211 // be omitted in the case where the minidump is written without an 212 // exception. 213 &MinidumpGenerator::WriteExceptionStream, 214 }; 215 bool result = false; 216 217 // If opening was successful, create the header, directory, and call each 218 // writer. The destructor for the TypedMDRVAs will cause the data to be 219 // flushed. The destructor for the MinidumpFileWriter will close the file. 220 if (writer_.Open(path)) { 221 TypedMDRVA<MDRawHeader> header(&writer_); 222 TypedMDRVA<MDRawDirectory> dir(&writer_); 223 224 if (!header.Allocate()) 225 return false; 226 227 int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0])); 228 229 // If we don't have exception information, don't write out the 230 // exception stream 231 if (!exception_thread_ && !exception_type_) 232 --writer_count; 233 234 // Add space for all writers 235 if (!dir.AllocateArray(writer_count)) 236 return false; 237 238 MDRawHeader *header_ptr = header.get(); 239 header_ptr->signature = MD_HEADER_SIGNATURE; 240 header_ptr->version = MD_HEADER_VERSION; 241 time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp))); 242 header_ptr->stream_count = writer_count; 243 header_ptr->stream_directory_rva = dir.position(); 244 245 MDRawDirectory local_dir; 246 result = true; 247 for (int i = 0; (result) && (i < writer_count); ++i) { 248 result = (this->*writers[i])(&local_dir); 249 250 if (result) 251 dir.CopyIndex(i, &local_dir); 252 } 253 } 254 return result; 255} 256 257size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) { 258 mach_vm_address_t stack_region_base = start_addr; 259 mach_vm_size_t stack_region_size; 260 natural_t nesting_level = 0; 261 vm_region_submap_info_64 submap_info; 262 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 263 264 vm_region_recurse_info_t region_info; 265 region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info); 266 267 if (start_addr == 0) { 268 return 0; 269 } 270 271 kern_return_t result = 272 mach_vm_region_recurse(crashing_task_, &stack_region_base, 273 &stack_region_size, &nesting_level, 274 region_info, &info_count); 275 276 if (result != KERN_SUCCESS || start_addr < stack_region_base) { 277 // Failure or stack corruption, since mach_vm_region had to go 278 // higher in the process address space to find a valid region. 279 return 0; 280 } 281 282 unsigned int tag = submap_info.user_tag; 283 284 // If the user tag is VM_MEMORY_STACK, look for more readable regions with 285 // the same tag placed immediately above the computed stack region. Under 286 // some circumstances, the stack for thread 0 winds up broken up into 287 // multiple distinct abutting regions. This can happen for several reasons, 288 // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes 289 // the access on stack pages by calling mprotect. 290 if (tag == VM_MEMORY_STACK) { 291 while (true) { 292 mach_vm_address_t next_region_base = stack_region_base + 293 stack_region_size; 294 mach_vm_address_t proposed_next_region_base = next_region_base; 295 mach_vm_size_t next_region_size; 296 nesting_level = 0; 297 info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 298 result = mach_vm_region_recurse(crashing_task_, &next_region_base, 299 &next_region_size, &nesting_level, 300 region_info, &info_count); 301 if (result != KERN_SUCCESS || 302 next_region_base != proposed_next_region_base || 303 submap_info.user_tag != tag || 304 (submap_info.protection & VM_PROT_READ) == 0) { 305 break; 306 } 307 308 stack_region_size += next_region_size; 309 } 310 } 311 312 return stack_region_base + stack_region_size - start_addr; 313} 314 315bool MinidumpGenerator::WriteStackFromStartAddress( 316 mach_vm_address_t start_addr, 317 MDMemoryDescriptor *stack_location) { 318 UntypedMDRVA memory(&writer_); 319 320 bool result = false; 321 size_t size = CalculateStackSize(start_addr); 322 323 if (size == 0) { 324 // In some situations the stack address for the thread can come back 0. 325 // In these cases we skip over the threads in question and stuff the 326 // stack with a clearly borked value. 327 start_addr = 0xDEADBEEF; 328 size = 16; 329 if (!memory.Allocate(size)) 330 return false; 331 332 unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of 333 // junk. 334 dummy_stack[0] = 0xDEADBEEF; 335 dummy_stack[1] = 0xDEADBEEF; 336 337 result = memory.Copy(dummy_stack, size); 338 } else { 339 340 if (!memory.Allocate(size)) 341 return false; 342 343 if (dynamic_images_) { 344 vector<uint8_t> stack_memory; 345 if (ReadTaskMemory(crashing_task_, 346 start_addr, 347 size, 348 stack_memory) != KERN_SUCCESS) { 349 return false; 350 } 351 352 result = memory.Copy(&stack_memory[0], size); 353 } else { 354 result = memory.Copy(reinterpret_cast<const void *>(start_addr), size); 355 } 356 } 357 358 stack_location->start_of_memory_range = start_addr; 359 stack_location->memory = memory.location(); 360 361 return result; 362} 363 364bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, 365 MDMemoryDescriptor *stack_location) { 366 switch (cpu_type_) { 367#ifdef HAS_ARM_SUPPORT 368 case CPU_TYPE_ARM: 369 return WriteStackARM(state, stack_location); 370#endif 371#ifdef HAS_PPC_SUPPORT 372 case CPU_TYPE_POWERPC: 373 return WriteStackPPC(state, stack_location); 374 case CPU_TYPE_POWERPC64: 375 return WriteStackPPC64(state, stack_location); 376#endif 377#ifdef HAS_X86_SUPPORT 378 case CPU_TYPE_I386: 379 return WriteStackX86(state, stack_location); 380 case CPU_TYPE_X86_64: 381 return WriteStackX86_64(state, stack_location); 382#endif 383 default: 384 return false; 385 } 386} 387 388bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, 389 MDLocationDescriptor *register_location) { 390 switch (cpu_type_) { 391#ifdef HAS_ARM_SUPPORT 392 case CPU_TYPE_ARM: 393 return WriteContextARM(state, register_location); 394#endif 395#ifdef HAS_PPC_SUPPORT 396 case CPU_TYPE_POWERPC: 397 return WriteContextPPC(state, register_location); 398 case CPU_TYPE_POWERPC64: 399 return WriteContextPPC64(state, register_location); 400#endif 401#ifdef HAS_X86_SUPPORT 402 case CPU_TYPE_I386: 403 return WriteContextX86(state, register_location); 404 case CPU_TYPE_X86_64: 405 return WriteContextX86_64(state, register_location); 406#endif 407 default: 408 return false; 409 } 410} 411 412u_int64_t MinidumpGenerator::CurrentPCForStack( 413 breakpad_thread_state_data_t state) { 414 switch (cpu_type_) { 415#ifdef HAS_ARM_SUPPORT 416 case CPU_TYPE_ARM: 417 return CurrentPCForStackARM(state); 418#endif 419#ifdef HAS_PPC_SUPPORT 420 case CPU_TYPE_POWERPC: 421 return CurrentPCForStackPPC(state); 422 case CPU_TYPE_POWERPC64: 423 return CurrentPCForStackPPC64(state); 424#endif 425#ifdef HAS_X86_SUPPORT 426 case CPU_TYPE_I386: 427 return CurrentPCForStackX86(state); 428 case CPU_TYPE_X86_64: 429 return CurrentPCForStackX86_64(state); 430#endif 431 default: 432 assert(0 && "Unknown CPU type!"); 433 return 0; 434 } 435} 436 437#ifdef HAS_ARM_SUPPORT 438bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state, 439 MDMemoryDescriptor *stack_location) { 440 arm_thread_state_t *machine_state = 441 reinterpret_cast<arm_thread_state_t *>(state); 442 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp); 443 return WriteStackFromStartAddress(start_addr, stack_location); 444} 445 446u_int64_t 447MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) { 448 arm_thread_state_t *machine_state = 449 reinterpret_cast<arm_thread_state_t *>(state); 450 451 return REGISTER_FROM_THREADSTATE(machine_state, pc); 452} 453 454bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state, 455 MDLocationDescriptor *register_location) 456{ 457 TypedMDRVA<MDRawContextARM> context(&writer_); 458 arm_thread_state_t *machine_state = 459 reinterpret_cast<arm_thread_state_t *>(state); 460 461 if (!context.Allocate()) 462 return false; 463 464 *register_location = context.location(); 465 MDRawContextARM *context_ptr = context.get(); 466 context_ptr->context_flags = MD_CONTEXT_ARM_FULL; 467 468#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a]) 469 470 context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp); 471 context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr); 472 context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc); 473 context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr); 474 475 AddGPR(0); 476 AddGPR(1); 477 AddGPR(2); 478 AddGPR(3); 479 AddGPR(4); 480 AddGPR(5); 481 AddGPR(6); 482 AddGPR(7); 483 AddGPR(8); 484 AddGPR(9); 485 AddGPR(10); 486 AddGPR(11); 487 AddGPR(12); 488#undef AddReg 489#undef AddGPR 490 491 return true; 492} 493#endif 494 495#ifdef HAS_PCC_SUPPORT 496bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state, 497 MDMemoryDescriptor *stack_location) { 498 ppc_thread_state_t *machine_state = 499 reinterpret_cast<ppc_thread_state_t *>(state); 500 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); 501 return WriteStackFromStartAddress(start_addr, stack_location); 502} 503 504bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state, 505 MDMemoryDescriptor *stack_location) { 506 ppc_thread_state64_t *machine_state = 507 reinterpret_cast<ppc_thread_state64_t *>(state); 508 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); 509 return WriteStackFromStartAddress(start_addr, stack_location); 510} 511 512u_int64_t 513MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) { 514 ppc_thread_state_t *machine_state = 515 reinterpret_cast<ppc_thread_state_t *>(state); 516 517 return REGISTER_FROM_THREADSTATE(machine_state, srr0); 518} 519 520u_int64_t 521MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) { 522 ppc_thread_state64_t *machine_state = 523 reinterpret_cast<ppc_thread_state64_t *>(state); 524 525 return REGISTER_FROM_THREADSTATE(machine_state, srr0); 526} 527 528bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state, 529 MDLocationDescriptor *register_location) 530{ 531 TypedMDRVA<MDRawContextPPC> context(&writer_); 532 ppc_thread_state_t *machine_state = 533 reinterpret_cast<ppc_thread_state_t *>(state); 534 535 if (!context.Allocate()) 536 return false; 537 538 *register_location = context.location(); 539 MDRawContextPPC *context_ptr = context.get(); 540 context_ptr->context_flags = MD_CONTEXT_PPC_BASE; 541 542#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 543#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) 544 545 AddReg(srr0); 546 AddReg(cr); 547 AddReg(xer); 548 AddReg(ctr); 549 AddReg(lr); 550 AddReg(vrsave); 551 552 AddGPR(0); 553 AddGPR(1); 554 AddGPR(2); 555 AddGPR(3); 556 AddGPR(4); 557 AddGPR(5); 558 AddGPR(6); 559 AddGPR(7); 560 AddGPR(8); 561 AddGPR(9); 562 AddGPR(10); 563 AddGPR(11); 564 AddGPR(12); 565 AddGPR(13); 566 AddGPR(14); 567 AddGPR(15); 568 AddGPR(16); 569 AddGPR(17); 570 AddGPR(18); 571 AddGPR(19); 572 AddGPR(20); 573 AddGPR(21); 574 AddGPR(22); 575 AddGPR(23); 576 AddGPR(24); 577 AddGPR(25); 578 AddGPR(26); 579 AddGPR(27); 580 AddGPR(28); 581 AddGPR(29); 582 AddGPR(30); 583 AddGPR(31); 584 AddReg(mq); 585#undef AddReg 586#undef AddGPR 587 588 return true; 589} 590 591bool MinidumpGenerator::WriteContextPPC64( 592 breakpad_thread_state_data_t state, 593 MDLocationDescriptor *register_location) { 594 TypedMDRVA<MDRawContextPPC64> context(&writer_); 595 ppc_thread_state64_t *machine_state = 596 reinterpret_cast<ppc_thread_state64_t *>(state); 597 598 if (!context.Allocate()) 599 return false; 600 601 *register_location = context.location(); 602 MDRawContextPPC64 *context_ptr = context.get(); 603 context_ptr->context_flags = MD_CONTEXT_PPC_BASE; 604 605#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 606#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) 607 608 AddReg(srr0); 609 AddReg(cr); 610 AddReg(xer); 611 AddReg(ctr); 612 AddReg(lr); 613 AddReg(vrsave); 614 615 AddGPR(0); 616 AddGPR(1); 617 AddGPR(2); 618 AddGPR(3); 619 AddGPR(4); 620 AddGPR(5); 621 AddGPR(6); 622 AddGPR(7); 623 AddGPR(8); 624 AddGPR(9); 625 AddGPR(10); 626 AddGPR(11); 627 AddGPR(12); 628 AddGPR(13); 629 AddGPR(14); 630 AddGPR(15); 631 AddGPR(16); 632 AddGPR(17); 633 AddGPR(18); 634 AddGPR(19); 635 AddGPR(20); 636 AddGPR(21); 637 AddGPR(22); 638 AddGPR(23); 639 AddGPR(24); 640 AddGPR(25); 641 AddGPR(26); 642 AddGPR(27); 643 AddGPR(28); 644 AddGPR(29); 645 AddGPR(30); 646 AddGPR(31); 647#undef AddReg 648#undef AddGPR 649 650 return true; 651} 652 653#endif 654 655#ifdef HAS_X86_SUPPORT 656bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state, 657 MDMemoryDescriptor *stack_location) { 658 i386_thread_state_t *machine_state = 659 reinterpret_cast<i386_thread_state_t *>(state); 660 661 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp); 662 return WriteStackFromStartAddress(start_addr, stack_location); 663} 664 665bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state, 666 MDMemoryDescriptor *stack_location) { 667 x86_thread_state64_t *machine_state = 668 reinterpret_cast<x86_thread_state64_t *>(state); 669 670 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp); 671 return WriteStackFromStartAddress(start_addr, stack_location); 672} 673 674u_int64_t 675MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) { 676 i386_thread_state_t *machine_state = 677 reinterpret_cast<i386_thread_state_t *>(state); 678 679 return REGISTER_FROM_THREADSTATE(machine_state, eip); 680} 681 682u_int64_t 683MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) { 684 x86_thread_state64_t *machine_state = 685 reinterpret_cast<x86_thread_state64_t *>(state); 686 687 return REGISTER_FROM_THREADSTATE(machine_state, rip); 688} 689 690bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state, 691 MDLocationDescriptor *register_location) 692{ 693 TypedMDRVA<MDRawContextX86> context(&writer_); 694 i386_thread_state_t *machine_state = 695 reinterpret_cast<i386_thread_state_t *>(state); 696 697 if (!context.Allocate()) 698 return false; 699 700 *register_location = context.location(); 701 MDRawContextX86 *context_ptr = context.get(); 702 703#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 704 705 context_ptr->context_flags = MD_CONTEXT_X86; 706 AddReg(eax); 707 AddReg(ebx); 708 AddReg(ecx); 709 AddReg(edx); 710 AddReg(esi); 711 AddReg(edi); 712 AddReg(ebp); 713 AddReg(esp); 714 715 AddReg(cs); 716 AddReg(ds); 717 AddReg(ss); 718 AddReg(es); 719 AddReg(fs); 720 AddReg(gs); 721 AddReg(eflags); 722 723 AddReg(eip); 724#undef AddReg 725 726 return true; 727} 728 729bool MinidumpGenerator::WriteContextX86_64( 730 breakpad_thread_state_data_t state, 731 MDLocationDescriptor *register_location) { 732 TypedMDRVA<MDRawContextAMD64> context(&writer_); 733 x86_thread_state64_t *machine_state = 734 reinterpret_cast<x86_thread_state64_t *>(state); 735 736 if (!context.Allocate()) 737 return false; 738 739 *register_location = context.location(); 740 MDRawContextAMD64 *context_ptr = context.get(); 741 742#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 743 744 context_ptr->context_flags = MD_CONTEXT_AMD64; 745 AddReg(rax); 746 AddReg(rbx); 747 AddReg(rcx); 748 AddReg(rdx); 749 AddReg(rdi); 750 AddReg(rsi); 751 AddReg(rbp); 752 AddReg(rsp); 753 AddReg(r8); 754 AddReg(r9); 755 AddReg(r10); 756 AddReg(r11); 757 AddReg(r12); 758 AddReg(r13); 759 AddReg(r14); 760 AddReg(r15); 761 AddReg(rip); 762 // according to AMD's software developer guide, bits above 18 are 763 // not used in the flags register. Since the minidump format 764 // specifies 32 bits for the flags register, we can truncate safely 765 // with no loss. 766 context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags)); 767 AddReg(cs); 768 AddReg(fs); 769 AddReg(gs); 770#undef AddReg 771 772 return true; 773} 774#endif 775 776bool MinidumpGenerator::GetThreadState(thread_act_t target_thread, 777 thread_state_t state, 778 mach_msg_type_number_t *count) { 779 if (task_context_ && target_thread == mach_thread_self()) { 780 switch (cpu_type_) { 781#ifdef HAS_ARM_SUPPORT 782 case CPU_TYPE_ARM: { 783 size_t final_size = 784 std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t)); 785 memcpy(state, &task_context_->uc_mcontext->__ss, final_size); 786 *count = final_size; 787 return true; 788 } 789#endif 790 } 791 } 792 thread_state_flavor_t flavor; 793 switch (cpu_type_) { 794#ifdef HAS_ARM_SUPPORT 795 case CPU_TYPE_ARM: 796 flavor = ARM_THREAD_STATE; 797 break; 798#endif 799#ifdef HAS_PPC_SUPPORT 800 case CPU_TYPE_POWERPC: 801 flavor = PPC_THREAD_STATE; 802 break; 803 case CPU_TYPE_POWERPC64: 804 flavor = PPC_THREAD_STATE64; 805 break; 806#endif 807#ifdef HAS_X86_SUPPORT 808 case CPU_TYPE_I386: 809 flavor = i386_THREAD_STATE; 810 break; 811 case CPU_TYPE_X86_64: 812 flavor = x86_THREAD_STATE64; 813 break; 814#endif 815 default: 816 return false; 817 } 818 return thread_get_state(target_thread, flavor, 819 state, count) == KERN_SUCCESS; 820} 821 822bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, 823 MDRawThread *thread) { 824 breakpad_thread_state_data_t state; 825 mach_msg_type_number_t state_count 826 = static_cast<mach_msg_type_number_t>(sizeof(state)); 827 828 if (GetThreadState(thread_id, state, &state_count)) { 829 if (!WriteStack(state, &thread->stack)) 830 return false; 831 832 memory_blocks_.push_back(thread->stack); 833 834 if (!WriteContext(state, &thread->thread_context)) 835 return false; 836 837 thread->thread_id = thread_id; 838 } else { 839 return false; 840 } 841 842 return true; 843} 844 845bool MinidumpGenerator::WriteThreadListStream( 846 MDRawDirectory *thread_list_stream) { 847 TypedMDRVA<MDRawThreadList> list(&writer_); 848 thread_act_port_array_t threads_for_task; 849 mach_msg_type_number_t thread_count; 850 int non_generator_thread_count; 851 852 if (task_threads(crashing_task_, &threads_for_task, &thread_count)) 853 return false; 854 855 // Don't include the generator thread 856 if (handler_thread_ != MACH_PORT_NULL) 857 non_generator_thread_count = thread_count - 1; 858 else 859 non_generator_thread_count = thread_count; 860 if (!list.AllocateObjectAndArray(non_generator_thread_count, 861 sizeof(MDRawThread))) 862 return false; 863 864 thread_list_stream->stream_type = MD_THREAD_LIST_STREAM; 865 thread_list_stream->location = list.location(); 866 867 list.get()->number_of_threads = non_generator_thread_count; 868 869 MDRawThread thread; 870 int thread_idx = 0; 871 872 for (unsigned int i = 0; i < thread_count; ++i) { 873 memset(&thread, 0, sizeof(MDRawThread)); 874 875 if (threads_for_task[i] != handler_thread_) { 876 if (!WriteThreadStream(threads_for_task[i], &thread)) 877 return false; 878 879 list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread)); 880 } 881 } 882 883 return true; 884} 885 886bool MinidumpGenerator::WriteMemoryListStream( 887 MDRawDirectory *memory_list_stream) { 888 TypedMDRVA<MDRawMemoryList> list(&writer_); 889 890 // If the dump has an exception, include some memory around the 891 // instruction pointer. 892 const size_t kIPMemorySize = 256; // bytes 893 bool have_ip_memory = false; 894 MDMemoryDescriptor ip_memory_d; 895 if (exception_thread_ && exception_type_) { 896 breakpad_thread_state_data_t state; 897 mach_msg_type_number_t stateCount 898 = static_cast<mach_msg_type_number_t>(sizeof(state)); 899 900 if (GetThreadState(exception_thread_, state, &stateCount)) { 901 u_int64_t ip = CurrentPCForStack(state); 902 // Bound it to the upper and lower bounds of the region 903 // it's contained within. If it's not in a known memory region, 904 // don't bother trying to write it. 905 mach_vm_address_t addr = static_cast<vm_address_t>(ip); 906 mach_vm_size_t size; 907 natural_t nesting_level = 0; 908 vm_region_submap_info_64 info; 909 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 910 vm_region_recurse_info_t recurse_info; 911 recurse_info = reinterpret_cast<vm_region_recurse_info_t>(&info); 912 913 kern_return_t ret = 914 mach_vm_region_recurse(crashing_task_, 915 &addr, 916 &size, 917 &nesting_level, 918 recurse_info, 919 &info_count); 920 if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) { 921 // Try to get 128 bytes before and after the IP, but 922 // settle for whatever's available. 923 ip_memory_d.start_of_memory_range = 924 std::max(uintptr_t(addr), 925 uintptr_t(ip - (kIPMemorySize / 2))); 926 uintptr_t end_of_range = 927 std::min(uintptr_t(ip + (kIPMemorySize / 2)), 928 uintptr_t(addr + size)); 929 ip_memory_d.memory.data_size = 930 end_of_range - 931 static_cast<uintptr_t>(ip_memory_d.start_of_memory_range); 932 have_ip_memory = true; 933 // This needs to get appended to the list even though 934 // the memory bytes aren't filled in yet so the entire 935 // list can be written first. The memory bytes will get filled 936 // in after the memory list is written. 937 memory_blocks_.push_back(ip_memory_d); 938 } 939 } 940 } 941 942 // Now fill in the memory list and write it. 943 unsigned memory_count = memory_blocks_.size(); 944 if (!list.AllocateObjectAndArray(memory_count, 945 sizeof(MDMemoryDescriptor))) 946 return false; 947 948 memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM; 949 memory_list_stream->location = list.location(); 950 951 list.get()->number_of_memory_ranges = memory_count; 952 953 unsigned int i; 954 for (i = 0; i < memory_count; ++i) { 955 list.CopyIndexAfterObject(i, &memory_blocks_[i], 956 sizeof(MDMemoryDescriptor)); 957 } 958 959 if (have_ip_memory) { 960 // Now read the memory around the instruction pointer. 961 UntypedMDRVA ip_memory(&writer_); 962 if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) 963 return false; 964 965 if (dynamic_images_) { 966 // Out-of-process. 967 vector<uint8_t> memory; 968 if (ReadTaskMemory(crashing_task_, 969 ip_memory_d.start_of_memory_range, 970 ip_memory_d.memory.data_size, 971 memory) != KERN_SUCCESS) { 972 return false; 973 } 974 975 ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size); 976 } else { 977 // In-process, just copy from local memory. 978 ip_memory.Copy( 979 reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range), 980 ip_memory_d.memory.data_size); 981 } 982 983 ip_memory_d.memory = ip_memory.location(); 984 // Write this again now that the data location is filled in. 985 list.CopyIndexAfterObject(i - 1, &ip_memory_d, 986 sizeof(MDMemoryDescriptor)); 987 } 988 989 return true; 990} 991 992bool 993MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) { 994 TypedMDRVA<MDRawExceptionStream> exception(&writer_); 995 996 if (!exception.Allocate()) 997 return false; 998 999 exception_stream->stream_type = MD_EXCEPTION_STREAM; 1000 exception_stream->location = exception.location(); 1001 MDRawExceptionStream *exception_ptr = exception.get(); 1002 exception_ptr->thread_id = exception_thread_; 1003 1004 // This naming is confusing, but it is the proper translation from 1005 // mach naming to minidump naming. 1006 exception_ptr->exception_record.exception_code = exception_type_; 1007 exception_ptr->exception_record.exception_flags = exception_code_; 1008 1009 breakpad_thread_state_data_t state; 1010 mach_msg_type_number_t state_count 1011 = static_cast<mach_msg_type_number_t>(sizeof(state)); 1012 1013 if (!GetThreadState(exception_thread_, state, &state_count)) 1014 return false; 1015 1016 if (!WriteContext(state, &exception_ptr->thread_context)) 1017 return false; 1018 1019 if (exception_type_ == EXC_BAD_ACCESS) 1020 exception_ptr->exception_record.exception_address = exception_subcode_; 1021 else 1022 exception_ptr->exception_record.exception_address = CurrentPCForStack(state); 1023 1024 return true; 1025} 1026 1027bool MinidumpGenerator::WriteSystemInfoStream( 1028 MDRawDirectory *system_info_stream) { 1029 TypedMDRVA<MDRawSystemInfo> info(&writer_); 1030 1031 if (!info.Allocate()) 1032 return false; 1033 1034 system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM; 1035 system_info_stream->location = info.location(); 1036 1037 // CPU Information 1038 uint32_t number_of_processors; 1039 size_t len = sizeof(number_of_processors); 1040 sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0); 1041 MDRawSystemInfo *info_ptr = info.get(); 1042 1043 switch (cpu_type_) { 1044#ifdef HAS_ARM_SUPPORT 1045 case CPU_TYPE_ARM: 1046 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM; 1047 break; 1048#endif 1049#ifdef HAS_PPC_SUPPORT 1050 case CPU_TYPE_POWERPC: 1051 case CPU_TYPE_POWERPC64: 1052 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; 1053 break; 1054#endif 1055#ifdef HAS_X86_SUPPORT 1056 case CPU_TYPE_I386: 1057 case CPU_TYPE_X86_64: 1058 if (cpu_type_ == CPU_TYPE_I386) 1059 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86; 1060 else 1061 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64; 1062#ifdef __i386__ 1063 // ebx is used for PIC code, so we need 1064 // to preserve it. 1065#define cpuid(op,eax,ebx,ecx,edx) \ 1066 asm ("pushl %%ebx \n\t" \ 1067 "cpuid \n\t" \ 1068 "movl %%ebx,%1 \n\t" \ 1069 "popl %%ebx" \ 1070 : "=a" (eax), \ 1071 "=g" (ebx), \ 1072 "=c" (ecx), \ 1073 "=d" (edx) \ 1074 : "0" (op)) 1075#elif defined(__x86_64__) 1076 1077#define cpuid(op,eax,ebx,ecx,edx) \ 1078 asm ("cpuid \n\t" \ 1079 : "=a" (eax), \ 1080 "=b" (ebx), \ 1081 "=c" (ecx), \ 1082 "=d" (edx) \ 1083 : "0" (op)) 1084#endif 1085 1086#if defined(__i386__) || defined(__x86_64__) 1087 int unused, unused2; 1088 // get vendor id 1089 cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0], 1090 info_ptr->cpu.x86_cpu_info.vendor_id[2], 1091 info_ptr->cpu.x86_cpu_info.vendor_id[1]); 1092 // get version and feature info 1093 cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, 1094 info_ptr->cpu.x86_cpu_info.feature_information); 1095 1096 // family 1097 info_ptr->processor_level = 1098 (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; 1099 // 0xMMSS (Model, Stepping) 1100 info_ptr->processor_revision = 1101 (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | 1102 ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); 1103 1104 // decode extended model info 1105 if (info_ptr->processor_level == 0xF || 1106 info_ptr->processor_level == 0x6) { 1107 info_ptr->processor_revision |= 1108 ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); 1109 } 1110 1111 // decode extended family info 1112 if (info_ptr->processor_level == 0xF) { 1113 info_ptr->processor_level += 1114 ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); 1115 } 1116 1117#endif // __i386__ || __x86_64_ 1118 break; 1119#endif // HAS_X86_SUPPORT 1120 default: 1121 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; 1122 break; 1123 } 1124 1125 info_ptr->number_of_processors = static_cast<uint8_t>(number_of_processors); 1126#if TARGET_OS_IPHONE 1127 info_ptr->platform_id = MD_OS_IOS; 1128#else 1129 info_ptr->platform_id = MD_OS_MAC_OS_X; 1130#endif // TARGET_OS_IPHONE 1131 1132 MDLocationDescriptor build_string_loc; 1133 1134 if (!writer_.WriteString(build_string_, 0, 1135 &build_string_loc)) 1136 return false; 1137 1138 info_ptr->csd_version_rva = build_string_loc.rva; 1139 info_ptr->major_version = os_major_version_; 1140 info_ptr->minor_version = os_minor_version_; 1141 info_ptr->build_number = os_build_number_; 1142 1143 return true; 1144} 1145 1146bool MinidumpGenerator::WriteModuleStream(unsigned int index, 1147 MDRawModule *module) { 1148 if (dynamic_images_) { 1149 // we're in a different process than the crashed process 1150 DynamicImage *image = dynamic_images_->GetImage(index); 1151 1152 if (!image) 1153 return false; 1154 1155 memset(module, 0, sizeof(MDRawModule)); 1156 1157 MDLocationDescriptor string_location; 1158 1159 string name = image->GetFilePath(); 1160 if (!writer_.WriteString(name.c_str(), 0, &string_location)) 1161 return false; 1162 1163 module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide(); 1164 module->size_of_image = static_cast<u_int32_t>(image->GetVMSize()); 1165 module->module_name_rva = string_location.rva; 1166 1167 // We'll skip the executable module, because they don't have 1168 // LC_ID_DYLIB load commands, and the crash processing server gets 1169 // version information from the Plist file, anyway. 1170 if (index != static_cast<uint32_t>(FindExecutableModule())) { 1171 module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE; 1172 module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION; 1173 // Convert MAC dylib version format, which is a 32 bit number, to the 1174 // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits> 1175 // so it fits nicely into the windows version with some massaging 1176 // The mapping is: 1177 // 1) upper 16 bits of MAC version go to lower 16 bits of product HI 1178 // 2) Next most significant 8 bits go to upper 16 bits of product LO 1179 // 3) Least significant 8 bits go to lower 16 bits of product LO 1180 uint32_t modVersion = image->GetVersion(); 1181 module->version_info.file_version_hi = 0; 1182 module->version_info.file_version_hi = modVersion >> 16; 1183 module->version_info.file_version_lo |= (modVersion & 0xff00) << 8; 1184 module->version_info.file_version_lo |= (modVersion & 0xff); 1185 } 1186 1187 if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) { 1188 return false; 1189 } 1190 } else { 1191 // Getting module info in the crashed process 1192 const breakpad_mach_header *header; 1193 header = (breakpad_mach_header*)_dyld_get_image_header(index); 1194 if (!header) 1195 return false; 1196 1197#ifdef __LP64__ 1198 assert(header->magic == MH_MAGIC_64); 1199 1200 if(header->magic != MH_MAGIC_64) 1201 return false; 1202#else 1203 assert(header->magic == MH_MAGIC); 1204 1205 if(header->magic != MH_MAGIC) 1206 return false; 1207#endif 1208 1209 int cpu_type = header->cputype; 1210 unsigned long slide = _dyld_get_image_vmaddr_slide(index); 1211 const char* name = _dyld_get_image_name(index); 1212 const struct load_command *cmd = 1213 reinterpret_cast<const struct load_command *>(header + 1); 1214 1215 memset(module, 0, sizeof(MDRawModule)); 1216 1217 for (unsigned int i = 0; cmd && (i < header->ncmds); i++) { 1218 if (cmd->cmd == LC_SEGMENT_ARCH) { 1219 1220 const breakpad_mach_segment_command *seg = 1221 reinterpret_cast<const breakpad_mach_segment_command *>(cmd); 1222 1223 if (!strcmp(seg->segname, "__TEXT")) { 1224 MDLocationDescriptor string_location; 1225 1226 if (!writer_.WriteString(name, 0, &string_location)) 1227 return false; 1228 1229 module->base_of_image = seg->vmaddr + slide; 1230 module->size_of_image = static_cast<u_int32_t>(seg->vmsize); 1231 module->module_name_rva = string_location.rva; 1232 1233 bool in_memory = false; 1234#if TARGET_OS_IPHONE 1235 in_memory = true; 1236#endif 1237 if (!WriteCVRecord(module, cpu_type, name, in_memory)) 1238 return false; 1239 1240 return true; 1241 } 1242 } 1243 1244 cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize); 1245 } 1246 } 1247 1248 return true; 1249} 1250 1251int MinidumpGenerator::FindExecutableModule() { 1252 if (dynamic_images_) { 1253 int index = dynamic_images_->GetExecutableImageIndex(); 1254 1255 if (index >= 0) { 1256 return index; 1257 } 1258 } else { 1259 int image_count = _dyld_image_count(); 1260 const struct mach_header *header; 1261 1262 for (int index = 0; index < image_count; ++index) { 1263 header = _dyld_get_image_header(index); 1264 1265 if (header->filetype == MH_EXECUTE) 1266 return index; 1267 } 1268 } 1269 1270 // failed - just use the first image 1271 return 0; 1272} 1273 1274bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, 1275 const char *module_path, bool in_memory) { 1276 TypedMDRVA<MDCVInfoPDB70> cv(&writer_); 1277 1278 // Only return the last path component of the full module path 1279 const char *module_name = strrchr(module_path, '/'); 1280 1281 // Increment past the slash 1282 if (module_name) 1283 ++module_name; 1284 else 1285 module_name = "<Unknown>"; 1286 1287 size_t module_name_length = strlen(module_name); 1288 1289 if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) 1290 return false; 1291 1292 if (!cv.CopyIndexAfterObject(0, module_name, module_name_length)) 1293 return false; 1294 1295 module->cv_record = cv.location(); 1296 MDCVInfoPDB70 *cv_ptr = cv.get(); 1297 cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; 1298 cv_ptr->age = 0; 1299 1300 // Get the module identifier 1301 unsigned char identifier[16]; 1302 bool result = false; 1303 if (in_memory) { 1304 MacFileUtilities::MachoID macho(module_path, 1305 reinterpret_cast<void *>(module->base_of_image), 1306 static_cast<size_t>(module->size_of_image)); 1307 result = macho.UUIDCommand(cpu_type, identifier); 1308 if (!result) 1309 result = macho.MD5(cpu_type, identifier); 1310 } 1311 1312 if (!result) { 1313 FileID file_id(module_path); 1314 result = file_id.MachoIdentifier(cpu_type, identifier); 1315 } 1316 1317 if (result) { 1318 cv_ptr->signature.data1 = 1319 static_cast<uint32_t>(identifier[0]) << 24 | 1320 static_cast<uint32_t>(identifier[1]) << 16 | 1321 static_cast<uint32_t>(identifier[2]) << 8 | 1322 static_cast<uint32_t>(identifier[3]); 1323 cv_ptr->signature.data2 = 1324 static_cast<uint16_t>(identifier[4] << 8) | identifier[5]; 1325 cv_ptr->signature.data3 = 1326 static_cast<uint16_t>(identifier[6] << 8) | identifier[7]; 1327 cv_ptr->signature.data4[0] = identifier[8]; 1328 cv_ptr->signature.data4[1] = identifier[9]; 1329 cv_ptr->signature.data4[2] = identifier[10]; 1330 cv_ptr->signature.data4[3] = identifier[11]; 1331 cv_ptr->signature.data4[4] = identifier[12]; 1332 cv_ptr->signature.data4[5] = identifier[13]; 1333 cv_ptr->signature.data4[6] = identifier[14]; 1334 cv_ptr->signature.data4[7] = identifier[15]; 1335 } 1336 1337 return true; 1338} 1339 1340bool MinidumpGenerator::WriteModuleListStream( 1341 MDRawDirectory *module_list_stream) { 1342 TypedMDRVA<MDRawModuleList> list(&writer_); 1343 1344 size_t image_count = dynamic_images_ ? 1345 static_cast<size_t>(dynamic_images_->GetImageCount()) : 1346 _dyld_image_count(); 1347 1348 if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) 1349 return false; 1350 1351 module_list_stream->stream_type = MD_MODULE_LIST_STREAM; 1352 module_list_stream->location = list.location(); 1353 list.get()->number_of_modules = image_count; 1354 1355 // Write out the executable module as the first one 1356 MDRawModule module; 1357 size_t executableIndex = FindExecutableModule(); 1358 1359 if (!WriteModuleStream(executableIndex, &module)) { 1360 return false; 1361 } 1362 1363 list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); 1364 int destinationIndex = 1; // Write all other modules after this one 1365 1366 for (size_t i = 0; i < image_count; ++i) { 1367 if (i != executableIndex) { 1368 if (!WriteModuleStream(i, &module)) { 1369 return false; 1370 } 1371 1372 list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE); 1373 } 1374 } 1375 1376 return true; 1377} 1378 1379bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) { 1380 TypedMDRVA<MDRawMiscInfo> info(&writer_); 1381 1382 if (!info.Allocate()) 1383 return false; 1384 1385 misc_info_stream->stream_type = MD_MISC_INFO_STREAM; 1386 misc_info_stream->location = info.location(); 1387 1388 MDRawMiscInfo *info_ptr = info.get(); 1389 info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo)); 1390 info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID | 1391 MD_MISCINFO_FLAGS1_PROCESS_TIMES | 1392 MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO; 1393 1394 // Process ID 1395 info_ptr->process_id = getpid(); 1396 1397 // Times 1398 struct rusage usage; 1399 if (getrusage(RUSAGE_SELF, &usage) != -1) { 1400 // Omit the fractional time since the MDRawMiscInfo only wants seconds 1401 info_ptr->process_user_time = 1402 static_cast<u_int32_t>(usage.ru_utime.tv_sec); 1403 info_ptr->process_kernel_time = 1404 static_cast<u_int32_t>(usage.ru_stime.tv_sec); 1405 } 1406 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 1407 static_cast<int>(info_ptr->process_id) }; 1408 u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0])); 1409 struct kinfo_proc proc; 1410 size_t size = sizeof(proc); 1411 if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) { 1412 info_ptr->process_create_time = 1413 static_cast<u_int32_t>(proc.kp_proc.p_starttime.tv_sec); 1414 } 1415 1416 // Speed 1417 uint64_t speed; 1418 const uint64_t kOneMillion = 1000 * 1000; 1419 size = sizeof(speed); 1420 sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0); 1421 info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion); 1422 info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion); 1423 size = sizeof(speed); 1424 sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0); 1425 info_ptr->processor_current_mhz = static_cast<u_int32_t>(speed / kOneMillion); 1426 1427 return true; 1428} 1429 1430bool MinidumpGenerator::WriteBreakpadInfoStream( 1431 MDRawDirectory *breakpad_info_stream) { 1432 TypedMDRVA<MDRawBreakpadInfo> info(&writer_); 1433 1434 if (!info.Allocate()) 1435 return false; 1436 1437 breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM; 1438 breakpad_info_stream->location = info.location(); 1439 MDRawBreakpadInfo *info_ptr = info.get(); 1440 1441 if (exception_thread_ && exception_type_) { 1442 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | 1443 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; 1444 info_ptr->dump_thread_id = handler_thread_; 1445 info_ptr->requesting_thread_id = exception_thread_; 1446 } else { 1447 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID; 1448 info_ptr->dump_thread_id = handler_thread_; 1449 info_ptr->requesting_thread_id = 0; 1450 } 1451 1452 return true; 1453} 1454 1455} // namespace google_breakpad 1456