| Index: breakpad/linux/minidump_writer.cc
|
| diff --git a/breakpad/linux/minidump_writer.cc b/breakpad/linux/minidump_writer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5a1d6c64914dc11c896e24c06c533e0a1e42bf5b
|
| --- /dev/null
|
| +++ b/breakpad/linux/minidump_writer.cc
|
| @@ -0,0 +1,611 @@
|
| +// Copyright (c) 2009, 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.
|
| +
|
| +// This code writes out minidump files:
|
| +// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
|
| +//
|
| +// Minidumps are a Microsoft format which Breakpad uses for recording crash
|
| +// dumps. This code has to run in a compromised environment (the address space
|
| +// may have received SIGSEGV), thus the following rules apply:
|
| +// * You may not enter the dynamic linker. This means that we cannot call
|
| +// any symbols in a shared library (inc libc). Because of this we replace
|
| +// libc functions in linux_libc_support.h.
|
| +// * You may not call syscalls via the libc wrappers. This rule is a subset
|
| +// of the first rule but it bears repeating. We have direct wrappers
|
| +// around the system calls in linux_syscall_support.h.
|
| +// * You may not malloc. There's an alternative allocator in memory.h and
|
| +// a canonical instance in the LinuxDumper object. We use the placement
|
| +// new form to allocate objects and we don't delete them.
|
| +
|
| +#include "breakpad/linux/minidump_writer.h"
|
| +#include "client/minidump_file_writer-inl.h"
|
| +
|
| +#include <unistd.h>
|
| +#include <fcntl.h>
|
| +#include <errno.h>
|
| +#include <sys/ucontext.h>
|
| +#include <sys/user.h>
|
| +
|
| +#include "client/minidump_file_writer.h"
|
| +#include "google_breakpad/common/minidump_format.h"
|
| +#include "google_breakpad/common/minidump_cpu_amd64.h"
|
| +#include "google_breakpad/common/minidump_cpu_x86.h"
|
| +
|
| +#include "breakpad/linux/exception_handler.h"
|
| +#include "breakpad/linux/linux_dumper.h"
|
| +#include "breakpad/linux/linux_libc_support.h"
|
| +#include "breakpad/linux/linux_syscall_support.h"
|
| +#include "breakpad/linux/minidump_format_linux.h"
|
| +
|
| +// Minidump defines register structures which are different from the raw
|
| +// structures which we get from the kernel. These are platform specific
|
| +// functions to juggle the ucontext and user structures into minidump format.
|
| +#if defined(__i386)
|
| +typedef MDRawContextX86 RawContextCPU;
|
| +
|
| +// Write a uint16_t to memory
|
| +// out: memory location to write to
|
| +// v: value to write.
|
| +static void U16(void* out, uint16_t v) {
|
| + memcpy(out, &v, sizeof(v));
|
| +}
|
| +
|
| +// Write a uint32_t to memory
|
| +// out: memory location to write to
|
| +// v: value to write.
|
| +static void U32(void* out, uint32_t v) {
|
| + memcpy(out, &v, sizeof(v));
|
| +}
|
| +
|
| +// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
|
| +// out: the minidump structure
|
| +// info: the collection of register structures.
|
| +static void CPUFillFromThreadInfo(MDRawContextX86 *out,
|
| + const google_breakpad::ThreadInfo &info) {
|
| + out->context_flags = MD_CONTEXT_X86_ALL;
|
| +
|
| + out->dr0 = info.dregs[0];
|
| + out->dr1 = info.dregs[1];
|
| + out->dr2 = info.dregs[2];
|
| + out->dr3 = info.dregs[3];
|
| + // 4 and 5 deliberatly omitted because they aren't included in the minidump
|
| + // format.
|
| + out->dr6 = info.dregs[6];
|
| + out->dr7 = info.dregs[7];
|
| +
|
| + out->gs = info.regs.xgs;
|
| + out->fs = info.regs.xfs;
|
| + out->es = info.regs.xes;
|
| + out->ds = info.regs.xds;
|
| +
|
| + out->edi = info.regs.edi;
|
| + out->esi = info.regs.esi;
|
| + out->ebx = info.regs.ebx;
|
| + out->edx = info.regs.edx;
|
| + out->ecx = info.regs.ecx;
|
| + out->eax = info.regs.eax;
|
| +
|
| + out->ebp = info.regs.ebp;
|
| + out->eip = info.regs.eip;
|
| + out->cs = info.regs.xcs;
|
| + out->eflags = info.regs.eflags;
|
| + out->esp = info.regs.esp;
|
| + out->ss = info.regs.xss;
|
| +
|
| + out->float_save.control_word = info.fpregs.cwd;
|
| + out->float_save.status_word = info.fpregs.swd;
|
| + out->float_save.tag_word = info.fpregs.twd;
|
| + out->float_save.error_offset = info.fpregs.fip;
|
| + out->float_save.error_selector = info.fpregs.fcs;
|
| + out->float_save.data_offset = info.fpregs.foo;
|
| + out->float_save.data_selector = info.fpregs.fos;
|
| +
|
| + // 8 registers * 10 bytes per register.
|
| + memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
|
| +
|
| + // This matches the Intel fpsave format.
|
| + U16(out->extended_registers + 0, info.fpregs.cwd);
|
| + U16(out->extended_registers + 2, info.fpregs.swd);
|
| + U16(out->extended_registers + 4, info.fpregs.twd);
|
| + U16(out->extended_registers + 6, info.fpxregs.fop);
|
| + U32(out->extended_registers + 8, info.fpxregs.fip);
|
| + U16(out->extended_registers + 12, info.fpxregs.fcs);
|
| + U32(out->extended_registers + 16, info.fpregs.foo);
|
| + U16(out->extended_registers + 20, info.fpregs.fos);
|
| + U32(out->extended_registers + 24, info.fpxregs.mxcsr);
|
| +
|
| + memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
|
| + memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
|
| +}
|
| +
|
| +// Juggle an x86 ucontext into minidump format
|
| +// out: the minidump structure
|
| +// info: the collection of register structures.
|
| +static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc) {
|
| + const greg_t* regs = uc->uc_mcontext.gregs;
|
| + const fpregset_t fp = uc->uc_mcontext.fpregs;
|
| +
|
| + out->context_flags = MD_CONTEXT_X86_FULL |
|
| + MD_CONTEXT_X86_FLOATING_POINT;
|
| +
|
| + out->gs = regs[REG_GS];
|
| + out->fs = regs[REG_FS];
|
| + out->es = regs[REG_ES];
|
| + out->ds = regs[REG_DS];
|
| +
|
| + out->edi = regs[REG_EDI];
|
| + out->esi = regs[REG_ESI];
|
| + out->ebx = regs[REG_EBX];
|
| + out->edx = regs[REG_EDX];
|
| + out->ecx = regs[REG_ECX];
|
| + out->eax = regs[REG_EAX];
|
| +
|
| + out->ebp = regs[REG_EBP];
|
| + out->eip = regs[REG_EIP];
|
| + out->cs = regs[REG_CS];
|
| + out->eflags = regs[REG_EFL];
|
| + out->esp = regs[REG_UESP];
|
| + out->ss = regs[REG_SS];
|
| +
|
| + out->float_save.control_word = fp->cw;
|
| + out->float_save.status_word = fp->sw;
|
| + out->float_save.tag_word = fp->tag;
|
| + out->float_save.error_offset = fp->ipoff;
|
| + out->float_save.error_selector = fp->cssel;
|
| + out->float_save.data_offset = fp->dataoff;
|
| + out->float_save.data_selector = fp->datasel;
|
| +
|
| + // 8 registers * 10 bytes per register.
|
| + memcpy(out->float_save.register_area, fp->_st, 10 * 8);
|
| +}
|
| +
|
| +#elif defined(__x86_64)
|
| +typedef MDRawContextAMD64 RawContextCPU;
|
| +
|
| +static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
|
| + const google_breakpad::ThreadInfo &info) {
|
| + out->context_flags = MD_CONTEXT_AMD64_FULL |
|
| + MD_CONTEXT_AMD64_SEGMENTS;
|
| +
|
| + out->cs = info.regs.cs;
|
| +
|
| + out->ds = info.regs.ds;
|
| + out->es = info.regs.es;
|
| + out->fs = info.regs.fs;
|
| + out->gs = info.regs.gs;
|
| +
|
| + out->ss = info.regs.ss;
|
| + out->eflags = info.regs.eflags;
|
| +
|
| + out->dr0 = info.dregs[0];
|
| + out->dr1 = info.dregs[1];
|
| + out->dr2 = info.dregs[2];
|
| + out->dr3 = info.dregs[3];
|
| + // 4 and 5 deliberatly omitted because they aren't included in the minidump
|
| + // format.
|
| + out->dr6 = info.dregs[6];
|
| + out->dr7 = info.dregs[7];
|
| +
|
| + out->rax = info.regs.rax;
|
| + out->rcx = info.regs.rcx;
|
| + out->rdx = info.regs.rdx;
|
| + out->rbx = info.regs.rbx;
|
| +
|
| + out->rsp = info.regs.rsp;
|
| +
|
| + out->rbp = info.regs.rbp;
|
| + out->rsi = info.regs.rsi;
|
| + out->rdi = info.regs.rdi;
|
| + out->r8 = info.regs.r8;
|
| + out->r9 = info.regs.r9;
|
| + out->r10 = info.regs.r10;
|
| + out->r11 = info.regs.r11;
|
| + out->r12 = info.regs.r12;
|
| + out->r13 = info.regs.r13;
|
| + out->r14 = info.regs.r14;
|
| + out->r15 = info.regs.r15;
|
| +
|
| + out->rip = info.regs.rip;
|
| +
|
| + out->flt_save.control_word = info.fpregs.cwd;
|
| + out->flt_save.status_word = info.fpregs.swd;
|
| + out->flt_save.tag_word = info.fpregs.twd;
|
| + out->flt_save.error_opcode = info.fpregs.fop;
|
| + out->flt_save.error_offset = info.fpregs.rip;
|
| + out->flt_save.error_selector = 0; // We don't have this.
|
| + out->flt_save.data_offset = info.fpregs.rdp;
|
| + out->flt_save.data_selector = 0; // We don't have this.
|
| + out->flt_save.mx_csr = info.fpregs.mxcsr;
|
| + out->flt_save.mx_csr_mask = info.fpregs.mxcsr_mask;
|
| + memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
|
| + memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
|
| +}
|
| +
|
| +static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc) {
|
| + const greg_t* regs = uc->gregs;
|
| + const fpregset_t fpregs = uc->fpregs;
|
| +
|
| + out->context_flags = MD_CONTEXT_AMD64_FULL;
|
| +
|
| + out->cs = regs[REG_CSGSFS] & 0xffff;
|
| +
|
| + out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
|
| + out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
|
| +
|
| + out->eflags = regs[REG_EFL];
|
| +
|
| + out->rax = regs[REG_RAX];
|
| + out->rcx = regs[REG_RCX];
|
| + out->rdx = regs[REG_RDX];
|
| + out->rbx = regs[REG_RBX];
|
| +
|
| + out->rsp = regs[REG_RSP];
|
| + out->rbp = regs[REG_RBP];
|
| + out->rsi = regs[REG_RSI];
|
| + out->rdi = regs[REG_RDI];
|
| + out->r8 = regs[REG_R8];
|
| + out->r9 = regs[REG_R9];
|
| + out->r10 = regs[REG_R10];
|
| + out->r11 = regs[REG_R11];
|
| + out->r12 = regs[REG_R12];
|
| + out->r13 = regs[REG_R13];
|
| + out->r14 = regs[REG_R14];
|
| + out->r15 = regs[REG_R15];
|
| +
|
| + out->rip = regs[REG_RIP];
|
| +
|
| + out->flt_save.control_word = fpregs->cwd;
|
| + out->flt_save.status_word = fpregs->swd;
|
| + out->flt_save.tag_word = fpregs->ftw;
|
| + out->flt_save.error_opcode = fpregs->fop;
|
| + out->flt_save.error_offset = fpregs->rip;
|
| + out->flt_save.data_offset = fpregs->rdp;
|
| + out->flt_save.error_selector = 0; // We don't have this.
|
| + out->flt_save.data_selector = 0; // We don't have this.
|
| + out->flt_save.mx_csr = fpregs->mxcsr;
|
| + out->flt_save.mx_csr_mask = fpregs->mxcsr_mask;
|
| + memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
|
| + memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
| +}
|
| +
|
| +#else
|
| +#error "This code has not been ported to your platform yet."
|
| +#endif
|
| +
|
| +namespace google_breakpad {
|
| +
|
| +class MinidumpWriter {
|
| + public:
|
| + MinidumpWriter(const char* filename,
|
| + pid_t crashing_pid,
|
| + const ExceptionHandler::CrashContext* context)
|
| + : filename_(filename),
|
| + siginfo_(&context->siginfo),
|
| + ucontext_(&context->context),
|
| + crashing_tid_(context->tid),
|
| + dumper_(crashing_pid) {
|
| + }
|
| +
|
| + bool Init() {
|
| + return dumper_.Init() && minidump_writer_.Open(filename_) &&
|
| + dumper_.ThreadsSuspend();
|
| + }
|
| +
|
| + ~MinidumpWriter() {
|
| + minidump_writer_.Close();
|
| + dumper_.ThreadsResume();
|
| + }
|
| +
|
| + bool Dump() {
|
| + // A minidump file contains a number of tagged streams. This is the number
|
| + // of stream which we write.
|
| + static const unsigned kNumWriters = 10;
|
| +
|
| + TypedMDRVA<MDRawHeader> header(&minidump_writer_);
|
| + TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
|
| + if (!header.Allocate())
|
| + return false;
|
| + if (!dir.AllocateArray(kNumWriters))
|
| + return false;
|
| + memset(header.get(), 0, sizeof(MDRawHeader));
|
| +
|
| + header.get()->signature = MD_HEADER_SIGNATURE;
|
| + header.get()->version = MD_HEADER_VERSION;
|
| + header.get()->time_date_stamp = time(NULL);
|
| + header.get()->stream_count = kNumWriters;
|
| + header.get()->stream_directory_rva = dir.position();
|
| +
|
| + unsigned dir_index = 0;
|
| + MDRawDirectory dirent;
|
| +
|
| + if (!WriteThreadListStream(&dirent))
|
| + return false;
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + if (!WriteMappings(&dirent))
|
| + return false;
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + if (!WriteExceptionStream(&dirent))
|
| + return false;
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_CPU_INFO;
|
| + if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_PROC_STATUS;
|
| + if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_LSB_RELEASE;
|
| + if (!WriteFile(&dirent.location, "/etc/lsb-release"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_CMD_LINE;
|
| + if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_ENVIRON;
|
| + if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_AUXV;
|
| + if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + dirent.stream_type = MD_LINUX_AUXV;
|
| + if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
|
| + NullifyDirectoryEntry(&dirent);
|
| + dir.CopyIndex(dir_index++, &dirent);
|
| +
|
| + // If you add more directory entries, don't forget to update kNumWriters,
|
| + // above.
|
| +
|
| + dumper_.ThreadsResume();
|
| + return true;
|
| + }
|
| +
|
| + // Write information about the threads.
|
| + bool WriteThreadListStream(MDRawDirectory* dirent) {
|
| + const unsigned num_threads = dumper_.threads().size();
|
| +
|
| + TypedMDRVA<uint32_t> list(&minidump_writer_);
|
| + if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
|
| + return false;
|
| +
|
| + dirent->stream_type = MD_THREAD_LIST_STREAM;
|
| + dirent->location = list.location();
|
| +
|
| + *list.get() = num_threads;
|
| +
|
| + for (unsigned i = 0; i < num_threads; ++i) {
|
| + MDRawThread thread;
|
| + my_memset(&thread, 0, sizeof(thread));
|
| + thread.thread_id = dumper_.threads()[i];
|
| + // We have a different source of information for the crashing thread. If
|
| + // we used the actual state of the thread we would find it running in the
|
| + // signal handler with the alternative stack, which would be deeply
|
| + // unhelpful.
|
| + if (thread.thread_id == crashing_tid_) {
|
| + const void* stack;
|
| + size_t stack_len;
|
| + if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
|
| + return false;
|
| + UntypedMDRVA memory(&minidump_writer_);
|
| + if (!memory.Allocate(stack_len))
|
| + return false;
|
| + uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
|
| + dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
|
| + memory.Copy(stack_copy, stack_len);
|
| + thread.stack.start_of_memory_range = (uintptr_t) (stack);
|
| + thread.stack.memory = memory.location();
|
| + TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
|
| + if (!cpu.Allocate())
|
| + return false;
|
| + my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
| + CPUFillFromUContext(cpu.get(), ucontext_);
|
| + thread.thread_context = cpu.location();
|
| + crashing_thread_context_ = cpu.location();
|
| + } else {
|
| + ThreadInfo info;
|
| + if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info))
|
| + return false;
|
| + UntypedMDRVA memory(&minidump_writer_);
|
| + if (!memory.Allocate(info.stack_len))
|
| + return false;
|
| + uint8_t* stack_copy =
|
| + (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
|
| + dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
|
| + info.stack_len);
|
| + memory.Copy(stack_copy, info.stack_len);
|
| + thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
|
| + thread.stack.memory = memory.location();
|
| + TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
|
| + if (!cpu.Allocate())
|
| + return false;
|
| + my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
| + CPUFillFromThreadInfo(cpu.get(), info);
|
| + thread.thread_context = cpu.location();
|
| + }
|
| +
|
| + list.CopyIndexAfterObject(i, &thread, sizeof(thread));
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + // Write information about the mappings in effect. Because we are using the
|
| + // minidump format, the information about the mappings is pretty limited.
|
| + // Because of this, we also include the full, unparsed, /proc/$x/maps file in
|
| + // another stream in the file.
|
| + bool WriteMappings(MDRawDirectory* dirent) {
|
| + const unsigned num_mappings = dumper_.mappings().size();
|
| +
|
| + TypedMDRVA<uint32_t> list(&minidump_writer_);
|
| + if (!list.AllocateObjectAndArray(num_mappings, sizeof(MDRawModule)))
|
| + return false;
|
| +
|
| + dirent->stream_type = MD_MODULE_LIST_STREAM;
|
| + dirent->location = list.location();
|
| + *list.get() = num_mappings;
|
| +
|
| + for (unsigned i = 0; i < num_mappings; ++i) {
|
| + const MappingInfo& mapping = *dumper_.mappings()[i];
|
| + MDRawModule mod;
|
| + my_memset(&mod, 0, sizeof(mod));
|
| + mod.base_of_image = mapping.start_addr;
|
| + mod.size_of_image = mapping.size;
|
| + UntypedMDRVA memory(&minidump_writer_);
|
| + const size_t filename_len = my_strlen(mapping.name);
|
| +
|
| + if (filename_len) {
|
| + MDLocationDescriptor ld;
|
| + if (!minidump_writer_.WriteString(mapping.name, filename_len, &ld))
|
| + return false;
|
| + mod.module_name_rva = ld.rva;
|
| + }
|
| +
|
| + list.CopyIndexAfterObject(i, &mod, sizeof(mod));
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + bool WriteExceptionStream(MDRawDirectory* dirent) {
|
| + TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
|
| + if (!exc.Allocate())
|
| + return false;
|
| + my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
|
| +
|
| + dirent->stream_type = MD_EXCEPTION_STREAM;
|
| + dirent->location = exc.location();
|
| +
|
| + exc.get()->thread_id = crashing_tid_;
|
| + exc.get()->exception_record.exception_code = siginfo_->si_signo;
|
| + exc.get()->exception_record.exception_address =
|
| + (uintptr_t) siginfo_->si_addr;
|
| + exc.get()->thread_context = crashing_thread_context_;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| +#if defined(__i386)
|
| + uintptr_t GetStackPointer() {
|
| + return ucontext_->uc_mcontext.gregs[REG_ESP];
|
| + }
|
| +#elif defined(__x86_64)
|
| + uintptr_t GetStackPointer() {
|
| + return ucontext_->uc_mcontext.gregs[REG_RSP];
|
| + }
|
| +#else
|
| +#error "This code has not been ported to your platform yet."
|
| +#endif
|
| +
|
| + void NullifyDirectoryEntry(MDRawDirectory* dirent) {
|
| + dirent->stream_type = 0;
|
| + dirent->location.data_size = 0;
|
| + dirent->location.rva = 0;
|
| + }
|
| +
|
| + bool WriteFile(MDLocationDescriptor* result, const char* filename) {
|
| + const int fd = sys_open(filename, O_RDONLY, 0);
|
| + if (fd < 0)
|
| + return false;
|
| +
|
| + // 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
|
| + // to read as much as we can into a buffer.
|
| + static const unsigned kMaxFileSize = 1024;
|
| + uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
|
| +
|
| + size_t done = 0;
|
| + while (done < kMaxFileSize) {
|
| + ssize_t r;
|
| + do {
|
| + r = sys_read(fd, data + done, kMaxFileSize - done);
|
| + } while (r == -1 && errno == EINTR);
|
| +
|
| + if (r < 1)
|
| + break;
|
| + done += r;
|
| + }
|
| + sys_close(fd);
|
| +
|
| + if (!done)
|
| + return false;
|
| +
|
| + UntypedMDRVA memory(&minidump_writer_);
|
| + if (!memory.Allocate(done))
|
| + return false;
|
| + memory.Copy(data, done);
|
| + *result = memory.location();
|
| + return true;
|
| + }
|
| +
|
| + bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
|
| + const char* filename) {
|
| + char buf[80];
|
| + memcpy(buf, "/proc/", 6);
|
| + const unsigned pid_len = my_int_len(pid);
|
| + my_itos(buf + 6, pid, pid_len);
|
| + buf[6 + pid_len] = '/';
|
| + memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
|
| + return WriteFile(result, buf);
|
| + }
|
| +
|
| + const char* const filename_; // output filename
|
| + const siginfo_t* const siginfo_; // from the signal handler (see sigaction)
|
| + const struct ucontext* const ucontext_; // also from the signal handler
|
| + const pid_t crashing_tid_; // the process which actually crashed
|
| + LinuxDumper dumper_;
|
| + MinidumpFileWriter minidump_writer_;
|
| + MDLocationDescriptor crashing_thread_context_;
|
| +};
|
| +
|
| +bool WriteMinidump(const char* filename, pid_t crashing_process,
|
| + const void* blob, size_t blob_size) {
|
| + if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
| + return false;
|
| + const ExceptionHandler::CrashContext* context =
|
| + reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
| + MinidumpWriter writer(filename, crashing_process, context);
|
| + if (!writer.Init())
|
| + return false;
|
| + return writer.Dump();
|
| +}
|
| +
|
| +} // namespace google_breakpad
|
|
|