minidump_generator.cc revision 9dcc03f47d10a9b34fbeb80fcb4d2fce968a5c67
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 <cstdio> 31 32#include <mach/host_info.h> 33#include <mach/mach_vm.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#include <mach/mach_vm.h> 40 41#include <CoreFoundation/CoreFoundation.h> 42 43#include "client/mac/handler/minidump_generator.h" 44#include "client/minidump_file_writer-inl.h" 45#include "common/mac/file_id.h" 46#include "common/mac/string_utilities.h" 47 48using MacStringUtils::ConvertToString; 49using MacStringUtils::IntegerValueAtIndex; 50 51namespace google_breakpad { 52 53// constructor when generating from within the crashed process 54MinidumpGenerator::MinidumpGenerator() 55 : exception_type_(0), 56 exception_code_(0), 57 exception_subcode_(0), 58 exception_thread_(0), 59 crashing_task_(mach_task_self()), 60 handler_thread_(mach_thread_self()), 61 dynamic_images_(NULL) { 62 GatherSystemInformation(); 63} 64 65// constructor when generating from a different process than the 66// crashed process 67MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, 68 mach_port_t handler_thread) 69 : exception_type_(0), 70 exception_code_(0), 71 exception_subcode_(0), 72 exception_thread_(0), 73 crashing_task_(crashing_task), 74 handler_thread_(handler_thread) { 75 if (crashing_task != mach_task_self()) { 76 dynamic_images_ = new DynamicImages(crashing_task_); 77 } else { 78 dynamic_images_ = NULL; 79 } 80 81 GatherSystemInformation(); 82} 83 84MinidumpGenerator::~MinidumpGenerator() { 85 delete dynamic_images_; 86} 87 88char MinidumpGenerator::build_string_[16]; 89int MinidumpGenerator::os_major_version_ = 0; 90int MinidumpGenerator::os_minor_version_ = 0; 91int MinidumpGenerator::os_build_number_ = 0; 92 93// static 94void MinidumpGenerator::GatherSystemInformation() { 95 // If this is non-zero, then we've already gathered the information 96 if (os_major_version_) 97 return; 98 99 // This code extracts the version and build information from the OS 100 CFStringRef vers_path = 101 CFSTR("/System/Library/CoreServices/SystemVersion.plist"); 102 CFURLRef sys_vers = 103 CFURLCreateWithFileSystemPath(NULL, 104 vers_path, 105 kCFURLPOSIXPathStyle, 106 false); 107 CFDataRef data; 108 SInt32 error; 109 CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL, 110 &error); 111 112 if (!data) 113 return; 114 115 CFDictionaryRef list = static_cast<CFDictionaryRef> 116 (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, 117 NULL)); 118 if (!list) 119 return; 120 121 CFStringRef build_version = static_cast<CFStringRef> 122 (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); 123 CFStringRef product_version = static_cast<CFStringRef> 124 (CFDictionaryGetValue(list, CFSTR("ProductVersion"))); 125 string build_str = ConvertToString(build_version); 126 string product_str = ConvertToString(product_version); 127 128 CFRelease(list); 129 CFRelease(sys_vers); 130 CFRelease(data); 131 132 strlcpy(build_string_, build_str.c_str(), sizeof(build_string_)); 133 134 // Parse the string that looks like "10.4.8" 135 os_major_version_ = IntegerValueAtIndex(product_str, 0); 136 os_minor_version_ = IntegerValueAtIndex(product_str, 1); 137 os_build_number_ = IntegerValueAtIndex(product_str, 2); 138} 139 140string MinidumpGenerator::UniqueNameInDirectory(const string &dir, 141 string *unique_name) { 142 CFUUIDRef uuid = CFUUIDCreate(NULL); 143 CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid); 144 CFRelease(uuid); 145 string file_name(ConvertToString(uuid_cfstr)); 146 CFRelease(uuid_cfstr); 147 string path(dir); 148 149 // Ensure that the directory (if non-empty) has a trailing slash so that 150 // we can append the file name and have a valid pathname. 151 if (!dir.empty()) { 152 if (dir.at(dir.size() - 1) != '/') 153 path.append(1, '/'); 154 } 155 156 path.append(file_name); 157 path.append(".dmp"); 158 159 if (unique_name) 160 *unique_name = file_name; 161 162 return path; 163} 164 165bool MinidumpGenerator::Write(const char *path) { 166 WriteStreamFN writers[] = { 167 &MinidumpGenerator::WriteThreadListStream, 168 &MinidumpGenerator::WriteSystemInfoStream, 169 &MinidumpGenerator::WriteModuleListStream, 170 &MinidumpGenerator::WriteMiscInfoStream, 171 &MinidumpGenerator::WriteBreakpadInfoStream, 172 // Exception stream needs to be the last entry in this array as it may 173 // be omitted in the case where the minidump is written without an 174 // exception. 175 &MinidumpGenerator::WriteExceptionStream, 176 }; 177 bool result = false; 178 179 // If opening was successful, create the header, directory, and call each 180 // writer. The destructor for the TypedMDRVAs will cause the data to be 181 // flushed. The destructor for the MinidumpFileWriter will close the file. 182 if (writer_.Open(path)) { 183 TypedMDRVA<MDRawHeader> header(&writer_); 184 TypedMDRVA<MDRawDirectory> dir(&writer_); 185 186 if (!header.Allocate()) 187 return false; 188 189 int writer_count = sizeof(writers) / sizeof(writers[0]); 190 191 // If we don't have exception information, don't write out the 192 // exception stream 193 if (!exception_thread_ && !exception_type_) 194 --writer_count; 195 196 // Add space for all writers 197 if (!dir.AllocateArray(writer_count)) 198 return false; 199 200 MDRawHeader *header_ptr = header.get(); 201 header_ptr->signature = MD_HEADER_SIGNATURE; 202 header_ptr->version = MD_HEADER_VERSION; 203 time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp))); 204 header_ptr->stream_count = writer_count; 205 header_ptr->stream_directory_rva = dir.position(); 206 207 MDRawDirectory local_dir; 208 result = true; 209 for (int i = 0; (result) && (i < writer_count); ++i) { 210 result = (this->*writers[i])(&local_dir); 211 212 if (result) 213 dir.CopyIndex(i, &local_dir); 214 } 215 } 216 return result; 217} 218 219size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) { 220 mach_vm_address_t stack_region_base = start_addr; 221 mach_vm_size_t stack_region_size; 222 natural_t nesting_level = 0; 223 vm_region_submap_info_64 submap_info; 224 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; 225 226 vm_region_recurse_info_t region_info; 227 region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info); 228 229 if (start_addr == 0) { 230 return 0; 231 } 232 233 kern_return_t result = 234 mach_vm_region_recurse(crashing_task_, &stack_region_base, 235 &stack_region_size, &nesting_level, 236 region_info, 237 &info_count); 238 239 if (start_addr < stack_region_base) { 240 // probably stack corruption, since mach_vm_region had to go 241 // higher in the process address space to find a valid region. 242 return 0; 243 } 244 245 246 if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) { 247 // The stack for thread 0 needs to extend all the way to 248 // 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER, 249 // for many processes, the stack is first created in one page 250 // below this, and is then later extended to a much larger size by 251 // creating a new VM region immediately below the initial page. 252 253 // You can see this for yourself by running vmmap on a "hello, 254 // world" program 255 256 // Because of the above, we'll add 4k to include the original 257 // stack frame page. 258 // This method of finding the stack region needs to be done in 259 // a better way; the breakpad issue 247 is tracking this. 260 stack_region_size += 0x1000; 261 } 262 263 return result == KERN_SUCCESS ? 264 stack_region_base + stack_region_size - start_addr : 0; 265} 266 267bool MinidumpGenerator::WriteStackFromStartAddress( 268 mach_vm_address_t start_addr, 269 MDMemoryDescriptor *stack_location) { 270 UntypedMDRVA memory(&writer_); 271 272 bool result = false; 273 size_t size = CalculateStackSize(start_addr); 274 275 if (size == 0) { 276 // In some situations the stack address for the thread can come back 0. 277 // In these cases we skip over the threads in question and stuff the 278 // stack with a clearly borked value. 279 start_addr = 0xDEADBEEF; 280 size = 16; 281 if (!memory.Allocate(size)) 282 return false; 283 284 unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of 285 // junk. 286 dummy_stack[0] = 0xDEADBEEF; 287 dummy_stack[1] = 0xDEADBEEF; 288 289 result = memory.Copy(dummy_stack, size); 290 } else { 291 292 if (!memory.Allocate(size)) 293 return false; 294 295 if (dynamic_images_) { 296 297 kern_return_t kr; 298 299 void *stack_memory = ReadTaskMemory(crashing_task_, 300 (void*)start_addr, 301 size, 302 &kr); 303 304 if (stack_memory == NULL) { 305 return false; 306 } 307 308 result = memory.Copy(stack_memory, size); 309 free(stack_memory); 310 } else { 311 result = memory.Copy(reinterpret_cast<const void *>(start_addr), size); 312 } 313 } 314 315 stack_location->start_of_memory_range = start_addr; 316 stack_location->memory = memory.location(); 317 318 return result; 319} 320 321#if TARGET_CPU_PPC || TARGET_CPU_PPC64 322bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, 323 MDMemoryDescriptor *stack_location) { 324 breakpad_thread_state_t *machine_state = 325 reinterpret_cast<breakpad_thread_state_t *>(state); 326 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); 327 return WriteStackFromStartAddress(start_addr, stack_location); 328} 329 330u_int64_t 331MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) { 332 breakpad_thread_state_t *machine_state = 333 reinterpret_cast<breakpad_thread_state_t *>(state); 334 335 return REGISTER_FROM_THREADSTATE(machine_state, srr0); 336} 337 338bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, 339 MDLocationDescriptor *register_location) { 340 TypedMDRVA<MinidumpContext> context(&writer_); 341 breakpad_thread_state_t *machine_state = 342 reinterpret_cast<breakpad_thread_state_t *>(state); 343 344 if (!context.Allocate()) 345 return false; 346 347 *register_location = context.location(); 348 MinidumpContext *context_ptr = context.get(); 349 context_ptr->context_flags = MD_CONTEXT_PPC_BASE; 350 351#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 352#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) 353 354 AddReg(srr0); 355 AddReg(cr); 356 AddReg(xer); 357 AddReg(ctr); 358 AddReg(lr); 359 AddReg(vrsave); 360 361 AddGPR(0); 362 AddGPR(1); 363 AddGPR(2); 364 AddGPR(3); 365 AddGPR(4); 366 AddGPR(5); 367 AddGPR(6); 368 AddGPR(7); 369 AddGPR(8); 370 AddGPR(9); 371 AddGPR(10); 372 AddGPR(11); 373 AddGPR(12); 374 AddGPR(13); 375 AddGPR(14); 376 AddGPR(15); 377 AddGPR(16); 378 AddGPR(17); 379 AddGPR(18); 380 AddGPR(19); 381 AddGPR(20); 382 AddGPR(21); 383 AddGPR(22); 384 AddGPR(23); 385 AddGPR(24); 386 AddGPR(25); 387 AddGPR(26); 388 AddGPR(27); 389 AddGPR(28); 390 AddGPR(29); 391 AddGPR(30); 392 AddGPR(31); 393 394#if TARGET_CPU_PPC 395 /* The mq register is only for PPC */ 396 AddReg(mq); 397#endif 398 399 400 return true; 401} 402 403#elif TARGET_CPU_X86 || TARGET_CPU_X86_64 404 405bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, 406 MDMemoryDescriptor *stack_location) { 407 breakpad_thread_state_t *machine_state = 408 reinterpret_cast<breakpad_thread_state_t *>(state); 409 410#if TARGET_CPU_X86_64 411 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp); 412#else 413 mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp); 414#endif 415 return WriteStackFromStartAddress(start_addr, stack_location); 416} 417 418u_int64_t 419MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) { 420 breakpad_thread_state_t *machine_state = 421 reinterpret_cast<breakpad_thread_state_t *>(state); 422 423#if TARGET_CPU_X86_64 424 return REGISTER_FROM_THREADSTATE(machine_state, rip); 425#else 426 return REGISTER_FROM_THREADSTATE(machine_state, eip); 427#endif 428} 429 430bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, 431 MDLocationDescriptor *register_location) { 432 TypedMDRVA<MinidumpContext> context(&writer_); 433 breakpad_thread_state_t *machine_state = 434 reinterpret_cast<breakpad_thread_state_t *>(state); 435 436 if (!context.Allocate()) 437 return false; 438 439 *register_location = context.location(); 440 MinidumpContext *context_ptr = context.get(); 441 442#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) 443#if TARGET_CPU_X86 444 context_ptr->context_flags = MD_CONTEXT_X86; 445 AddReg(eax); 446 AddReg(ebx); 447 AddReg(ecx); 448 AddReg(edx); 449 AddReg(esi); 450 AddReg(edi); 451 AddReg(ebp); 452 AddReg(esp); 453 454 AddReg(cs); 455 AddReg(ds); 456 AddReg(ss); 457 AddReg(es); 458 AddReg(fs); 459 AddReg(gs); 460 AddReg(eflags); 461 462 AddReg(eip); 463#else 464 context_ptr->context_flags = MD_CONTEXT_AMD64; 465 AddReg(rax); 466 AddReg(rbx); 467 AddReg(rcx); 468 AddReg(rdx); 469 AddReg(rdi); 470 AddReg(rsi); 471 AddReg(rbp); 472 AddReg(rsp); 473 AddReg(r8); 474 AddReg(r9); 475 AddReg(r10); 476 AddReg(r11); 477 AddReg(r12); 478 AddReg(r13); 479 AddReg(r14); 480 AddReg(r15); 481 AddReg(rip); 482 // according to AMD's software developer guide, bits above 18 are 483 // not used in the flags register. Since the minidump format 484 // specifies 32 bits for the flags register, we can truncate safely 485 // with no loss. 486 context_ptr->eflags = machine_state->__rflags; 487 AddReg(cs); 488 AddReg(fs); 489 AddReg(gs); 490#endif 491#undef AddReg(a) 492 493 return true; 494} 495#endif 496 497bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, 498 MDRawThread *thread) { 499 breakpad_thread_state_data_t state; 500 mach_msg_type_number_t state_count = sizeof(state); 501 502 if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE, 503 state, &state_count) == 504 KERN_SUCCESS) { 505 if (!WriteStack(state, &thread->stack)) 506 return false; 507 508 if (!WriteContext(state, &thread->thread_context)) 509 return false; 510 511 thread->thread_id = thread_id; 512 } else { 513 return false; 514 } 515 516 return true; 517} 518 519bool MinidumpGenerator::WriteThreadListStream( 520 MDRawDirectory *thread_list_stream) { 521 TypedMDRVA<MDRawThreadList> list(&writer_); 522 thread_act_port_array_t threads_for_task; 523 mach_msg_type_number_t thread_count; 524 int non_generator_thread_count; 525 526 if (task_threads(crashing_task_, &threads_for_task, &thread_count)) 527 return false; 528 529 // Don't include the generator thread 530 non_generator_thread_count = thread_count - 1; 531 if (!list.AllocateObjectAndArray(non_generator_thread_count, 532 sizeof(MDRawThread))) 533 return false; 534 535 thread_list_stream->stream_type = MD_THREAD_LIST_STREAM; 536 thread_list_stream->location = list.location(); 537 538 list.get()->number_of_threads = non_generator_thread_count; 539 540 MDRawThread thread; 541 int thread_idx = 0; 542 543 for (unsigned int i = 0; i < thread_count; ++i) { 544 memset(&thread, 0, sizeof(MDRawThread)); 545 546 if (threads_for_task[i] != handler_thread_) { 547 if (!WriteThreadStream(threads_for_task[i], &thread)) 548 return false; 549 550 list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread)); 551 } 552 } 553 554 return true; 555} 556 557bool 558MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) { 559 TypedMDRVA<MDRawExceptionStream> exception(&writer_); 560 561 if (!exception.Allocate()) 562 return false; 563 564 exception_stream->stream_type = MD_EXCEPTION_STREAM; 565 exception_stream->location = exception.location(); 566 MDRawExceptionStream *exception_ptr = exception.get(); 567 exception_ptr->thread_id = exception_thread_; 568 569 // This naming is confusing, but it is the proper translation from 570 // mach naming to minidump naming. 571 exception_ptr->exception_record.exception_code = exception_type_; 572 exception_ptr->exception_record.exception_flags = exception_code_; 573 574 breakpad_thread_state_data_t state; 575 mach_msg_type_number_t stateCount = sizeof(state); 576 577 if (thread_get_state(exception_thread_, 578 BREAKPAD_MACHINE_THREAD_STATE, 579 state, 580 &stateCount) != KERN_SUCCESS) 581 return false; 582 583 if (!WriteContext(state, &exception_ptr->thread_context)) 584 return false; 585 586 if (exception_type_ == EXC_BAD_ACCESS) 587 exception_ptr->exception_record.exception_address = exception_subcode_; 588 else 589 exception_ptr->exception_record.exception_address = CurrentPCForStack(state); 590 591 return true; 592} 593 594bool MinidumpGenerator::WriteSystemInfoStream( 595 MDRawDirectory *system_info_stream) { 596 TypedMDRVA<MDRawSystemInfo> info(&writer_); 597 598 if (!info.Allocate()) 599 return false; 600 601 system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM; 602 system_info_stream->location = info.location(); 603 604 // CPU Information 605 uint32_t cpu_type; 606 size_t len = sizeof(cpu_type); 607 sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0); 608 uint32_t number_of_processors; 609 len = sizeof(number_of_processors); 610 sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0); 611 MDRawSystemInfo *info_ptr = info.get(); 612 613 switch (cpu_type) { 614 case CPU_TYPE_POWERPC: 615 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; 616 break; 617 case CPU_TYPE_I386: 618 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86; 619#ifdef __i386__ 620 // ebx is used for PIC code, so we need 621 // to preserve it. 622#define cpuid(op,eax,ebx,ecx,edx) \ 623 asm ("pushl %%ebx \n\t" \ 624 "cpuid \n\t" \ 625 "movl %%ebx,%1 \n\t" \ 626 "popl %%ebx" \ 627 : "=a" (eax), \ 628 "=g" (ebx), \ 629 "=c" (ecx), \ 630 "=d" (edx) \ 631 : "0" (op)) 632 int unused, unused2; 633 // get vendor id 634 cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0], 635 info_ptr->cpu.x86_cpu_info.vendor_id[2], 636 info_ptr->cpu.x86_cpu_info.vendor_id[1]); 637 // get version and feature info 638 cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, 639 info_ptr->cpu.x86_cpu_info.feature_information); 640 641 // family 642 info_ptr->processor_level = 643 (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; 644 // 0xMMSS (Model, Stepping) 645 info_ptr->processor_revision = 646 (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | 647 ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); 648 649 // decode extended model info 650 if (info_ptr->processor_level == 0xF || 651 info_ptr->processor_level == 0x6) { 652 info_ptr->processor_revision |= 653 ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); 654 } 655 656 // decode extended family info 657 if (info_ptr->processor_level == 0xF) { 658 info_ptr->processor_level += 659 ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); 660 } 661 662#endif // __i386__ 663 break; 664 default: 665 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; 666 break; 667 } 668 669 info_ptr->number_of_processors = number_of_processors; 670 info_ptr->platform_id = MD_OS_MAC_OS_X; 671 672 MDLocationDescriptor build_string_loc; 673 674 if (!writer_.WriteString(build_string_, 0, 675 &build_string_loc)) 676 return false; 677 678 info_ptr->csd_version_rva = build_string_loc.rva; 679 info_ptr->major_version = os_major_version_; 680 info_ptr->minor_version = os_minor_version_; 681 info_ptr->build_number = os_build_number_; 682 683 return true; 684} 685 686bool MinidumpGenerator::WriteModuleStream(unsigned int index, 687 MDRawModule *module) { 688 if (dynamic_images_) { 689 // we're in a different process than the crashed process 690 DynamicImage *image = dynamic_images_->GetImage(index); 691 692 if (!image) 693 return false; 694 695 const breakpad_mach_header *header = image->GetMachHeader(); 696 697 if (!header) 698 return false; 699 700 int cpu_type = header->cputype; 701 702 memset(module, 0, sizeof(MDRawModule)); 703 704 MDLocationDescriptor string_location; 705 706 const char* name = image->GetFilePath(); 707 if (!writer_.WriteString(name, 0, &string_location)) 708 return false; 709 710 module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide(); 711 module->size_of_image = image->GetVMSize(); 712 module->module_name_rva = string_location.rva; 713 714 // We'll skip the executable module, because they don't have 715 // LC_ID_DYLIB load commands, and the crash processing server gets 716 // version information from the Plist file, anyway. 717 if (index != (uint32_t)FindExecutableModule()) { 718 module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE; 719 module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION; 720 // Convert MAC dylib version format, which is a 32 bit number, to the 721 // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits> 722 // so it fits nicely into the windows version with some massaging 723 // The mapping is: 724 // 1) upper 16 bits of MAC version go to lower 16 bits of product HI 725 // 2) Next most significant 8 bits go to upper 16 bits of product LO 726 // 3) Least significant 8 bits go to lower 16 bits of product LO 727 uint32_t modVersion = image->GetVersion(); 728 module->version_info.file_version_hi = 0; 729 module->version_info.file_version_hi = modVersion >> 16; 730 module->version_info.file_version_lo |= (modVersion & 0xff00) << 8; 731 module->version_info.file_version_lo |= (modVersion & 0xff); 732 } 733 734 if (!WriteCVRecord(module, cpu_type, name)) { 735 return false; 736 } 737 } else { 738 // we're getting module info in the crashed process 739 740 const breakpad_mach_header *header; 741 header = (breakpad_mach_header*)_dyld_get_image_header(index); 742 if (!header) 743 return false; 744 745#ifdef __LP64__ 746 assert(header->magic == MH_MAGIC_64); 747 748 if(header->magic != MH_MAGIC_64) 749 return false; 750#else 751 assert(header->magic == MH_MAGIC); 752 753 if(header->magic != MH_MAGIC) 754 return false; 755#endif 756 757 int cpu_type = header->cputype; 758 unsigned long slide = _dyld_get_image_vmaddr_slide(index); 759 const char* name = _dyld_get_image_name(index); 760 const struct load_command *cmd = 761 reinterpret_cast<const struct load_command *>(header + 1); 762 763 memset(module, 0, sizeof(MDRawModule)); 764 765 for (unsigned int i = 0; cmd && (i < header->ncmds); i++) { 766 if (cmd->cmd == LC_SEGMENT) { 767 768 const breakpad_mach_segment_command *seg = 769 reinterpret_cast<const breakpad_mach_segment_command *>(cmd); 770 771 if (!strcmp(seg->segname, "__TEXT")) { 772 MDLocationDescriptor string_location; 773 774 if (!writer_.WriteString(name, 0, &string_location)) 775 return false; 776 777 module->base_of_image = seg->vmaddr + slide; 778 module->size_of_image = seg->vmsize; 779 module->module_name_rva = string_location.rva; 780 781 if (!WriteCVRecord(module, cpu_type, name)) 782 return false; 783 784 return true; 785 } 786 } 787 788 cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize); 789 } 790 } 791 792 return true; 793} 794 795int MinidumpGenerator::FindExecutableModule() { 796 if (dynamic_images_) { 797 int index = dynamic_images_->GetExecutableImageIndex(); 798 799 if (index >= 0) { 800 return index; 801 } 802 } else { 803 int image_count = _dyld_image_count(); 804 const struct mach_header *header; 805 806 for (int index = 0; index < image_count; ++index) { 807 header = _dyld_get_image_header(index); 808 809 if (header->filetype == MH_EXECUTE) 810 return index; 811 } 812 } 813 814 // failed - just use the first image 815 return 0; 816} 817 818bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, 819 const char *module_path) { 820 TypedMDRVA<MDCVInfoPDB70> cv(&writer_); 821 822 // Only return the last path component of the full module path 823 const char *module_name = strrchr(module_path, '/'); 824 825 // Increment past the slash 826 if (module_name) 827 ++module_name; 828 else 829 module_name = "<Unknown>"; 830 831 size_t module_name_length = strlen(module_name); 832 833 if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) 834 return false; 835 836 if (!cv.CopyIndexAfterObject(0, module_name, module_name_length)) 837 return false; 838 839 module->cv_record = cv.location(); 840 MDCVInfoPDB70 *cv_ptr = cv.get(); 841 cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; 842 cv_ptr->age = 0; 843 844 // Get the module identifier 845 FileID file_id(module_path); 846 unsigned char identifier[16]; 847 848 if (file_id.MachoIdentifier(cpu_type, identifier)) { 849 cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | 850 (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | 851 (uint32_t)identifier[3]; 852 cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; 853 cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; 854 cv_ptr->signature.data4[0] = identifier[8]; 855 cv_ptr->signature.data4[1] = identifier[9]; 856 cv_ptr->signature.data4[2] = identifier[10]; 857 cv_ptr->signature.data4[3] = identifier[11]; 858 cv_ptr->signature.data4[4] = identifier[12]; 859 cv_ptr->signature.data4[5] = identifier[13]; 860 cv_ptr->signature.data4[6] = identifier[14]; 861 cv_ptr->signature.data4[7] = identifier[15]; 862 } 863 864 return true; 865} 866 867bool MinidumpGenerator::WriteModuleListStream( 868 MDRawDirectory *module_list_stream) { 869 TypedMDRVA<MDRawModuleList> list(&writer_); 870 871 int image_count = dynamic_images_ ? 872 dynamic_images_->GetImageCount() : _dyld_image_count(); 873 874 if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) 875 return false; 876 877 module_list_stream->stream_type = MD_MODULE_LIST_STREAM; 878 module_list_stream->location = list.location(); 879 list.get()->number_of_modules = image_count; 880 881 // Write out the executable module as the first one 882 MDRawModule module; 883 int executableIndex = FindExecutableModule(); 884 885 if (!WriteModuleStream(executableIndex, &module)) { 886 return false; 887 } 888 889 list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); 890 int destinationIndex = 1; // Write all other modules after this one 891 892 for (int i = 0; i < image_count; ++i) { 893 if (i != executableIndex) { 894 if (!WriteModuleStream(i, &module)) { 895 return false; 896 } 897 898 list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE); 899 } 900 } 901 902 return true; 903} 904 905bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) { 906 TypedMDRVA<MDRawMiscInfo> info(&writer_); 907 908 if (!info.Allocate()) 909 return false; 910 911 misc_info_stream->stream_type = MD_MISC_INFO_STREAM; 912 misc_info_stream->location = info.location(); 913 914 MDRawMiscInfo *info_ptr = info.get(); 915 info_ptr->size_of_info = sizeof(MDRawMiscInfo); 916 info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID | 917 MD_MISCINFO_FLAGS1_PROCESS_TIMES | 918 MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO; 919 920 // Process ID 921 info_ptr->process_id = getpid(); 922 923 // Times 924 struct rusage usage; 925 if (getrusage(RUSAGE_SELF, &usage) != -1) { 926 // Omit the fractional time since the MDRawMiscInfo only wants seconds 927 info_ptr->process_user_time = usage.ru_utime.tv_sec; 928 info_ptr->process_kernel_time = usage.ru_stime.tv_sec; 929 } 930 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id }; 931 size_t size; 932 if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) { 933 mach_vm_address_t addr; 934 if (mach_vm_allocate(mach_task_self(), 935 &addr, 936 size, 937 true) == KERN_SUCCESS) { 938 struct kinfo_proc *proc = (struct kinfo_proc *)addr; 939 if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0)) 940 info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec; 941 mach_vm_deallocate(mach_task_self(), addr, size); 942 } 943 } 944 945 // Speed 946 uint64_t speed; 947 size = sizeof(speed); 948 sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0); 949 info_ptr->processor_max_mhz = speed / (1000 * 1000); 950 info_ptr->processor_mhz_limit = speed / (1000 * 1000); 951 size = sizeof(speed); 952 sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0); 953 info_ptr->processor_current_mhz = speed / (1000 * 1000); 954 955 return true; 956} 957 958bool MinidumpGenerator::WriteBreakpadInfoStream( 959 MDRawDirectory *breakpad_info_stream) { 960 TypedMDRVA<MDRawBreakpadInfo> info(&writer_); 961 962 if (!info.Allocate()) 963 return false; 964 965 breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM; 966 breakpad_info_stream->location = info.location(); 967 MDRawBreakpadInfo *info_ptr = info.get(); 968 969 if (exception_thread_ && exception_type_) { 970 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | 971 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; 972 info_ptr->dump_thread_id = handler_thread_; 973 info_ptr->requesting_thread_id = exception_thread_; 974 } else { 975 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID; 976 info_ptr->dump_thread_id = handler_thread_; 977 info_ptr->requesting_thread_id = 0; 978 } 979 980 return true; 981} 982 983} // namespace google_breakpad 984