// 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 <CoreFoundation/CoreFoundation.h>
#include <algorithm>
#include <cstdio>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <mach/host_info.h>
#include <mach/machine.h>
#include <mach/vm_statistics.h>
#include <sys/resource.h>
#include <sys/sysctl.h>

#include "client/mac/handler/minidump_generator.h"

#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_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 {

using mach_o::FileID;

#if defined(__LP64__) && __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()),
      task_context_(NULL),
      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()),
      task_context_(NULL),
      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);
    CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers);
    CFRelease(sys_vers);
    if (!read_stream) {
        return;
    }
    if (!CFReadStreamOpen(read_stream)) {
        CFRelease(read_stream);
        return;
    }
    CFMutableDataRef data = NULL;
    while (true) {
        // Actual data file tests: Mac at 480 bytes and iOS at 413 bytes.
        const CFIndex kMaxBufferLength = 1024;
        UInt8 data_bytes[kMaxBufferLength];
        CFIndex num_bytes_read =
            CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength);
        if (num_bytes_read < 0) {
            if (data) {
                CFRelease(data);
                data = NULL;
            }
            break;
        } else if (num_bytes_read == 0) {
            break;
        } else if (!data) {
            data = CFDataCreateMutable(NULL, 0);
        }
        CFDataAppendBytes(data, data_bytes, num_bytes_read);
    }
    CFReadStreamClose(read_stream);
    CFRelease(read_stream);
    if (!data) {
        return;
    }
    CFDictionaryRef list =
        static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(
            NULL, data, kCFPropertyListImmutable, NULL, NULL));
    CFRelease(data);
    if (!list) {
        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);

    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);
}

void MinidumpGenerator::SetTaskContext(breakpad_ucontext_t* task_context) {
    task_context_ = task_context;
}

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;
            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_ARM64_SUPPORT
        case CPU_TYPE_ARM64:
            return WriteStackARM64(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_ARM64_SUPPORT
        case CPU_TYPE_ARM64:
            return WriteContextARM64(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;
    }
}

uint64_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_ARM64_SUPPORT
        case CPU_TYPE_ARM64:
            return CurrentPCForStackARM64(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(0 && "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);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) {
    auto 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_);
    auto 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 AddGPR

    return true;
}
#endif

#ifdef HAS_ARM64_SUPPORT
bool MinidumpGenerator::WriteStackARM64(breakpad_thread_state_data_t state,
                                        MDMemoryDescriptor* stack_location) {
    auto machine_state = reinterpret_cast<arm_thread_state64_t*>(state);
    mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp);
    return WriteStackFromStartAddress(start_addr, stack_location);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackARM64(breakpad_thread_state_data_t state) {
    auto machine_state = reinterpret_cast<arm_thread_state64_t*>(state);

    return REGISTER_FROM_THREADSTATE(machine_state, pc);
}

bool MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state,
                                          MDLocationDescriptor* register_location) {
    TypedMDRVA<MDRawContextARM64_Old> context(&writer_);
    auto machine_state = reinterpret_cast<arm_thread_state64_t*>(state);

    if (!context.Allocate())
        return false;

    *register_location = context.location();
    MDRawContextARM64_Old* context_ptr = context.get();
    context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD;

#    define AddGPR(a) \
        context_ptr->iregs[a] = ARRAY_REGISTER_FROM_THREADSTATE(machine_state, x, a)

    context_ptr->iregs[29] = REGISTER_FROM_THREADSTATE(machine_state, fp);
    context_ptr->iregs[30] = REGISTER_FROM_THREADSTATE(machine_state, lr);
    context_ptr->iregs[31] = REGISTER_FROM_THREADSTATE(machine_state, sp);
    context_ptr->iregs[32] = 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);
    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);
#    undef AddGPR

    return true;
}
#endif

#ifdef HAS_PCC_SUPPORT
bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state,
                                      MDMemoryDescriptor* stack_location) {
    auto 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) {
    auto 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);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) {
    auto machine_state = reinterpret_cast<ppc_thread_state_t*>(state);

    return REGISTER_FROM_THREADSTATE(machine_state, srr0);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) {
    auto 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_);
    auto 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 = static_cast<__typeof__(context_ptr->a)>( \
                          REGISTER_FROM_THREADSTATE(machine_state, a))
#    define AddGPR(a) context_ptr->gpr[a] = \
    static_cast<__typeof__(context_ptr->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_);
    auto 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 = static_cast<__typeof__(context_ptr->a)>( \
                          REGISTER_FROM_THREADSTATE(machine_state, a))
#    define AddGPR(a) context_ptr->gpr[a] = \
    static_cast<__typeof__(context_ptr->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) {
    auto 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) {
    auto machine_state = reinterpret_cast<x86_thread_state64_t*>(state);

    mach_vm_address_t start_addr = static_cast<mach_vm_address_t>(
        REGISTER_FROM_THREADSTATE(machine_state, rsp));
    return WriteStackFromStartAddress(start_addr, stack_location);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) {
    auto machine_state = reinterpret_cast<i386_thread_state_t*>(state);

    return REGISTER_FROM_THREADSTATE(machine_state, eip);
}

uint64_t
    MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) {
    auto 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_);
    auto 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 = static_cast<__typeof__(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_);
    auto 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 = static_cast<__typeof__(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<uint32_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) {
    if (task_context_ && target_thread == mach_thread_self()) {
        switch (cpu_type_) {
#ifdef HAS_ARM_SUPPORT
            case CPU_TYPE_ARM:
                size_t final_size =
                    std::min(static_cast<size_t>(*count), sizeof(arm_thread_state_t));
                memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
                *count = static_cast<mach_msg_type_number_t>(final_size);
                return true;
#endif
#ifdef HAS_ARM64_SUPPORT
            case CPU_TYPE_ARM64: {
                size_t final_size =
                    std::min(static_cast<size_t>(*count), sizeof(arm_thread_state64_t));
                memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
                *count = static_cast<mach_msg_type_number_t>(final_size);
                return true;
            }
#endif
#ifdef HAS_X86_SUPPORT
            case CPU_TYPE_I386:
            case CPU_TYPE_X86_64: {
                size_t state_size = cpu_type_ == CPU_TYPE_I386 ? sizeof(i386_thread_state_t) : sizeof(x86_thread_state64_t);
                size_t final_size =
                    std::min(static_cast<size_t>(*count), state_size);
                memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size);
                *count = static_cast<mach_msg_type_number_t>(final_size);
                return true;
            }
#endif
        }
    }

    thread_state_flavor_t flavor;
    switch (cpu_type_) {
#ifdef HAS_ARM_SUPPORT
        case CPU_TYPE_ARM:
            flavor = ARM_THREAD_STATE;
            break;
#endif
#ifdef HAS_ARM64_SUPPORT
        case CPU_TYPE_ARM64:
            flavor = ARM_THREAD_STATE64;
            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;
    }
    // For some unknown reason,
    // sometimes thread_get_state returns this value on Rosetta,
    // but still succeeds
    const int ROSETTA_SUCCESS = 268435459;
    auto tgs = thread_get_state(target_thread, flavor, state, count);
    return tgs == KERN_SUCCESS || tgs == ROSETTA_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 (GetThreadState(exception_thread_, state, &stateCount)) {
            uint64_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 = static_cast<vm_address_t>(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;
            vm_region_recurse_info_t recurse_info;
            recurse_info = reinterpret_cast<vm_region_recurse_info_t>(&info);

            kern_return_t ret =
                mach_vm_region_recurse(crashing_task_,
                                       &addr,
                                       &size,
                                       &nesting_level,
                                       recurse_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));
                uintptr_t range_diff = end_of_range -
                                       static_cast<uintptr_t>(ip_memory_d.start_of_memory_range);
                ip_memory_d.memory.data_size = static_cast<uint32_t>(range_diff);
                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.
    size_t 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 = static_cast<uint32_t>(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_ARM64_SUPPORT
        case CPU_TYPE_ARM64:
            info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM64_OLD;
            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 = static_cast<uint16_t>(
                (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 = static_cast<uint8_t>(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<uint32_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 != static_cast<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);
        auto 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) {
                auto 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<uint32_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(uint8_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(
            reinterpret_cast<void*>(module->base_of_image),
            static_cast<size_t>(module->size_of_image));
        result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
        if (!result)
            result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
    }

    if (!result) {
        FileID file_id(module_path);
        result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier);
    }

    if (result) {
        cv_ptr->signature.data1 =
            static_cast<uint32_t>(identifier[0]) << 24 |
            static_cast<uint32_t>(identifier[1]) << 16 |
            static_cast<uint32_t>(identifier[2]) << 8 |
            static_cast<uint32_t>(identifier[3]);
        cv_ptr->signature.data2 =
            static_cast<uint16_t>(identifier[4] << 8) | identifier[5];
        cv_ptr->signature.data3 =
            static_cast<uint16_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_);

    uint32_t image_count = dynamic_images_ ? 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 = static_cast<uint32_t>(image_count);

    // Write out the executable module as the first one
    MDRawModule module;
    uint32_t executableIndex = FindExecutableModule();

    if (!WriteModuleStream(static_cast<unsigned>(executableIndex), &module)) {
        return false;
    }

    list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE);
    int destinationIndex = 1;  // Write all other modules after this one

    for (uint32_t i = 0; i < image_count; ++i) {
        if (i != executableIndex) {
            if (!WriteModuleStream(static_cast<unsigned>(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<uint32_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<uint32_t>(usage.ru_utime.tv_sec);
        info_ptr->process_kernel_time =
            static_cast<uint32_t>(usage.ru_stime.tv_sec);
    }
    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, static_cast<int>(info_ptr->process_id)};
    uint mibsize = static_cast<uint>(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<uint32_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<uint32_t>(speed / kOneMillion);
    info_ptr->processor_mhz_limit = static_cast<uint32_t>(speed / kOneMillion);
    size = sizeof(speed);
    sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0);
    info_ptr->processor_current_mhz = static_cast<uint32_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
