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