summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc')
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc1432
1 files changed, 1432 insertions, 0 deletions
diff --git a/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc
new file mode 100644
index 0000000..b1d429c
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/handler/minidump_generator.cc
@@ -0,0 +1,1432 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+#include <cstdio>
+
+#include <mach/host_info.h>
+#include <mach/vm_statistics.h>
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "client/mac/handler/minidump_generator.h"
+
+#ifdef HAS_ARM_SUPPORT
+#include <mach/arm/thread_status.h>
+#endif
+#ifdef HAS_PPC_SUPPORT
+#include <mach/ppc/thread_status.h>
+#endif
+#ifdef HAS_X86_SUPPORT
+#include <mach/i386/thread_status.h>
+#endif
+
+#include "client/minidump_file_writer-inl.h"
+#include "common/mac/file_id.h"
+#include "common/mac/macho_id.h"
+#include "common/mac/string_utilities.h"
+
+using MacStringUtils::ConvertToString;
+using MacStringUtils::IntegerValueAtIndex;
+
+namespace google_breakpad {
+
+#if __LP64__
+#define LC_SEGMENT_ARCH LC_SEGMENT_64
+#else
+#define LC_SEGMENT_ARCH LC_SEGMENT
+#endif
+
+// constructor when generating from within the crashed process
+MinidumpGenerator::MinidumpGenerator()
+ : writer_(),
+ exception_type_(0),
+ exception_code_(0),
+ exception_subcode_(0),
+ exception_thread_(0),
+ crashing_task_(mach_task_self()),
+ handler_thread_(mach_thread_self()),
+ cpu_type_(DynamicImages::GetNativeCPUType()),
+ dynamic_images_(NULL),
+ memory_blocks_(&allocator_) {
+ GatherSystemInformation();
+}
+
+// constructor when generating from a different process than the
+// crashed process
+MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
+ mach_port_t handler_thread)
+ : writer_(),
+ exception_type_(0),
+ exception_code_(0),
+ exception_subcode_(0),
+ exception_thread_(0),
+ crashing_task_(crashing_task),
+ handler_thread_(handler_thread),
+ cpu_type_(DynamicImages::GetNativeCPUType()),
+ dynamic_images_(NULL),
+ memory_blocks_(&allocator_) {
+ if (crashing_task != mach_task_self()) {
+ dynamic_images_ = new DynamicImages(crashing_task_);
+ cpu_type_ = dynamic_images_->GetCPUType();
+ } else {
+ dynamic_images_ = NULL;
+ cpu_type_ = DynamicImages::GetNativeCPUType();
+ }
+
+ GatherSystemInformation();
+}
+
+MinidumpGenerator::~MinidumpGenerator() {
+ delete dynamic_images_;
+}
+
+char MinidumpGenerator::build_string_[16];
+int MinidumpGenerator::os_major_version_ = 0;
+int MinidumpGenerator::os_minor_version_ = 0;
+int MinidumpGenerator::os_build_number_ = 0;
+
+// static
+void MinidumpGenerator::GatherSystemInformation() {
+ // If this is non-zero, then we've already gathered the information
+ if (os_major_version_)
+ return;
+
+ // This code extracts the version and build information from the OS
+ CFStringRef vers_path =
+ CFSTR("/System/Library/CoreServices/SystemVersion.plist");
+ CFURLRef sys_vers =
+ CFURLCreateWithFileSystemPath(NULL,
+ vers_path,
+ kCFURLPOSIXPathStyle,
+ false);
+ CFDataRef data;
+ SInt32 error;
+ CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
+ &error);
+
+ if (!data) {
+ CFRelease(sys_vers);
+ return;
+ }
+
+ CFDictionaryRef list = static_cast<CFDictionaryRef>
+ (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
+ NULL));
+ if (!list) {
+ CFRelease(sys_vers);
+ CFRelease(data);
+ return;
+ }
+
+ CFStringRef build_version = static_cast<CFStringRef>
+ (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
+ CFStringRef product_version = static_cast<CFStringRef>
+ (CFDictionaryGetValue(list, CFSTR("ProductVersion")));
+ string build_str = ConvertToString(build_version);
+ string product_str = ConvertToString(product_version);
+
+ CFRelease(list);
+ CFRelease(sys_vers);
+ CFRelease(data);
+
+ strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
+
+ // Parse the string that looks like "10.4.8"
+ os_major_version_ = IntegerValueAtIndex(product_str, 0);
+ os_minor_version_ = IntegerValueAtIndex(product_str, 1);
+ os_build_number_ = IntegerValueAtIndex(product_str, 2);
+}
+
+string MinidumpGenerator::UniqueNameInDirectory(const string &dir,
+ string *unique_name) {
+ CFUUIDRef uuid = CFUUIDCreate(NULL);
+ CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid);
+ CFRelease(uuid);
+ string file_name(ConvertToString(uuid_cfstr));
+ CFRelease(uuid_cfstr);
+ string path(dir);
+
+ // Ensure that the directory (if non-empty) has a trailing slash so that
+ // we can append the file name and have a valid pathname.
+ if (!dir.empty()) {
+ if (dir.at(dir.size() - 1) != '/')
+ path.append(1, '/');
+ }
+
+ path.append(file_name);
+ path.append(".dmp");
+
+ if (unique_name)
+ *unique_name = file_name;
+
+ return path;
+}
+
+bool MinidumpGenerator::Write(const char *path) {
+ WriteStreamFN writers[] = {
+ &MinidumpGenerator::WriteThreadListStream,
+ &MinidumpGenerator::WriteMemoryListStream,
+ &MinidumpGenerator::WriteSystemInfoStream,
+ &MinidumpGenerator::WriteModuleListStream,
+ &MinidumpGenerator::WriteMiscInfoStream,
+ &MinidumpGenerator::WriteBreakpadInfoStream,
+ // Exception stream needs to be the last entry in this array as it may
+ // be omitted in the case where the minidump is written without an
+ // exception.
+ &MinidumpGenerator::WriteExceptionStream,
+ };
+ bool result = false;
+
+ // If opening was successful, create the header, directory, and call each
+ // writer. The destructor for the TypedMDRVAs will cause the data to be
+ // flushed. The destructor for the MinidumpFileWriter will close the file.
+ if (writer_.Open(path)) {
+ TypedMDRVA<MDRawHeader> header(&writer_);
+ TypedMDRVA<MDRawDirectory> dir(&writer_);
+
+ if (!header.Allocate())
+ return false;
+
+ int writer_count = static_cast<int>(sizeof(writers) / sizeof(writers[0]));
+
+ // If we don't have exception information, don't write out the
+ // exception stream
+ if (!exception_thread_ && !exception_type_)
+ --writer_count;
+
+ // Add space for all writers
+ if (!dir.AllocateArray(writer_count))
+ return false;
+
+ MDRawHeader *header_ptr = header.get();
+ header_ptr->signature = MD_HEADER_SIGNATURE;
+ header_ptr->version = MD_HEADER_VERSION;
+ time(reinterpret_cast<time_t *>(&(header_ptr->time_date_stamp)));
+ header_ptr->stream_count = writer_count;
+ header_ptr->stream_directory_rva = dir.position();
+
+ MDRawDirectory local_dir;
+ result = true;
+ for (int i = 0; (result) && (i < writer_count); ++i) {
+ result = (this->*writers[i])(&local_dir);
+
+ if (result)
+ dir.CopyIndex(i, &local_dir);
+ }
+ }
+ return result;
+}
+
+size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
+ mach_vm_address_t stack_region_base = start_addr;
+ mach_vm_size_t stack_region_size;
+ natural_t nesting_level = 0;
+ vm_region_submap_info_64 submap_info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ vm_region_recurse_info_t region_info;
+ region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);
+
+ if (start_addr == 0) {
+ return 0;
+ }
+
+ kern_return_t result =
+ mach_vm_region_recurse(crashing_task_, &stack_region_base,
+ &stack_region_size, &nesting_level,
+ region_info, &info_count);
+
+ if (result != KERN_SUCCESS || start_addr < stack_region_base) {
+ // Failure or stack corruption, since mach_vm_region had to go
+ // higher in the process address space to find a valid region.
+ return 0;
+ }
+
+ unsigned int tag = submap_info.user_tag;
+
+ // If the user tag is VM_MEMORY_STACK, look for more readable regions with
+ // the same tag placed immediately above the computed stack region. Under
+ // some circumstances, the stack for thread 0 winds up broken up into
+ // multiple distinct abutting regions. This can happen for several reasons,
+ // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes
+ // the access on stack pages by calling mprotect.
+ if (tag == VM_MEMORY_STACK) {
+ while (true) {
+ mach_vm_address_t next_region_base = stack_region_base +
+ stack_region_size;
+ mach_vm_address_t proposed_next_region_base = next_region_base;
+ mach_vm_size_t next_region_size;
+ nesting_level = 0;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+ result = mach_vm_region_recurse(crashing_task_, &next_region_base,
+ &next_region_size, &nesting_level,
+ region_info, &info_count);
+ if (result != KERN_SUCCESS ||
+ next_region_base != proposed_next_region_base ||
+ submap_info.user_tag != tag ||
+ (submap_info.protection & VM_PROT_READ) == 0) {
+ break;
+ }
+
+ stack_region_size += next_region_size;
+ }
+ }
+
+ return stack_region_base + stack_region_size - start_addr;
+}
+
+bool MinidumpGenerator::WriteStackFromStartAddress(
+ mach_vm_address_t start_addr,
+ MDMemoryDescriptor *stack_location) {
+ UntypedMDRVA memory(&writer_);
+
+ bool result = false;
+ size_t size = CalculateStackSize(start_addr);
+
+ if (size == 0) {
+ // In some situations the stack address for the thread can come back 0.
+ // In these cases we skip over the threads in question and stuff the
+ // stack with a clearly borked value.
+ start_addr = 0xDEADBEEF;
+ size = 16;
+ if (!memory.Allocate(size))
+ return false;
+
+ unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of
+ // junk.
+ dummy_stack[0] = 0xDEADBEEF;
+ dummy_stack[1] = 0xDEADBEEF;
+
+ result = memory.Copy(dummy_stack, size);
+ } else {
+
+ if (!memory.Allocate(size))
+ return false;
+
+ if (dynamic_images_) {
+ vector<uint8_t> stack_memory;
+ if (ReadTaskMemory(crashing_task_,
+ start_addr,
+ size,
+ stack_memory) != KERN_SUCCESS) {
+ return false;
+ }
+
+ result = memory.Copy(&stack_memory[0], size);
+ } else {
+ result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
+ }
+ }
+
+ stack_location->start_of_memory_range = start_addr;
+ stack_location->memory = memory.location();
+
+ return result;
+}
+
+bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return WriteStackARM(state, stack_location);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return WriteStackPPC(state, stack_location);
+ case CPU_TYPE_POWERPC64:
+ return WriteStackPPC64(state, stack_location);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return WriteStackX86(state, stack_location);
+ case CPU_TYPE_X86_64:
+ return WriteStackX86_64(state, stack_location);
+#endif
+ default:
+ return false;
+ }
+}
+
+bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return WriteContextARM(state, register_location);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return WriteContextPPC(state, register_location);
+ case CPU_TYPE_POWERPC64:
+ return WriteContextPPC64(state, register_location);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return WriteContextX86(state, register_location);
+ case CPU_TYPE_X86_64:
+ return WriteContextX86_64(state, register_location);
+#endif
+ default:
+ return false;
+ }
+}
+
+u_int64_t MinidumpGenerator::CurrentPCForStack(
+ breakpad_thread_state_data_t state) {
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ return CurrentPCForStackARM(state);
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ return CurrentPCForStackPPC(state);
+ case CPU_TYPE_POWERPC64:
+ return CurrentPCForStackPPC64(state);
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ return CurrentPCForStackX86(state);
+ case CPU_TYPE_X86_64:
+ return CurrentPCForStackX86_64(state);
+#endif
+ default:
+ assert("Unknown CPU type!");
+ return 0;
+ }
+}
+
+#ifdef HAS_ARM_SUPPORT
+bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+u_int64_t
+MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, pc);
+}
+
+bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextARM> context(&writer_);
+ arm_thread_state_t *machine_state =
+ reinterpret_cast<arm_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextARM *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_ARM_FULL;
+
+#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a])
+
+ context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp);
+ context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr);
+ context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc);
+ context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+#undef AddReg
+#undef AddGPR
+
+ return true;
+}
+#endif
+
+#ifdef HAS_PCC_SUPPORT
+bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+u_int64_t
+MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, srr0);
+}
+
+u_int64_t
+MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, srr0);
+}
+
+bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextPPC> context(&writer_);
+ ppc_thread_state_t *machine_state =
+ reinterpret_cast<ppc_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextPPC *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
+
+#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+
+ AddReg(srr0);
+ AddReg(cr);
+ AddReg(xer);
+ AddReg(ctr);
+ AddReg(lr);
+ AddReg(vrsave);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+ AddGPR(13);
+ AddGPR(14);
+ AddGPR(15);
+ AddGPR(16);
+ AddGPR(17);
+ AddGPR(18);
+ AddGPR(19);
+ AddGPR(20);
+ AddGPR(21);
+ AddGPR(22);
+ AddGPR(23);
+ AddGPR(24);
+ AddGPR(25);
+ AddGPR(26);
+ AddGPR(27);
+ AddGPR(28);
+ AddGPR(29);
+ AddGPR(30);
+ AddGPR(31);
+ AddReg(mq);
+#undef AddReg
+#undef AddGPR
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteContextPPC64(
+ breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextPPC64> context(&writer_);
+ ppc_thread_state64_t *machine_state =
+ reinterpret_cast<ppc_thread_state64_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextPPC64 *context_ptr = context.get();
+ context_ptr->context_flags = MD_CONTEXT_PPC_BASE;
+
+#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a)
+
+ AddReg(srr0);
+ AddReg(cr);
+ AddReg(xer);
+ AddReg(ctr);
+ AddReg(lr);
+ AddReg(vrsave);
+
+ AddGPR(0);
+ AddGPR(1);
+ AddGPR(2);
+ AddGPR(3);
+ AddGPR(4);
+ AddGPR(5);
+ AddGPR(6);
+ AddGPR(7);
+ AddGPR(8);
+ AddGPR(9);
+ AddGPR(10);
+ AddGPR(11);
+ AddGPR(12);
+ AddGPR(13);
+ AddGPR(14);
+ AddGPR(15);
+ AddGPR(16);
+ AddGPR(17);
+ AddGPR(18);
+ AddGPR(19);
+ AddGPR(20);
+ AddGPR(21);
+ AddGPR(22);
+ AddGPR(23);
+ AddGPR(24);
+ AddGPR(25);
+ AddGPR(26);
+ AddGPR(27);
+ AddGPR(28);
+ AddGPR(29);
+ AddGPR(30);
+ AddGPR(31);
+#undef AddReg
+#undef AddGPR
+
+ return true;
+}
+
+#endif
+
+#ifdef HAS_X86_SUPPORT
+bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state,
+ MDMemoryDescriptor *stack_location) {
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
+ return WriteStackFromStartAddress(start_addr, stack_location);
+}
+
+u_int64_t
+MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, eip);
+}
+
+u_int64_t
+MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ return REGISTER_FROM_THREADSTATE(machine_state, rip);
+}
+
+bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location)
+{
+ TypedMDRVA<MDRawContextX86> context(&writer_);
+ i386_thread_state_t *machine_state =
+ reinterpret_cast<i386_thread_state_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextX86 *context_ptr = context.get();
+
+#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+
+ context_ptr->context_flags = MD_CONTEXT_X86;
+ AddReg(eax);
+ AddReg(ebx);
+ AddReg(ecx);
+ AddReg(edx);
+ AddReg(esi);
+ AddReg(edi);
+ AddReg(ebp);
+ AddReg(esp);
+
+ AddReg(cs);
+ AddReg(ds);
+ AddReg(ss);
+ AddReg(es);
+ AddReg(fs);
+ AddReg(gs);
+ AddReg(eflags);
+
+ AddReg(eip);
+#undef AddReg
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteContextX86_64(
+ breakpad_thread_state_data_t state,
+ MDLocationDescriptor *register_location) {
+ TypedMDRVA<MDRawContextAMD64> context(&writer_);
+ x86_thread_state64_t *machine_state =
+ reinterpret_cast<x86_thread_state64_t *>(state);
+
+ if (!context.Allocate())
+ return false;
+
+ *register_location = context.location();
+ MDRawContextAMD64 *context_ptr = context.get();
+
+#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a)
+
+ context_ptr->context_flags = MD_CONTEXT_AMD64;
+ AddReg(rax);
+ AddReg(rbx);
+ AddReg(rcx);
+ AddReg(rdx);
+ AddReg(rdi);
+ AddReg(rsi);
+ AddReg(rbp);
+ AddReg(rsp);
+ AddReg(r8);
+ AddReg(r9);
+ AddReg(r10);
+ AddReg(r11);
+ AddReg(r12);
+ AddReg(r13);
+ AddReg(r14);
+ AddReg(r15);
+ AddReg(rip);
+ // according to AMD's software developer guide, bits above 18 are
+ // not used in the flags register. Since the minidump format
+ // specifies 32 bits for the flags register, we can truncate safely
+ // with no loss.
+ context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
+ AddReg(cs);
+ AddReg(fs);
+ AddReg(gs);
+#undef AddReg
+
+ return true;
+}
+#endif
+
+bool MinidumpGenerator::GetThreadState(thread_act_t target_thread,
+ thread_state_t state,
+ mach_msg_type_number_t *count) {
+ thread_state_flavor_t flavor;
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ flavor = ARM_THREAD_STATE;
+ break;
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ flavor = PPC_THREAD_STATE;
+ break;
+ case CPU_TYPE_POWERPC64:
+ flavor = PPC_THREAD_STATE64;
+ break;
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ flavor = i386_THREAD_STATE;
+ break;
+ case CPU_TYPE_X86_64:
+ flavor = x86_THREAD_STATE64;
+ break;
+#endif
+ default:
+ return false;
+ }
+ return thread_get_state(target_thread, flavor,
+ state, count) == KERN_SUCCESS;
+}
+
+bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
+ MDRawThread *thread) {
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t state_count
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (GetThreadState(thread_id, state, &state_count)) {
+ if (!WriteStack(state, &thread->stack))
+ return false;
+
+ memory_blocks_.push_back(thread->stack);
+
+ if (!WriteContext(state, &thread->thread_context))
+ return false;
+
+ thread->thread_id = thread_id;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteThreadListStream(
+ MDRawDirectory *thread_list_stream) {
+ TypedMDRVA<MDRawThreadList> list(&writer_);
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+ int non_generator_thread_count;
+
+ if (task_threads(crashing_task_, &threads_for_task, &thread_count))
+ return false;
+
+ // Don't include the generator thread
+ if (handler_thread_ != MACH_PORT_NULL)
+ non_generator_thread_count = thread_count - 1;
+ else
+ non_generator_thread_count = thread_count;
+ if (!list.AllocateObjectAndArray(non_generator_thread_count,
+ sizeof(MDRawThread)))
+ return false;
+
+ thread_list_stream->stream_type = MD_THREAD_LIST_STREAM;
+ thread_list_stream->location = list.location();
+
+ list.get()->number_of_threads = non_generator_thread_count;
+
+ MDRawThread thread;
+ int thread_idx = 0;
+
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ memset(&thread, 0, sizeof(MDRawThread));
+
+ if (threads_for_task[i] != handler_thread_) {
+ if (!WriteThreadStream(threads_for_task[i], &thread))
+ return false;
+
+ list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread));
+ }
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteMemoryListStream(
+ MDRawDirectory *memory_list_stream) {
+ TypedMDRVA<MDRawMemoryList> list(&writer_);
+
+ // If the dump has an exception, include some memory around the
+ // instruction pointer.
+ const size_t kIPMemorySize = 256; // bytes
+ bool have_ip_memory = false;
+ MDMemoryDescriptor ip_memory_d;
+ if (exception_thread_ && exception_type_) {
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t stateCount
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (thread_get_state(exception_thread_,
+ BREAKPAD_MACHINE_THREAD_STATE,
+ state,
+ &stateCount) == KERN_SUCCESS) {
+ u_int64_t ip = CurrentPCForStack(state);
+ // Bound it to the upper and lower bounds of the region
+ // it's contained within. If it's not in a known memory region,
+ // don't bother trying to write it.
+ mach_vm_address_t addr = ip;
+ mach_vm_size_t size;
+ natural_t nesting_level = 0;
+ vm_region_submap_info_64 info;
+ mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ kern_return_t ret =
+ mach_vm_region_recurse(crashing_task_,
+ &addr,
+ &size,
+ &nesting_level,
+ (vm_region_recurse_info_t)&info,
+ &info_count);
+ if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) {
+ // Try to get 128 bytes before and after the IP, but
+ // settle for whatever's available.
+ ip_memory_d.start_of_memory_range =
+ std::max(uintptr_t(addr),
+ uintptr_t(ip - (kIPMemorySize / 2)));
+ uintptr_t end_of_range =
+ std::min(uintptr_t(ip + (kIPMemorySize / 2)),
+ uintptr_t(addr + size));
+ ip_memory_d.memory.data_size =
+ end_of_range - ip_memory_d.start_of_memory_range;
+ have_ip_memory = true;
+ // This needs to get appended to the list even though
+ // the memory bytes aren't filled in yet so the entire
+ // list can be written first. The memory bytes will get filled
+ // in after the memory list is written.
+ memory_blocks_.push_back(ip_memory_d);
+ }
+ }
+ }
+
+ // Now fill in the memory list and write it.
+ unsigned memory_count = memory_blocks_.size();
+ if (!list.AllocateObjectAndArray(memory_count,
+ sizeof(MDMemoryDescriptor)))
+ return false;
+
+ memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM;
+ memory_list_stream->location = list.location();
+
+ list.get()->number_of_memory_ranges = memory_count;
+
+ unsigned int i;
+ for (i = 0; i < memory_count; ++i) {
+ list.CopyIndexAfterObject(i, &memory_blocks_[i],
+ sizeof(MDMemoryDescriptor));
+ }
+
+ if (have_ip_memory) {
+ // Now read the memory around the instruction pointer.
+ UntypedMDRVA ip_memory(&writer_);
+ if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
+ return false;
+
+ if (dynamic_images_) {
+ // Out-of-process.
+ vector<uint8_t> memory;
+ if (ReadTaskMemory(crashing_task_,
+ ip_memory_d.start_of_memory_range,
+ ip_memory_d.memory.data_size,
+ memory) != KERN_SUCCESS) {
+ return false;
+ }
+
+ ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
+ } else {
+ // In-process, just copy from local memory.
+ ip_memory.Copy(
+ reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
+ ip_memory_d.memory.data_size);
+ }
+
+ ip_memory_d.memory = ip_memory.location();
+ // Write this again now that the data location is filled in.
+ list.CopyIndexAfterObject(i - 1, &ip_memory_d,
+ sizeof(MDMemoryDescriptor));
+ }
+
+ return true;
+}
+
+bool
+MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) {
+ TypedMDRVA<MDRawExceptionStream> exception(&writer_);
+
+ if (!exception.Allocate())
+ return false;
+
+ exception_stream->stream_type = MD_EXCEPTION_STREAM;
+ exception_stream->location = exception.location();
+ MDRawExceptionStream *exception_ptr = exception.get();
+ exception_ptr->thread_id = exception_thread_;
+
+ // This naming is confusing, but it is the proper translation from
+ // mach naming to minidump naming.
+ exception_ptr->exception_record.exception_code = exception_type_;
+ exception_ptr->exception_record.exception_flags = exception_code_;
+
+ breakpad_thread_state_data_t state;
+ mach_msg_type_number_t state_count
+ = static_cast<mach_msg_type_number_t>(sizeof(state));
+
+ if (!GetThreadState(exception_thread_, state, &state_count))
+ return false;
+
+ if (!WriteContext(state, &exception_ptr->thread_context))
+ return false;
+
+ if (exception_type_ == EXC_BAD_ACCESS)
+ exception_ptr->exception_record.exception_address = exception_subcode_;
+ else
+ exception_ptr->exception_record.exception_address = CurrentPCForStack(state);
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteSystemInfoStream(
+ MDRawDirectory *system_info_stream) {
+ TypedMDRVA<MDRawSystemInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM;
+ system_info_stream->location = info.location();
+
+ // CPU Information
+ uint32_t number_of_processors;
+ size_t len = sizeof(number_of_processors);
+ sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0);
+ MDRawSystemInfo *info_ptr = info.get();
+
+ switch (cpu_type_) {
+#ifdef HAS_ARM_SUPPORT
+ case CPU_TYPE_ARM:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
+ break;
+#endif
+#ifdef HAS_PPC_SUPPORT
+ case CPU_TYPE_POWERPC:
+ case CPU_TYPE_POWERPC64:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
+ break;
+#endif
+#ifdef HAS_X86_SUPPORT
+ case CPU_TYPE_I386:
+ case CPU_TYPE_X86_64:
+ if (cpu_type_ == CPU_TYPE_I386)
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
+ else
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
+#ifdef __i386__
+ // ebx is used for PIC code, so we need
+ // to preserve it.
+#define cpuid(op,eax,ebx,ecx,edx) \
+ asm ("pushl %%ebx \n\t" \
+ "cpuid \n\t" \
+ "movl %%ebx,%1 \n\t" \
+ "popl %%ebx" \
+ : "=a" (eax), \
+ "=g" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#elif defined(__x86_64__)
+
+#define cpuid(op,eax,ebx,ecx,edx) \
+ asm ("cpuid \n\t" \
+ : "=a" (eax), \
+ "=b" (ebx), \
+ "=c" (ecx), \
+ "=d" (edx) \
+ : "0" (op))
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+ int unused, unused2;
+ // get vendor id
+ cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
+ info_ptr->cpu.x86_cpu_info.vendor_id[2],
+ info_ptr->cpu.x86_cpu_info.vendor_id[1]);
+ // get version and feature info
+ cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2,
+ info_ptr->cpu.x86_cpu_info.feature_information);
+
+ // family
+ info_ptr->processor_level =
+ (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8;
+ // 0xMMSS (Model, Stepping)
+ info_ptr->processor_revision =
+ (info_ptr->cpu.x86_cpu_info.version_information & 0xF) |
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4);
+
+ // decode extended model info
+ if (info_ptr->processor_level == 0xF ||
+ info_ptr->processor_level == 0x6) {
+ info_ptr->processor_revision |=
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4);
+ }
+
+ // decode extended family info
+ if (info_ptr->processor_level == 0xF) {
+ info_ptr->processor_level +=
+ ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
+ }
+
+#endif // __i386__ || __x86_64_
+ break;
+#endif // HAS_X86_SUPPORT
+ default:
+ info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
+ break;
+ }
+
+ info_ptr->number_of_processors = number_of_processors;
+#if TARGET_OS_IPHONE
+ info_ptr->platform_id = MD_OS_IOS;
+#else
+ info_ptr->platform_id = MD_OS_MAC_OS_X;
+#endif // TARGET_OS_IPHONE
+
+ MDLocationDescriptor build_string_loc;
+
+ if (!writer_.WriteString(build_string_, 0,
+ &build_string_loc))
+ return false;
+
+ info_ptr->csd_version_rva = build_string_loc.rva;
+ info_ptr->major_version = os_major_version_;
+ info_ptr->minor_version = os_minor_version_;
+ info_ptr->build_number = os_build_number_;
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteModuleStream(unsigned int index,
+ MDRawModule *module) {
+ if (dynamic_images_) {
+ // we're in a different process than the crashed process
+ DynamicImage *image = dynamic_images_->GetImage(index);
+
+ if (!image)
+ return false;
+
+ memset(module, 0, sizeof(MDRawModule));
+
+ MDLocationDescriptor string_location;
+
+ string name = image->GetFilePath();
+ if (!writer_.WriteString(name.c_str(), 0, &string_location))
+ return false;
+
+ module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide();
+ module->size_of_image = static_cast<u_int32_t>(image->GetVMSize());
+ module->module_name_rva = string_location.rva;
+
+ // We'll skip the executable module, because they don't have
+ // LC_ID_DYLIB load commands, and the crash processing server gets
+ // version information from the Plist file, anyway.
+ if (index != (uint32_t)FindExecutableModule()) {
+ module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE;
+ module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION;
+ // Convert MAC dylib version format, which is a 32 bit number, to the
+ // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits>
+ // so it fits nicely into the windows version with some massaging
+ // The mapping is:
+ // 1) upper 16 bits of MAC version go to lower 16 bits of product HI
+ // 2) Next most significant 8 bits go to upper 16 bits of product LO
+ // 3) Least significant 8 bits go to lower 16 bits of product LO
+ uint32_t modVersion = image->GetVersion();
+ module->version_info.file_version_hi = 0;
+ module->version_info.file_version_hi = modVersion >> 16;
+ module->version_info.file_version_lo |= (modVersion & 0xff00) << 8;
+ module->version_info.file_version_lo |= (modVersion & 0xff);
+ }
+
+ if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) {
+ return false;
+ }
+ } else {
+ // Getting module info in the crashed process
+ const breakpad_mach_header *header;
+ header = (breakpad_mach_header*)_dyld_get_image_header(index);
+ if (!header)
+ return false;
+
+#ifdef __LP64__
+ assert(header->magic == MH_MAGIC_64);
+
+ if(header->magic != MH_MAGIC_64)
+ return false;
+#else
+ assert(header->magic == MH_MAGIC);
+
+ if(header->magic != MH_MAGIC)
+ return false;
+#endif
+
+ int cpu_type = header->cputype;
+ unsigned long slide = _dyld_get_image_vmaddr_slide(index);
+ const char* name = _dyld_get_image_name(index);
+ const struct load_command *cmd =
+ reinterpret_cast<const struct load_command *>(header + 1);
+
+ memset(module, 0, sizeof(MDRawModule));
+
+ for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
+ if (cmd->cmd == LC_SEGMENT_ARCH) {
+
+ const breakpad_mach_segment_command *seg =
+ reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
+
+ if (!strcmp(seg->segname, "__TEXT")) {
+ MDLocationDescriptor string_location;
+
+ if (!writer_.WriteString(name, 0, &string_location))
+ return false;
+
+ module->base_of_image = seg->vmaddr + slide;
+ module->size_of_image = static_cast<u_int32_t>(seg->vmsize);
+ module->module_name_rva = string_location.rva;
+
+ bool in_memory = false;
+#if TARGET_OS_IPHONE
+ in_memory = true;
+#endif
+ if (!WriteCVRecord(module, cpu_type, name, in_memory))
+ return false;
+
+ return true;
+ }
+ }
+
+ cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
+ }
+ }
+
+ return true;
+}
+
+int MinidumpGenerator::FindExecutableModule() {
+ if (dynamic_images_) {
+ int index = dynamic_images_->GetExecutableImageIndex();
+
+ if (index >= 0) {
+ return index;
+ }
+ } else {
+ int image_count = _dyld_image_count();
+ const struct mach_header *header;
+
+ for (int index = 0; index < image_count; ++index) {
+ header = _dyld_get_image_header(index);
+
+ if (header->filetype == MH_EXECUTE)
+ return index;
+ }
+ }
+
+ // failed - just use the first image
+ return 0;
+}
+
+bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type,
+ const char *module_path, bool in_memory) {
+ TypedMDRVA<MDCVInfoPDB70> cv(&writer_);
+
+ // Only return the last path component of the full module path
+ const char *module_name = strrchr(module_path, '/');
+
+ // Increment past the slash
+ if (module_name)
+ ++module_name;
+ else
+ module_name = "<Unknown>";
+
+ size_t module_name_length = strlen(module_name);
+
+ if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t)))
+ return false;
+
+ if (!cv.CopyIndexAfterObject(0, module_name, module_name_length))
+ return false;
+
+ module->cv_record = cv.location();
+ MDCVInfoPDB70 *cv_ptr = cv.get();
+ cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE;
+ cv_ptr->age = 0;
+
+ // Get the module identifier
+ unsigned char identifier[16];
+ bool result = false;
+ if (in_memory) {
+ MacFileUtilities::MachoID macho(module_path,
+ reinterpret_cast<void *>(module->base_of_image),
+ static_cast<size_t>(module->size_of_image));
+ result = macho.UUIDCommand(cpu_type, identifier);
+ if (!result)
+ result = macho.MD5(cpu_type, identifier);
+ }
+
+ if (!result) {
+ FileID file_id(module_path);
+ result = file_id.MachoIdentifier(cpu_type, identifier);
+ }
+
+ if (result) {
+ cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 |
+ (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 |
+ (uint32_t)identifier[3];
+ cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5];
+ cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7];
+ cv_ptr->signature.data4[0] = identifier[8];
+ cv_ptr->signature.data4[1] = identifier[9];
+ cv_ptr->signature.data4[2] = identifier[10];
+ cv_ptr->signature.data4[3] = identifier[11];
+ cv_ptr->signature.data4[4] = identifier[12];
+ cv_ptr->signature.data4[5] = identifier[13];
+ cv_ptr->signature.data4[6] = identifier[14];
+ cv_ptr->signature.data4[7] = identifier[15];
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteModuleListStream(
+ MDRawDirectory *module_list_stream) {
+ TypedMDRVA<MDRawModuleList> list(&writer_);
+
+ size_t image_count = dynamic_images_ ?
+ static_cast<size_t>(dynamic_images_->GetImageCount()) :
+ _dyld_image_count();
+
+ if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE))
+ return false;
+
+ module_list_stream->stream_type = MD_MODULE_LIST_STREAM;
+ module_list_stream->location = list.location();
+ list.get()->number_of_modules = image_count;
+
+ // Write out the executable module as the first one
+ MDRawModule module;
+ size_t executableIndex = FindExecutableModule();
+
+ if (!WriteModuleStream(executableIndex, &module)) {
+ return false;
+ }
+
+ list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
+ int destinationIndex = 1; // Write all other modules after this one
+
+ for (size_t i = 0; i < image_count; ++i) {
+ if (i != executableIndex) {
+ if (!WriteModuleStream(i, &module)) {
+ return false;
+ }
+
+ list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE);
+ }
+ }
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) {
+ TypedMDRVA<MDRawMiscInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ misc_info_stream->stream_type = MD_MISC_INFO_STREAM;
+ misc_info_stream->location = info.location();
+
+ MDRawMiscInfo *info_ptr = info.get();
+ info_ptr->size_of_info = static_cast<u_int32_t>(sizeof(MDRawMiscInfo));
+ info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID |
+ MD_MISCINFO_FLAGS1_PROCESS_TIMES |
+ MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO;
+
+ // Process ID
+ info_ptr->process_id = getpid();
+
+ // Times
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) != -1) {
+ // Omit the fractional time since the MDRawMiscInfo only wants seconds
+ info_ptr->process_user_time =
+ static_cast<u_int32_t>(usage.ru_utime.tv_sec);
+ info_ptr->process_kernel_time =
+ static_cast<u_int32_t>(usage.ru_stime.tv_sec);
+ }
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
+ static_cast<int>(info_ptr->process_id) };
+ u_int mibsize = static_cast<u_int>(sizeof(mib) / sizeof(mib[0]));
+ struct kinfo_proc proc;
+ size_t size = sizeof(proc);
+ if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) {
+ info_ptr->process_create_time =
+ static_cast<u_int32_t>(proc.kp_proc.p_starttime.tv_sec);
+ }
+
+ // Speed
+ uint64_t speed;
+ const uint64_t kOneMillion = 1000 * 1000;
+ size = sizeof(speed);
+ sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0);
+ info_ptr->processor_max_mhz = static_cast<u_int32_t>(speed / kOneMillion);
+ info_ptr->processor_mhz_limit = static_cast<u_int32_t>(speed / kOneMillion);
+ size = sizeof(speed);
+ sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
+ info_ptr->processor_current_mhz = static_cast<u_int32_t>(speed / kOneMillion);
+
+ return true;
+}
+
+bool MinidumpGenerator::WriteBreakpadInfoStream(
+ MDRawDirectory *breakpad_info_stream) {
+ TypedMDRVA<MDRawBreakpadInfo> info(&writer_);
+
+ if (!info.Allocate())
+ return false;
+
+ breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM;
+ breakpad_info_stream->location = info.location();
+ MDRawBreakpadInfo *info_ptr = info.get();
+
+ if (exception_thread_ && exception_type_) {
+ info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
+ MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
+ info_ptr->dump_thread_id = handler_thread_;
+ info_ptr->requesting_thread_id = exception_thread_;
+ } else {
+ info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID;
+ info_ptr->dump_thread_id = handler_thread_;
+ info_ptr->requesting_thread_id = 0;
+ }
+
+ return true;
+}
+
+} // namespace google_breakpad