minidump_generator.cc revision 32d40647362a014f2184a6f4d2c8e0453eb8f655
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/vm_statistics.h> 34#include <mach-o/dyld.h> 35#include <mach-o/loader.h> 36#include <sys/sysctl.h> 37#include <sys/resource.h> 38 39#include <CoreFoundation/CoreFoundation.h> 40 41#include "client/mac/handler/minidump_generator.h" 42#include "client/minidump_file_writer-inl.h" 43#include "common/mac/file_id.h" 44#include "common/mac/string_utilities.h" 45 46using MacStringUtils::ConvertToString; 47using MacStringUtils::IntegerValueAtIndex; 48 49namespace google_breakpad { 50 51MinidumpGenerator::MinidumpGenerator() 52 : exception_type_(0), 53 exception_code_(0), 54 exception_thread_(0), 55 crashing_task_(mach_task_self()), 56 handler_thread_(mach_thread_self()) { 57 dynamic_images_ = new DynamicImages(mach_task_self()); 58 GatherSystemInformation(); 59} 60 61MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread) 62 : exception_type_(0), 63 exception_code_(0), 64 exception_thread_(0), 65 crashing_task_(crashing_task), 66 handler_thread_(handler_thread) { 67 dynamic_images_ = new DynamicImages(crashing_task_); 68 GatherSystemInformation(); 69} 70 71MinidumpGenerator::~MinidumpGenerator() { 72} 73 74char MinidumpGenerator::build_string_[16]; 75int MinidumpGenerator::os_major_version_ = 0; 76int MinidumpGenerator::os_minor_version_ = 0; 77int MinidumpGenerator::os_build_number_ = 0; 78 79// static 80void MinidumpGenerator::GatherSystemInformation() { 81 // If this is non-zero, then we've already gathered the information 82 if (os_major_version_) 83 return; 84 85 // This code extracts the version and build information from the OS 86 CFStringRef vers_path = 87 CFSTR("/System/Library/CoreServices/SystemVersion.plist"); 88 CFURLRef sys_vers = 89 CFURLCreateWithFileSystemPath(NULL, vers_path, kCFURLPOSIXPathStyle, false); 90 CFDataRef data; 91 SInt32 error; 92 CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL, 93 &error); 94 95 if (!data) 96 return; 97 98 CFDictionaryRef list = static_cast<CFDictionaryRef> 99 (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, 100 NULL)); 101 if (!list) 102 return; 103 104 CFStringRef build_version = static_cast<CFStringRef> 105 (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); 106 CFStringRef product_version = static_cast<CFStringRef> 107 (CFDictionaryGetValue(list, CFSTR("ProductVersion"))); 108 string build_str = ConvertToString(build_version); 109 string product_str = ConvertToString(product_version); 110 111 CFRelease(list); 112 CFRelease(sys_vers); 113 CFRelease(data); 114 115 strlcpy(build_string_, build_str.c_str(), sizeof(build_string_)); 116 117 // Parse the string that looks like "10.4.8" 118 os_major_version_ = IntegerValueAtIndex(product_str, 0); 119 os_minor_version_ = IntegerValueAtIndex(product_str, 1); 120 os_build_number_ = IntegerValueAtIndex(product_str, 2); 121} 122 123string MinidumpGenerator::UniqueNameInDirectory(const string &dir, 124 string *unique_name) { 125 CFUUIDRef uuid = CFUUIDCreate(NULL); 126 CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid); 127 CFRelease(uuid); 128 string file_name(ConvertToString(uuid_cfstr)); 129 CFRelease(uuid_cfstr); 130 string path(dir); 131 132 // Ensure that the directory (if non-empty) has a trailing slash so that 133 // we can append the file name and have a valid pathname. 134 if (!dir.empty()) { 135 if (dir.at(dir.size() - 1) != '/') 136 path.append(1, '/'); 137 } 138 139 path.append(file_name); 140 path.append(".dmp"); 141 142 if (unique_name) 143 *unique_name = file_name; 144 145 return path; 146} 147 148bool MinidumpGenerator::Write(const char *path) { 149 WriteStreamFN writers[] = { 150 &MinidumpGenerator::WriteThreadListStream, 151 &MinidumpGenerator::WriteSystemInfoStream, 152 &MinidumpGenerator::WriteModuleListStream, 153 &MinidumpGenerator::WriteMiscInfoStream, 154 &MinidumpGenerator::WriteBreakpadInfoStream, 155 // Exception stream needs to be the last entry in this array as it may 156 // be omitted in the case where the minidump is written without an 157 // exception. 158 &MinidumpGenerator::WriteExceptionStream, 159 }; 160 bool result = true; 161 162 // If opening was successful, create the header, directory, and call each 163 // writer. The destructor for the TypedMDRVAs will cause the data to be 164 // flushed. The destructor for the MinidumpFileWriter will close the file. 165 if (writer_.Open(path)) { 166 TypedMDRVA<MDRawHeader> header(&writer_); 167 TypedMDRVA<MDRawDirectory> dir(&writer_); 168 169 if (!header.Allocate()) 170 return false; 171 172 int writer_count = sizeof(writers) / sizeof(writers[0]); 173 174 // If we don't have exception information, don't write out the 175 // exception stream 176 if (!exception_thread_ && !exception_type_) 177 --writer_count; 178 179 // Add space for all writers 180 if (!dir.AllocateArray(writer_count)) 181 return false; 182 183 MDRawHeader *header_ptr = header.get(); 184 header_ptr->signature = MD_HEADER_SIGNATURE; 185 header_ptr->version = MD_HEADER_VERSION; 186 time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp))); 187 header_ptr->stream_count = writer_count; 188 header_ptr->stream_directory_rva = dir.position(); 189 190 MDRawDirectory local_dir; 191 for (int i = 0; (result) && (i < writer_count); ++i) { 192 result = (this->*writers[i])(&local_dir); 193 194 if (result) 195 dir.CopyIndex(i, &local_dir); 196 } 197 } 198 199 return result; 200} 201 202size_t MinidumpGenerator::CalculateStackSize(vm_address_t start_addr) { 203 vm_address_t stack_region_base = start_addr; 204 vm_size_t stack_region_size; 205 natural_t nesting_level = 0; 206 vm_region_submap_info submap_info; 207 mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT; 208 kern_return_t result = 209 vm_region_recurse(crashing_task_, &stack_region_base, &stack_region_size, 210 &nesting_level, 211 reinterpret_cast<vm_region_recurse_info_t>(&submap_info), 212 &info_count); 213 214 if ((stack_region_base + stack_region_size) == 0xbffff000) { 215 // The stack for thread 0 needs to extend all the way to 0xc0000000 216 // For many processes the stack is first created in one page 217 // from 0xbffff000 - 0xc0000000 and is then later extended to 218 // a much larger size by creating a new VM region immediately below 219 // the initial page 220 221 // include the original stack frame page (0xbffff000 - 0xc0000000) 222 stack_region_size += 0x1000; 223 } 224 225 return result == KERN_SUCCESS ? 226 stack_region_base + stack_region_size - start_addr : 0; 227} 228 229bool MinidumpGenerator::WriteStackFromStartAddress( 230 vm_address_t start_addr, 231 MDMemoryDescriptor *stack_location) { 232 UntypedMDRVA memory(&writer_); 233 size_t size = CalculateStackSize(start_addr); 234 235 // If there's an error in the calculation, return at least the current 236 // stack information 237 if (size == 0) 238 size = 16; 239 240 if (!memory.Allocate(size)) 241 return false; 242 243 void *stack_memory = ReadTaskMemory(crashing_task_, (void*)start_addr, size); 244 245 bool result = memory.Copy(stack_memory, size); 246 247 free(stack_memory); 248 249 250 stack_location->start_of_memory_range = start_addr; 251 stack_location->memory = memory.location(); 252 253 return result; 254} 255 256#if TARGET_CPU_PPC 257bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, 258 MDMemoryDescriptor *stack_location) { 259 ppc_thread_state_t *machine_state = 260 reinterpret_cast<ppc_thread_state_t *>(state); 261 vm_address_t start_addr = machine_state->r1; 262 return WriteStackFromStartAddress(start_addr, stack_location); 263} 264 265u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) { 266 ppc_thread_state_t *machine_state = 267 reinterpret_cast<ppc_thread_state_t *>(state); 268 269 return machine_state->srr0; 270} 271 272bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, 273 MDLocationDescriptor *register_location) { 274 TypedMDRVA<MDRawContextPPC> context(&writer_); 275 ppc_thread_state_t *machine_state = 276 reinterpret_cast<ppc_thread_state_t *>(state); 277 278 if (!context.Allocate()) 279 return false; 280 281 *register_location = context.location(); 282 MDRawContextPPC *context_ptr = context.get(); 283 context_ptr->context_flags = MD_CONTEXT_PPC_BASE; 284 285#define AddReg(a) context_ptr->a = machine_state->a 286#define AddGPR(a) context_ptr->gpr[a] = machine_state->r ## a 287 AddReg(srr0); 288 AddReg(cr); 289 AddReg(xer); 290 AddReg(ctr); 291 AddReg(mq); 292 AddReg(lr); 293 AddReg(vrsave); 294 295 AddGPR(0); 296 AddGPR(1); 297 AddGPR(2); 298 AddGPR(3); 299 AddGPR(4); 300 AddGPR(5); 301 AddGPR(6); 302 AddGPR(7); 303 AddGPR(8); 304 AddGPR(9); 305 AddGPR(10); 306 AddGPR(11); 307 AddGPR(12); 308 AddGPR(13); 309 AddGPR(14); 310 AddGPR(15); 311 AddGPR(16); 312 AddGPR(17); 313 AddGPR(18); 314 AddGPR(19); 315 AddGPR(20); 316 AddGPR(21); 317 AddGPR(22); 318 AddGPR(23); 319 AddGPR(24); 320 AddGPR(25); 321 AddGPR(26); 322 AddGPR(27); 323 AddGPR(28); 324 AddGPR(29); 325 AddGPR(30); 326 AddGPR(31); 327 return true; 328} 329 330#elif TARGET_CPU_X86 331bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, 332 MDMemoryDescriptor *stack_location) { 333 i386_thread_state_t *machine_state = 334 reinterpret_cast<i386_thread_state_t *>(state); 335 vm_address_t start_addr = machine_state->esp; 336 return WriteStackFromStartAddress(start_addr, stack_location); 337} 338 339u_int64_t MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) { 340 i386_thread_state_t *machine_state = 341 reinterpret_cast<i386_thread_state_t *>(state); 342 343 return machine_state->eip; 344} 345 346bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, 347 MDLocationDescriptor *register_location) { 348 TypedMDRVA<MDRawContextX86> context(&writer_); 349 i386_thread_state_t *machine_state = 350 reinterpret_cast<i386_thread_state_t *>(state); 351 352 if (!context.Allocate()) 353 return false; 354 355 *register_location = context.location(); 356 MDRawContextX86 *context_ptr = context.get(); 357 context_ptr->context_flags = MD_CONTEXT_X86; 358#define AddReg(a) context_ptr->a = machine_state->a 359 AddReg(cs); 360 AddReg(ds); 361 AddReg(ss); 362 AddReg(es); 363 AddReg(fs); 364 AddReg(gs); 365 AddReg(eflags); 366 367 AddReg(eip); 368 AddReg(eax); 369 AddReg(ebx); 370 AddReg(ecx); 371 AddReg(edx); 372 AddReg(esi); 373 AddReg(edi); 374 AddReg(ebp); 375 AddReg(esp); 376 return true; 377} 378#endif 379 380bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, 381 MDRawThread *thread) { 382 breakpad_thread_state_data_t state; 383 mach_msg_type_number_t state_count = sizeof(state); 384 385 if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE, 386 state, &state_count) == 387 KERN_SUCCESS) { 388 if (!WriteStack(state, &thread->stack)) 389 return false; 390 391 if (!WriteContext(state, &thread->thread_context)) 392 return false; 393 394 thread->thread_id = thread_id; 395 } else { 396 return false; 397 } 398 399 return true; 400} 401 402bool MinidumpGenerator::WriteThreadListStream( 403 MDRawDirectory *thread_list_stream) { 404 TypedMDRVA<MDRawThreadList> list(&writer_); 405 thread_act_port_array_t threads_for_task; 406 mach_msg_type_number_t thread_count; 407 int non_generator_thread_count; 408 409 if (task_threads(crashing_task_, &threads_for_task, &thread_count)) 410 return false; 411 412 // Don't include the generator thread 413 non_generator_thread_count = thread_count - 1; 414 if (!list.AllocateObjectAndArray(non_generator_thread_count, 415 sizeof(MDRawThread))) 416 return false; 417 418 thread_list_stream->stream_type = MD_THREAD_LIST_STREAM; 419 thread_list_stream->location = list.location(); 420 421 list.get()->number_of_threads = non_generator_thread_count; 422 423 MDRawThread thread; 424 int thread_idx = 0; 425 426 for (unsigned int i = 0; i < thread_count; ++i) { 427 memset(&thread, 0, sizeof(MDRawThread)); 428 429 if (threads_for_task[i] != handler_thread_) { 430 if (!WriteThreadStream(threads_for_task[i], &thread)) 431 return false; 432 433 list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread)); 434 } 435 } 436 437 return true; 438} 439 440bool MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) { 441 TypedMDRVA<MDRawExceptionStream> exception(&writer_); 442 443 if (!exception.Allocate()) 444 return false; 445 446 exception_stream->stream_type = MD_EXCEPTION_STREAM; 447 exception_stream->location = exception.location(); 448 MDRawExceptionStream *exception_ptr = exception.get(); 449 exception_ptr->thread_id = exception_thread_; 450 451 // This naming is confusing, but it is the proper translation from 452 // mach naming to minidump naming. 453 exception_ptr->exception_record.exception_code = exception_type_; 454 exception_ptr->exception_record.exception_flags = exception_code_; 455 456 breakpad_thread_state_data_t state; 457 mach_msg_type_number_t stateCount = sizeof(state); 458 459 if (thread_get_state(exception_thread_, BREAKPAD_MACHINE_THREAD_STATE, state, 460 &stateCount) != KERN_SUCCESS) 461 return false; 462 463 if (!WriteContext(state, &exception_ptr->thread_context)) 464 return false; 465 466 exception_ptr->exception_record.exception_address = CurrentPCForStack(state); 467 468 return true; 469} 470 471bool MinidumpGenerator::WriteSystemInfoStream( 472 MDRawDirectory *system_info_stream) { 473 TypedMDRVA<MDRawSystemInfo> info(&writer_); 474 475 if (!info.Allocate()) 476 return false; 477 478 system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM; 479 system_info_stream->location = info.location(); 480 481 // CPU Information 482 uint32_t cpu_type; 483 size_t len = sizeof(cpu_type); 484 sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0); 485 uint32_t number_of_processors; 486 len = sizeof(number_of_processors); 487 sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0); 488 MDRawSystemInfo *info_ptr = info.get(); 489 490 switch (cpu_type) { 491 case CPU_TYPE_POWERPC: 492 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; 493 break; 494 case CPU_TYPE_I386: 495 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86; 496#ifdef __i386__ 497 // ebx is used for PIC code, so we need 498 // to preserve it. 499#define cpuid(op,eax,ebx,ecx,edx) \ 500 asm ("pushl %%ebx \n\t" \ 501 "cpuid \n\t" \ 502 "movl %%ebx,%1 \n\t" \ 503 "popl %%ebx" \ 504 : "=a" (eax), \ 505 "=g" (ebx), \ 506 "=c" (ecx), \ 507 "=d" (edx) \ 508 : "0" (op)) 509 int unused, unused2; 510 // get vendor id 511 cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0], 512 info_ptr->cpu.x86_cpu_info.vendor_id[2], 513 info_ptr->cpu.x86_cpu_info.vendor_id[1]); 514 // get version and feature info 515 cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, 516 info_ptr->cpu.x86_cpu_info.feature_information); 517 // family 518 info_ptr->processor_level = 519 (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; 520 // 0xMMSS (Model, Stepping) 521 info_ptr->processor_revision = 522 (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | 523 ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); 524#endif // __i386__ 525 break; 526 default: 527 info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; 528 break; 529 } 530 531 info_ptr->number_of_processors = number_of_processors; 532 info_ptr->platform_id = MD_OS_MAC_OS_X; 533 534 MDLocationDescriptor build_string_loc; 535 536 if (!writer_.WriteString(build_string_, 0, 537 &build_string_loc)) 538 return false; 539 540 info_ptr->csd_version_rva = build_string_loc.rva; 541 info_ptr->major_version = os_major_version_; 542 info_ptr->minor_version = os_minor_version_; 543 info_ptr->build_number = os_build_number_; 544 545 return true; 546} 547 548bool MinidumpGenerator::WriteModuleStream(unsigned int index, 549 MDRawModule *module) { 550 DynamicImage *image = dynamic_images_->GetImage(index); 551 552 if (!image) 553 return false; 554 555 const mach_header *header = image->GetMachHeader(); 556 557 if (!header) 558 return false; 559 560 int cpu_type = header->cputype; 561 562 memset(module, 0, sizeof(MDRawModule)); 563 564 MDLocationDescriptor string_location; 565 566 const char* name = image->GetFilePath(); 567 if (!writer_.WriteString(name, 0, &string_location)) 568 return false; 569 570 module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide(); 571 module->size_of_image = image->GetVMSize(); 572 module->module_name_rva = string_location.rva; 573 574 if (!WriteCVRecord(module, cpu_type, name)) { 575 return false; 576 } 577 578 return true; 579} 580 581int MinidumpGenerator::FindExecutableModule() { 582 int index = dynamic_images_->GetExecutableImageIndex(); 583 584 if (index >= 0) { 585 return index; 586 } 587 588 // failed - just use the first image 589 return 0; 590} 591 592bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, 593 const char *module_path) { 594 TypedMDRVA<MDCVInfoPDB70> cv(&writer_); 595 596 // Only return the last path component of the full module path 597 const char *module_name = strrchr(module_path, '/'); 598 599 // Increment past the slash 600 if (module_name) 601 ++module_name; 602 else 603 module_name = "<Unknown>"; 604 605 size_t module_name_length = strlen(module_name); 606 607 if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) 608 return false; 609 610 if (!cv.CopyIndexAfterObject(0, module_name, module_name_length)) 611 return false; 612 613 module->cv_record = cv.location(); 614 MDCVInfoPDB70 *cv_ptr = cv.get(); 615 cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; 616 cv_ptr->age = 0; 617 618 // Get the module identifier 619 FileID file_id(module_path); 620 unsigned char identifier[16]; 621 622 if (file_id.MachoIdentifier(cpu_type, identifier)) { 623 cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | 624 (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | 625 (uint32_t)identifier[3]; 626 cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; 627 cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; 628 cv_ptr->signature.data4[0] = identifier[8]; 629 cv_ptr->signature.data4[1] = identifier[9]; 630 cv_ptr->signature.data4[2] = identifier[10]; 631 cv_ptr->signature.data4[3] = identifier[11]; 632 cv_ptr->signature.data4[4] = identifier[12]; 633 cv_ptr->signature.data4[5] = identifier[13]; 634 cv_ptr->signature.data4[6] = identifier[14]; 635 cv_ptr->signature.data4[7] = identifier[15]; 636 } 637 638 return true; 639} 640 641bool MinidumpGenerator::WriteModuleListStream( 642 MDRawDirectory *module_list_stream) { 643 TypedMDRVA<MDRawModuleList> list(&writer_); 644 645 if (!_dyld_present()) 646 return false; 647 648 int image_count = dynamic_images_->GetImageCount(); 649 650 if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) 651 return false; 652 653 module_list_stream->stream_type = MD_MODULE_LIST_STREAM; 654 module_list_stream->location = list.location(); 655 list.get()->number_of_modules = image_count; 656 657 // Write out the executable module as the first one 658 MDRawModule module; 659 int executableIndex = FindExecutableModule(); 660 661 if (!WriteModuleStream(executableIndex, &module)) { 662 return false; 663 } 664 665 list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); 666 int destinationIndex = 1; // Write all other modules after this one 667 668 for (int i = 0; i < image_count; ++i) { 669 if (i != executableIndex) { 670 if (!WriteModuleStream(i, &module)) { 671 return false; 672 } 673 674 list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE); 675 } 676 } 677 678 return true; 679} 680 681bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) { 682 TypedMDRVA<MDRawMiscInfo> info(&writer_); 683 684 if (!info.Allocate()) 685 return false; 686 687 misc_info_stream->stream_type = MD_MISC_INFO_STREAM; 688 misc_info_stream->location = info.location(); 689 690 MDRawMiscInfo *info_ptr = info.get(); 691 info_ptr->size_of_info = sizeof(MDRawMiscInfo); 692 info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID | 693 MD_MISCINFO_FLAGS1_PROCESS_TIMES | 694 MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO; 695 696 // Process ID 697 info_ptr->process_id = getpid(); 698 699 // Times 700 struct rusage usage; 701 if (getrusage(RUSAGE_SELF, &usage) != -1) { 702 // Omit the fractional time since the MDRawMiscInfo only wants seconds 703 info_ptr->process_user_time = usage.ru_utime.tv_sec; 704 info_ptr->process_kernel_time = usage.ru_stime.tv_sec; 705 } 706 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, info_ptr->process_id }; 707 size_t size; 708 if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &size, NULL, 0)) { 709 vm_address_t addr; 710 if (vm_allocate(mach_task_self(), &addr, size, true) == KERN_SUCCESS) { 711 struct kinfo_proc *proc = (struct kinfo_proc *)addr; 712 if (!sysctl(mib, sizeof(mib) / sizeof(mib[0]), proc, &size, NULL, 0)) 713 info_ptr->process_create_time = proc->kp_proc.p_starttime.tv_sec; 714 vm_deallocate(mach_task_self(), addr, size); 715 } 716 } 717 718 // Speed 719 uint64_t speed; 720 size = sizeof(speed); 721 sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0); 722 info_ptr->processor_max_mhz = speed / (1000 * 1000); 723 info_ptr->processor_mhz_limit = speed / (1000 * 1000); 724 size = sizeof(speed); 725 sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0); 726 info_ptr->processor_current_mhz = speed / (1000 * 1000); 727 728 return true; 729} 730 731bool MinidumpGenerator::WriteBreakpadInfoStream( 732 MDRawDirectory *breakpad_info_stream) { 733 TypedMDRVA<MDRawBreakpadInfo> info(&writer_); 734 735 if (!info.Allocate()) 736 return false; 737 738 breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM; 739 breakpad_info_stream->location = info.location(); 740 MDRawBreakpadInfo *info_ptr = info.get(); 741 742 if (exception_thread_ && exception_type_) { 743 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | 744 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; 745 info_ptr->dump_thread_id = handler_thread_; 746 info_ptr->requesting_thread_id = exception_thread_; 747 } else { 748 info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID; 749 info_ptr->dump_thread_id = handler_thread_; 750 info_ptr->requesting_thread_id = 0; 751 } 752 753 return true; 754} 755 756} // namespace google_breakpad 757