Fix a couple of bugs where we generate incorrect minidump files on Linux.o
Patch by Markus Gutschke <markus@chromium.org>. R=thestig Review URL: http://breakpad.appspot.com/150001 Review URL: http://breakpad.appspot.com/155001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@649 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
3a69e0e1d1
commit
f5c8f6fb61
4 changed files with 310 additions and 30 deletions
|
@ -72,6 +72,7 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include <sys/signal.h>
|
#include <sys/signal.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
|
@ -268,7 +269,10 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||||
// crashed. The default action for all the signals which we catch is Core, so
|
// crashed. The default action for all the signals which we catch is Core, so
|
||||||
// this is the end of us.
|
// this is the end of us.
|
||||||
signal(sig, SIG_DFL);
|
signal(sig, SIG_DFL);
|
||||||
tgkill(getpid(), sys_gettid(), sig);
|
|
||||||
|
// TODO(markus): mask signal and return to caller
|
||||||
|
tgkill(getpid(), syscall(__NR_gettid), sig);
|
||||||
|
_exit(1);
|
||||||
|
|
||||||
// not reached.
|
// not reached.
|
||||||
}
|
}
|
||||||
|
@ -296,7 +300,7 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Allow ourselves to be dumped.
|
// Allow ourselves to be dumped.
|
||||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
prctl(PR_SET_DUMPABLE, 1);
|
||||||
CrashContext context;
|
CrashContext context;
|
||||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||||
|
@ -309,7 +313,7 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||||
sizeof(context.float_state));
|
sizeof(context.float_state));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
context.tid = sys_gettid();
|
context.tid = syscall(__NR_gettid);
|
||||||
if (crash_handler_ != NULL) {
|
if (crash_handler_ != NULL) {
|
||||||
if (crash_handler_(&context, sizeof(context),
|
if (crash_handler_(&context, sizeof(context),
|
||||||
callback_context_)) {
|
callback_context_)) {
|
||||||
|
|
|
@ -75,6 +75,26 @@ static bool SuspendThread(pid_t pid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if defined(__i386) || defined(__x86_64)
|
||||||
|
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
||||||
|
// the seccomp sandbox. Not only does this cause difficulties down the line
|
||||||
|
// when trying to dump the thread's stack, it also results in the minidumps
|
||||||
|
// containing information about the trusted threads. This information is
|
||||||
|
// generally completely meaningless and just pollutes the minidumps.
|
||||||
|
// We thus test the stack pointer and exclude any threads that are part of
|
||||||
|
// the seccomp sandbox's trusted code.
|
||||||
|
user_regs_struct regs;
|
||||||
|
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 ||
|
||||||
|
#if defined(__i386)
|
||||||
|
!regs.esp
|
||||||
|
#elif defined(__x86_64)
|
||||||
|
!regs.rsp
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,11 +131,19 @@ bool LinuxDumper::Init() {
|
||||||
bool LinuxDumper::ThreadsSuspend() {
|
bool LinuxDumper::ThreadsSuspend() {
|
||||||
if (threads_suspended_)
|
if (threads_suspended_)
|
||||||
return true;
|
return true;
|
||||||
bool good = true;
|
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||||
for (size_t i = 0; i < threads_.size(); ++i)
|
if (!SuspendThread(threads_[i])) {
|
||||||
good &= SuspendThread(threads_[i]);
|
// If the thread either disappeared before we could attach to it, or if
|
||||||
|
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||||
|
// silently drop it from the minidump.
|
||||||
|
memmove(&threads_[i], &threads_[i+1],
|
||||||
|
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||||
|
threads_.resize(threads_.size() - 1);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
threads_suspended_ = true;
|
threads_suspended_ = true;
|
||||||
return good;
|
return threads_.size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LinuxDumper::ThreadsResume() {
|
bool LinuxDumper::ThreadsResume() {
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <link.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
|
@ -62,20 +63,10 @@
|
||||||
#include "client/linux/handler/exception_handler.h"
|
#include "client/linux/handler/exception_handler.h"
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
#include "client/linux/minidump_writer/line_reader.h"
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||||
|
#include "client/linux/minidump_writer/minidump_extension_linux.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "common/linux/linux_syscall_support.h"
|
#include "common/linux/linux_syscall_support.h"
|
||||||
|
|
||||||
// These are additional minidump stream values which are specific to the linux
|
|
||||||
// breakpad implementation.
|
|
||||||
enum {
|
|
||||||
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
|
|
||||||
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
|
|
||||||
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
|
|
||||||
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
|
|
||||||
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
|
|
||||||
MD_LINUX_AUXV = 0x47670008 /* /proc/$x/auxv */
|
|
||||||
};
|
|
||||||
|
|
||||||
// Minidump defines register structures which are different from the raw
|
// Minidump defines register structures which are different from the raw
|
||||||
// structures which we get from the kernel. These are platform specific
|
// structures which we get from the kernel. These are platform specific
|
||||||
// functions to juggle the ucontext and user structures into minidump format.
|
// functions to juggle the ucontext and user structures into minidump format.
|
||||||
|
@ -390,9 +381,26 @@ class MinidumpWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Dump() {
|
bool Dump() {
|
||||||
|
// The dynamic linker makes information available that helps gdb find all
|
||||||
|
// DSOs loaded into the program. If we can access this information, we dump
|
||||||
|
// it to a MD_LINUX_DSO_DEBUG stream.
|
||||||
|
struct r_debug* r_debug = NULL;
|
||||||
|
uint32_t dynamic_length = 0;
|
||||||
|
|
||||||
|
for (int i = 0;;) {
|
||||||
|
ElfW(Dyn) dyn;
|
||||||
|
dynamic_length += sizeof(dyn);
|
||||||
|
dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn));
|
||||||
|
if (dyn.d_tag == DT_DEBUG) {
|
||||||
|
r_debug = (struct r_debug*)dyn.d_un.d_ptr;
|
||||||
|
continue;
|
||||||
|
} else if (dyn.d_tag == DT_NULL)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// A minidump file contains a number of tagged streams. This is the number
|
// A minidump file contains a number of tagged streams. This is the number
|
||||||
// of stream which we write.
|
// of stream which we write.
|
||||||
static const unsigned kNumWriters = 11;
|
const unsigned kNumWriters = 11 + !!r_debug;
|
||||||
|
|
||||||
TypedMDRVA<MDRawHeader> header(&minidump_writer_);
|
TypedMDRVA<MDRawHeader> header(&minidump_writer_);
|
||||||
TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
|
TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
|
||||||
|
@ -457,11 +465,18 @@ class MinidumpWriter {
|
||||||
NullifyDirectoryEntry(&dirent);
|
NullifyDirectoryEntry(&dirent);
|
||||||
dir.CopyIndex(dir_index++, &dirent);
|
dir.CopyIndex(dir_index++, &dirent);
|
||||||
|
|
||||||
dirent.stream_type = MD_LINUX_AUXV;
|
dirent.stream_type = MD_LINUX_MAPS;
|
||||||
if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
|
if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
|
||||||
NullifyDirectoryEntry(&dirent);
|
NullifyDirectoryEntry(&dirent);
|
||||||
dir.CopyIndex(dir_index++, &dirent);
|
dir.CopyIndex(dir_index++, &dirent);
|
||||||
|
|
||||||
|
if (r_debug) {
|
||||||
|
dirent.stream_type = MD_LINUX_DSO_DEBUG;
|
||||||
|
if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length))
|
||||||
|
NullifyDirectoryEntry(&dirent);
|
||||||
|
dir.CopyIndex(dir_index++, &dirent);
|
||||||
|
}
|
||||||
|
|
||||||
// If you add more directory entries, don't forget to update kNumWriters,
|
// If you add more directory entries, don't forget to update kNumWriters,
|
||||||
// above.
|
// above.
|
||||||
|
|
||||||
|
@ -469,6 +484,123 @@ class MinidumpWriter {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the top of the stack is part of a system call that has been
|
||||||
|
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
||||||
|
// all the way back to the point where the interception happened.
|
||||||
|
void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
|
||||||
|
uint8_t* stack_copy) {
|
||||||
|
#if defined(__x86_64)
|
||||||
|
u_int64_t bp = cpu->rbp;
|
||||||
|
u_int64_t top = thread.stack.start_of_memory_range;
|
||||||
|
for (int i = 4; i--; ) {
|
||||||
|
if (bp < top ||
|
||||||
|
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||||
|
thread.stack.memory.data_size ||
|
||||||
|
bp & 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint64_t old_top = top;
|
||||||
|
top = bp;
|
||||||
|
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||||
|
memcpy(&bp, bp_addr, sizeof(bp));
|
||||||
|
if (bp == 0xDEADBEEFDEADBEEFull) {
|
||||||
|
struct {
|
||||||
|
uint64_t r15;
|
||||||
|
uint64_t r14;
|
||||||
|
uint64_t r13;
|
||||||
|
uint64_t r12;
|
||||||
|
uint64_t r11;
|
||||||
|
uint64_t r10;
|
||||||
|
uint64_t r9;
|
||||||
|
uint64_t r8;
|
||||||
|
uint64_t rdi;
|
||||||
|
uint64_t rsi;
|
||||||
|
uint64_t rdx;
|
||||||
|
uint64_t rcx;
|
||||||
|
uint64_t rbx;
|
||||||
|
uint64_t deadbeef;
|
||||||
|
uint64_t rbp;
|
||||||
|
uint64_t fakeret;
|
||||||
|
uint64_t ret;
|
||||||
|
/* char redzone[128]; */
|
||||||
|
} seccomp_stackframe;
|
||||||
|
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||||
|
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||||
|
sizeof(seccomp_stackframe) >
|
||||||
|
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(&seccomp_stackframe,
|
||||||
|
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||||
|
sizeof(seccomp_stackframe));
|
||||||
|
cpu->rbx = seccomp_stackframe.rbx;
|
||||||
|
cpu->rcx = seccomp_stackframe.rcx;
|
||||||
|
cpu->rdx = seccomp_stackframe.rdx;
|
||||||
|
cpu->rsi = seccomp_stackframe.rsi;
|
||||||
|
cpu->rdi = seccomp_stackframe.rdi;
|
||||||
|
cpu->rbp = seccomp_stackframe.rbp;
|
||||||
|
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
||||||
|
cpu->r8 = seccomp_stackframe.r8;
|
||||||
|
cpu->r9 = seccomp_stackframe.r9;
|
||||||
|
cpu->r10 = seccomp_stackframe.r10;
|
||||||
|
cpu->r11 = seccomp_stackframe.r11;
|
||||||
|
cpu->r12 = seccomp_stackframe.r12;
|
||||||
|
cpu->r13 = seccomp_stackframe.r13;
|
||||||
|
cpu->r14 = seccomp_stackframe.r14;
|
||||||
|
cpu->r15 = seccomp_stackframe.r15;
|
||||||
|
cpu->rip = seccomp_stackframe.fakeret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(__i386)
|
||||||
|
u_int32_t bp = cpu->ebp;
|
||||||
|
u_int32_t top = thread.stack.start_of_memory_range;
|
||||||
|
for (int i = 4; i--; ) {
|
||||||
|
if (bp < top ||
|
||||||
|
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||||
|
thread.stack.memory.data_size ||
|
||||||
|
bp & 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint32_t old_top = top;
|
||||||
|
top = bp;
|
||||||
|
u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||||
|
memcpy(&bp, bp_addr, sizeof(bp));
|
||||||
|
if (bp == 0xDEADBEEFu) {
|
||||||
|
struct {
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t deadbeef;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t fakeret;
|
||||||
|
uint32_t ret;
|
||||||
|
} seccomp_stackframe;
|
||||||
|
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||||
|
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||||
|
sizeof(seccomp_stackframe) >
|
||||||
|
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(&seccomp_stackframe,
|
||||||
|
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||||
|
sizeof(seccomp_stackframe));
|
||||||
|
cpu->ebx = seccomp_stackframe.ebx;
|
||||||
|
cpu->ecx = seccomp_stackframe.ecx;
|
||||||
|
cpu->edx = seccomp_stackframe.edx;
|
||||||
|
cpu->esi = seccomp_stackframe.esi;
|
||||||
|
cpu->edi = seccomp_stackframe.edi;
|
||||||
|
cpu->ebp = seccomp_stackframe.ebp;
|
||||||
|
cpu->esp = top + 4*sizeof(void*);
|
||||||
|
cpu->eip = seccomp_stackframe.fakeret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Write information about the threads.
|
// Write information about the threads.
|
||||||
bool WriteThreadListStream(MDRawDirectory* dirent) {
|
bool WriteThreadListStream(MDRawDirectory* dirent) {
|
||||||
const unsigned num_threads = dumper_.threads().size();
|
const unsigned num_threads = dumper_.threads().size();
|
||||||
|
@ -508,6 +640,7 @@ class MinidumpWriter {
|
||||||
return false;
|
return false;
|
||||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||||
CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
|
CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
|
||||||
|
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||||
thread.thread_context = cpu.location();
|
thread.thread_context = cpu.location();
|
||||||
crashing_thread_context_ = cpu.location();
|
crashing_thread_context_ = cpu.location();
|
||||||
} else {
|
} else {
|
||||||
|
@ -529,6 +662,7 @@ class MinidumpWriter {
|
||||||
return false;
|
return false;
|
||||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||||
CPUFillFromThreadInfo(cpu.get(), info);
|
CPUFillFromThreadInfo(cpu.get(), info);
|
||||||
|
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||||
thread.thread_context = cpu.location();
|
thread.thread_context = cpu.location();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -656,6 +790,83 @@ class MinidumpWriter {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
|
||||||
|
uint32_t dynamic_length) {
|
||||||
|
// The caller provided us with a pointer to "struct r_debug". We can
|
||||||
|
// look up the "r_map" field to get a linked list of all loaded DSOs.
|
||||||
|
// Our list of DSOs potentially is different from the ones in the crashing
|
||||||
|
// process. So, we have to be careful to never dereference pointers
|
||||||
|
// directly. Instead, we use CopyFromProcess() everywhere.
|
||||||
|
// See <link.h> for a more detailed discussion of the how the dynamic
|
||||||
|
// loader communicates with debuggers.
|
||||||
|
|
||||||
|
// Count the number of loaded DSOs
|
||||||
|
int dso_count = 0;
|
||||||
|
struct r_debug debug_entry;
|
||||||
|
dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug,
|
||||||
|
sizeof(debug_entry));
|
||||||
|
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||||
|
struct link_map map;
|
||||||
|
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
|
||||||
|
ptr = map.l_next;
|
||||||
|
dso_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA;
|
||||||
|
if (dso_count > 0) {
|
||||||
|
// If we have at least one DSO, create an array of MDRawLinkMap
|
||||||
|
// entries in the minidump file.
|
||||||
|
TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
|
||||||
|
if (!linkmap.AllocateArray(dso_count))
|
||||||
|
return false;
|
||||||
|
linkmap_rva = linkmap.location().rva;
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
// Iterate over DSOs and write their information to mini dump
|
||||||
|
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||||
|
struct link_map map;
|
||||||
|
dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
|
||||||
|
ptr = map.l_next;
|
||||||
|
char filename[257] = { 0 };
|
||||||
|
if (map.l_name) {
|
||||||
|
dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name,
|
||||||
|
sizeof(filename) - 1);
|
||||||
|
}
|
||||||
|
MDLocationDescriptor location;
|
||||||
|
if (!minidump_writer_.WriteString(filename, 0, &location))
|
||||||
|
return false;
|
||||||
|
MDRawLinkMap entry;
|
||||||
|
entry.name = location.rva;
|
||||||
|
entry.addr = (void*)map.l_addr;
|
||||||
|
entry.ld = (void*)map.l_ld;
|
||||||
|
linkmap.CopyIndex(idx++, &entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write MD_LINUX_DSO_DEBUG record
|
||||||
|
TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
|
||||||
|
if (!debug.AllocateObjectAndArray(1, dynamic_length))
|
||||||
|
return false;
|
||||||
|
my_memset(debug.get(), 0, sizeof(MDRawDebug));
|
||||||
|
dirent->stream_type = MD_LINUX_DSO_DEBUG;
|
||||||
|
dirent->location = debug.location();
|
||||||
|
|
||||||
|
debug.get()->version = debug_entry.r_version;
|
||||||
|
debug.get()->map = linkmap_rva;
|
||||||
|
debug.get()->dso_count = dso_count;
|
||||||
|
debug.get()->brk = (void*)debug_entry.r_brk;
|
||||||
|
debug.get()->ldbase = (void*)debug_entry.r_ldbase;
|
||||||
|
debug.get()->dynamic = (void*)&_DYNAMIC;
|
||||||
|
|
||||||
|
char *dso_debug_data = new char[dynamic_length];
|
||||||
|
dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC,
|
||||||
|
dynamic_length);
|
||||||
|
debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
|
||||||
|
delete[] dso_debug_data;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if defined(__i386)
|
#if defined(__i386)
|
||||||
uintptr_t GetStackPointer() {
|
uintptr_t GetStackPointer() {
|
||||||
|
@ -721,7 +932,7 @@ class MinidumpWriter {
|
||||||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||||
i++) {
|
i++) {
|
||||||
CpuInfoEntry* entry = &cpu_info_table[i];
|
CpuInfoEntry* entry = &cpu_info_table[i];
|
||||||
if (entry->found)
|
if (entry->found && i)
|
||||||
continue;
|
continue;
|
||||||
if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
|
if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
|
||||||
const char* value = strchr(line, ':');
|
const char* value = strchr(line, ':');
|
||||||
|
@ -807,29 +1018,48 @@ popline:
|
||||||
// We can't stat the files because several of the files that we want to
|
// We can't stat the files because several of the files that we want to
|
||||||
// read are kernel seqfiles, which always have a length of zero. So we have
|
// read are kernel seqfiles, which always have a length of zero. So we have
|
||||||
// to read as much as we can into a buffer.
|
// to read as much as we can into a buffer.
|
||||||
static const unsigned kMaxFileSize = 1024;
|
static const unsigned kBufSize = 1024 - 2*sizeof(void*);
|
||||||
uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
|
struct Buffers {
|
||||||
|
struct Buffers* next;
|
||||||
|
size_t len;
|
||||||
|
uint8_t data[kBufSize];
|
||||||
|
} *buffers =
|
||||||
|
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
|
||||||
|
buffers->next = NULL;
|
||||||
|
buffers->len = 0;
|
||||||
|
|
||||||
size_t done = 0;
|
size_t total = 0;
|
||||||
while (done < kMaxFileSize) {
|
for (struct Buffers* bufptr = buffers;;) {
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
do {
|
do {
|
||||||
r = sys_read(fd, data + done, kMaxFileSize - done);
|
r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
|
||||||
} while (r == -1 && errno == EINTR);
|
} while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
if (r < 1)
|
if (r < 1)
|
||||||
break;
|
break;
|
||||||
done += r;
|
|
||||||
|
total += r;
|
||||||
|
bufptr->len += r;
|
||||||
|
if (bufptr->len == kBufSize) {
|
||||||
|
bufptr->next =
|
||||||
|
(struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
|
||||||
|
bufptr = bufptr->next;
|
||||||
|
bufptr->next = NULL;
|
||||||
|
bufptr->len = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sys_close(fd);
|
sys_close(fd);
|
||||||
|
|
||||||
if (!done)
|
if (!total)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
UntypedMDRVA memory(&minidump_writer_);
|
UntypedMDRVA memory(&minidump_writer_);
|
||||||
if (!memory.Allocate(done))
|
if (!memory.Allocate(total))
|
||||||
return false;
|
return false;
|
||||||
memory.Copy(data, done);
|
for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
|
||||||
|
memory.Copy(pos, &buffers->data, buffers->len);
|
||||||
|
pos += buffers->len;
|
||||||
|
}
|
||||||
*result = memory.location();
|
*result = memory.location();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,24 @@ class wasteful_vector {
|
||||||
return used_;
|
return used_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void resize(unsigned sz, T c = T()) {
|
||||||
|
// No need to test "sz >= 0", as "sz" is unsigned.
|
||||||
|
if (sz <= used_) {
|
||||||
|
used_ = sz;
|
||||||
|
} else {
|
||||||
|
unsigned a = allocated_;
|
||||||
|
if (sz > a) {
|
||||||
|
while (sz > a) {
|
||||||
|
a *= 2;
|
||||||
|
}
|
||||||
|
Realloc(a);
|
||||||
|
}
|
||||||
|
while (sz > used_) {
|
||||||
|
a_[used_++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
T& operator[](size_t index) {
|
T& operator[](size_t index) {
|
||||||
return a_[index];
|
return a_[index];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue