| 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 | 
|  |