Allow out-of-process minidump generation to work on processes of a different CPU architecture

R=mark at http://breakpad.appspot.com/241001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@746 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
ted.mielczarek@gmail.com 2010-12-15 21:55:56 +00:00
parent 0d9bd40775
commit 0344a368de
10 changed files with 1262 additions and 653 deletions

View file

@ -31,7 +31,9 @@
#include <cstdio>
#include <mach/host_info.h>
#include <mach/i386/thread_status.h>
#include <mach/mach_vm.h>
#include <mach/ppc/thread_status.h>
#include <mach/vm_statistics.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
@ -65,6 +67,7 @@ MinidumpGenerator::MinidumpGenerator()
exception_thread_(0),
crashing_task_(mach_task_self()),
handler_thread_(mach_thread_self()),
cpu_type_(DynamicImages::GetNativeCPUType()),
dynamic_images_(NULL),
memory_blocks_(&allocator_) {
GatherSystemInformation();
@ -81,12 +84,15 @@ MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task,
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();
@ -254,8 +260,10 @@ size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) {
return 0;
}
if ((stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK) {
if (((cpu_type_ & CPU_ARCH_ABI64) &&
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_64BIT) ||
(!(cpu_type_ & CPU_ARCH_ABI64) &&
(stack_region_base + stack_region_size) == TOP_OF_THREAD0_STACK_32BIT)) {
// The stack for thread 0 needs to extend all the way to
// 0xc0000000 on 32 bit and 00007fff5fc00000 on 64bit. HOWEVER,
// for many processes, the stack is first created in one page
@ -305,20 +313,15 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return false;
if (dynamic_images_) {
kern_return_t kr;
void *stack_memory = ReadTaskMemory(crashing_task_,
(void*)start_addr,
size,
&kr);
if (stack_memory == NULL) {
vector<uint8_t> stack_memory;
if (ReadTaskMemory(crashing_task_,
start_addr,
size,
stack_memory) != KERN_SUCCESS) {
return false;
}
result = memory.Copy(stack_memory, size);
free(stack_memory);
result = memory.Copy(&stack_memory[0], size);
} else {
result = memory.Copy(reinterpret_cast<const void *>(start_addr), size);
}
@ -330,34 +333,99 @@ bool MinidumpGenerator::WriteStackFromStartAddress(
return result;
}
#if TARGET_CPU_PPC || TARGET_CPU_PPC64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return WriteStackPPC(state, stack_location);
case CPU_TYPE_POWERPC64:
return WriteStackPPC64(state, stack_location);
case CPU_TYPE_I386:
return WriteStackX86(state, stack_location);
case CPU_TYPE_X86_64:
return WriteStackX86_64(state, stack_location);
default:
return false;
}
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return WriteContextPPC(state, register_location);
case CPU_TYPE_POWERPC64:
return WriteContextPPC64(state, register_location);
case CPU_TYPE_I386:
return WriteContextX86(state, register_location);
case CPU_TYPE_X86_64:
return WriteContextX86_64(state, register_location);
default:
return false;
}
}
u_int64_t MinidumpGenerator::CurrentPCForStack(
breakpad_thread_state_data_t state) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
return CurrentPCForStackPPC(state);
case CPU_TYPE_POWERPC64:
return CurrentPCForStackPPC64(state);
case CPU_TYPE_I386:
return CurrentPCForStackX86(state);
case CPU_TYPE_X86_64:
return CurrentPCForStackX86_64(state);
default:
assert("Unknown CPU type!");
return 0;
}
}
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::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
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);
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
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();
MinidumpContext *context_ptr = context.get();
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)
@ -402,57 +470,124 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
AddGPR(29);
AddGPR(30);
AddGPR(31);
#if TARGET_CPU_PPC
/* The mq register is only for PPC */
AddReg(mq);
#endif
#undef AddReg
#undef AddGPR
return true;
}
#elif TARGET_CPU_X86 || TARGET_CPU_X86_64
bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp);
#else
mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp);
#endif
return WriteStackFromStartAddress(start_addr, stack_location);
}
u_int64_t
MinidumpGenerator::CurrentPCForStack(breakpad_thread_state_data_t state) {
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
#if TARGET_CPU_X86_64
return REGISTER_FROM_THREADSTATE(machine_state, rip);
#else
return REGISTER_FROM_THREADSTATE(machine_state, eip);
#endif
}
bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location) {
TypedMDRVA<MinidumpContext> context(&writer_);
breakpad_thread_state_t *machine_state =
reinterpret_cast<breakpad_thread_state_t *>(state);
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();
MinidumpContext *context_ptr = context.get();
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)
#if TARGET_CPU_X86
#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;
}
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);
@ -472,7 +607,26 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
AddReg(eflags);
AddReg(eip);
#else
#undef AddReg(a)
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);
@ -495,16 +649,38 @@ bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state,
// 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>(machine_state->__rflags);
context_ptr->eflags = static_cast<u_int32_t>(REGISTER_FROM_THREADSTATE(machine_state, rflags));
AddReg(cs);
AddReg(fs);
AddReg(gs);
#endif
#undef AddReg(a)
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_) {
case CPU_TYPE_POWERPC:
flavor = PPC_THREAD_STATE;
break;
case CPU_TYPE_POWERPC64:
flavor = PPC_THREAD_STATE64;
break;
case CPU_TYPE_I386:
flavor = i386_THREAD_STATE;
break;
case CPU_TYPE_X86_64:
flavor = x86_THREAD_STATE64;
break;
default:
return false;
}
return thread_get_state(target_thread, flavor,
state, count) == KERN_SUCCESS;
}
bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
MDRawThread *thread) {
@ -512,9 +688,7 @@ bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id,
mach_msg_type_number_t state_count
= static_cast<mach_msg_type_number_t>(sizeof(state));
if (thread_get_state(thread_id, BREAKPAD_MACHINE_THREAD_STATE,
state, &state_count) ==
KERN_SUCCESS) {
if (GetThreadState(thread_id, state, &state_count)) {
if (!WriteStack(state, &thread->stack))
return false;
@ -653,21 +827,15 @@ bool MinidumpGenerator::WriteMemoryListStream(
if (dynamic_images_) {
// Out-of-process.
kern_return_t kr;
void *memory =
ReadTaskMemory(
crashing_task_,
reinterpret_cast<const void *>(ip_memory_d.start_of_memory_range),
ip_memory_d.memory.data_size,
&kr);
if (memory == NULL) {
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, ip_memory_d.memory.data_size);
free(memory);
ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size);
} else {
// In-process, just copy from local memory.
ip_memory.Copy(
@ -733,23 +901,23 @@ bool MinidumpGenerator::WriteSystemInfoStream(
system_info_stream->location = info.location();
// CPU Information
uint32_t cpu_type;
size_t len = sizeof(cpu_type);
sysctlbyname("hw.cputype", &cpu_type, &len, NULL, 0);
uint32_t number_of_processors;
len = sizeof(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) {
switch (cpu_type_) {
case CPU_TYPE_POWERPC:
case CPU_TYPE_POWERPC64:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
break;
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
// hw.cputype is currently always I386 even on an x86-64 system
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__
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
// ebx is used for PIC code, so we need
// to preserve it.
#define cpuid(op,eax,ebx,ecx,edx) \
@ -763,7 +931,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
"=d" (edx) \
: "0" (op))
#elif defined(__x86_64__)
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
#define cpuid(op,eax,ebx,ecx,edx) \
asm ("cpuid \n\t" \
: "=a" (eax), \
@ -837,19 +1005,12 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
if (!image)
return false;
const breakpad_mach_header *header = image->GetMachHeader();
if (!header)
return false;
int cpu_type = header->cputype;
memset(module, 0, sizeof(MDRawModule));
MDLocationDescriptor string_location;
const char* name = image->GetFilePath();
if (!writer_.WriteString(name, 0, &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();
@ -876,12 +1037,11 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
module->version_info.file_version_lo |= (modVersion & 0xff);
}
if (!WriteCVRecord(module, cpu_type, name)) {
if (!WriteCVRecord(module, image->GetCPUType(), name.c_str())) {
return false;
}
} else {
// we're getting module info in the crashed process
// Getting module info in the crashed process
const breakpad_mach_header *header;
header = (breakpad_mach_header*)_dyld_get_image_header(index);
if (!header)
@ -903,7 +1063,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
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);
reinterpret_cast<const struct load_command *>(header + 1);
memset(module, 0, sizeof(MDRawModule));
@ -911,7 +1071,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
if (cmd->cmd == LC_SEGMENT_ARCH) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
MDLocationDescriptor string_location;