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