LCOV - code coverage report
Current view: directory - toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer - minidump_writer.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 584 0 0.0 %
Date: 2012-06-02 Functions: 33 0 0.0 %

       1                 : // Copyright (c) 2009, Google Inc.
       2                 : // All rights reserved.
       3                 : //
       4                 : // Redistribution and use in source and binary forms, with or without
       5                 : // modification, are permitted provided that the following conditions are
       6                 : // met:
       7                 : //
       8                 : //     * Redistributions of source code must retain the above copyright
       9                 : // notice, this list of conditions and the following disclaimer.
      10                 : //     * Redistributions in binary form must reproduce the above
      11                 : // copyright notice, this list of conditions and the following disclaimer
      12                 : // in the documentation and/or other materials provided with the
      13                 : // distribution.
      14                 : //     * Neither the name of Google Inc. nor the names of its
      15                 : // contributors may be used to endorse or promote products derived from
      16                 : // this software without specific prior written permission.
      17                 : //
      18                 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      19                 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      20                 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      21                 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      22                 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      23                 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      24                 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      25                 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      26                 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      27                 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      28                 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      29                 : 
      30                 : // This code writes out minidump files:
      31                 : //   http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
      32                 : //
      33                 : // Minidumps are a Microsoft format which Breakpad uses for recording crash
      34                 : // dumps. This code has to run in a compromised environment (the address space
      35                 : // may have received SIGSEGV), thus the following rules apply:
      36                 : //   * You may not enter the dynamic linker. This means that we cannot call
      37                 : //     any symbols in a shared library (inc libc). Because of this we replace
      38                 : //     libc functions in linux_libc_support.h.
      39                 : //   * You may not call syscalls via the libc wrappers. This rule is a subset
      40                 : //     of the first rule but it bears repeating. We have direct wrappers
      41                 : //     around the system calls in linux_syscall_support.h.
      42                 : //   * You may not malloc. There's an alternative allocator in memory.h and
      43                 : //     a canonical instance in the LinuxDumper object. We use the placement
      44                 : //     new form to allocate objects and we don't delete them.
      45                 : 
      46                 : #include "client/linux/minidump_writer/minidump_writer.h"
      47                 : #include "client/minidump_file_writer-inl.h"
      48                 : 
      49                 : #include <algorithm>
      50                 : 
      51                 : #include <errno.h>
      52                 : #include <fcntl.h>
      53                 : #if defined(__ANDROID__)
      54                 : #include "client/linux/android_link.h"
      55                 : #else
      56                 : #include <link.h>
      57                 : #endif
      58                 : #include <stdio.h>
      59                 : #include <unistd.h>
      60                 : #include <ctype.h>
      61                 : #if !defined(__ANDROID__)
      62                 : #include <sys/ucontext.h>
      63                 : #include <sys/user.h>
      64                 : #endif
      65                 : #include <sys/utsname.h>
      66                 : 
      67                 : #include "client/minidump_file_writer.h"
      68                 : #include "google_breakpad/common/minidump_format.h"
      69                 : #include "google_breakpad/common/minidump_cpu_amd64.h"
      70                 : #include "google_breakpad/common/minidump_cpu_x86.h"
      71                 : 
      72                 : #include "client/linux/android_ucontext.h"
      73                 : #include "client/linux/handler/exception_handler.h"
      74                 : #include "client/linux/minidump_writer/line_reader.h"
      75                 : #include "client/linux/minidump_writer/linux_dumper.h"
      76                 : #include "client/linux/minidump_writer/minidump_extension_linux.h"
      77                 : #include "common/linux/linux_libc_support.h"
      78                 : #include "common/linux/linux_syscall_support.h"
      79                 : 
      80                 : using google_breakpad::ThreadInfo;
      81                 : 
      82                 : // Minidump defines register structures which are different from the raw
      83                 : // structures which we get from the kernel. These are platform specific
      84                 : // functions to juggle the ucontext and user structures into minidump format.
      85                 : #if defined(__i386)
      86                 : typedef MDRawContextX86 RawContextCPU;
      87                 : 
      88                 : // Write a uint16_t to memory
      89                 : //   out: memory location to write to
      90                 : //   v: value to write.
      91               0 : static void U16(void* out, uint16_t v) {
      92               0 :   memcpy(out, &v, sizeof(v));
      93               0 : }
      94                 : 
      95                 : // Write a uint32_t to memory
      96                 : //   out: memory location to write to
      97                 : //   v: value to write.
      98               0 : static void U32(void* out, uint32_t v) {
      99               0 :   memcpy(out, &v, sizeof(v));
     100               0 : }
     101                 : 
     102                 : // Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
     103                 : //   out: the minidump structure
     104                 : //   info: the collection of register structures.
     105               0 : static void CPUFillFromThreadInfo(MDRawContextX86 *out,
     106                 :                                   const ThreadInfo &info) {
     107               0 :   out->context_flags = MD_CONTEXT_X86_ALL;
     108                 : 
     109               0 :   out->dr0 = info.dregs[0];
     110               0 :   out->dr1 = info.dregs[1];
     111               0 :   out->dr2 = info.dregs[2];
     112               0 :   out->dr3 = info.dregs[3];
     113                 :   // 4 and 5 deliberatly omitted because they aren't included in the minidump
     114                 :   // format.
     115               0 :   out->dr6 = info.dregs[6];
     116               0 :   out->dr7 = info.dregs[7];
     117                 : 
     118               0 :   out->gs = info.regs.xgs;
     119               0 :   out->fs = info.regs.xfs;
     120               0 :   out->es = info.regs.xes;
     121               0 :   out->ds = info.regs.xds;
     122                 : 
     123               0 :   out->edi = info.regs.edi;
     124               0 :   out->esi = info.regs.esi;
     125               0 :   out->ebx = info.regs.ebx;
     126               0 :   out->edx = info.regs.edx;
     127               0 :   out->ecx = info.regs.ecx;
     128               0 :   out->eax = info.regs.eax;
     129                 : 
     130               0 :   out->ebp = info.regs.ebp;
     131               0 :   out->eip = info.regs.eip;
     132               0 :   out->cs = info.regs.xcs;
     133               0 :   out->eflags = info.regs.eflags;
     134               0 :   out->esp = info.regs.esp;
     135               0 :   out->ss = info.regs.xss;
     136                 : 
     137               0 :   out->float_save.control_word = info.fpregs.cwd;
     138               0 :   out->float_save.status_word = info.fpregs.swd;
     139               0 :   out->float_save.tag_word = info.fpregs.twd;
     140               0 :   out->float_save.error_offset = info.fpregs.fip;
     141               0 :   out->float_save.error_selector = info.fpregs.fcs;
     142               0 :   out->float_save.data_offset = info.fpregs.foo;
     143               0 :   out->float_save.data_selector = info.fpregs.fos;
     144                 : 
     145                 :   // 8 registers * 10 bytes per register.
     146               0 :   memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
     147                 : 
     148                 :   // This matches the Intel fpsave format.
     149               0 :   U16(out->extended_registers + 0, info.fpregs.cwd);
     150               0 :   U16(out->extended_registers + 2, info.fpregs.swd);
     151               0 :   U16(out->extended_registers + 4, info.fpregs.twd);
     152               0 :   U16(out->extended_registers + 6, info.fpxregs.fop);
     153               0 :   U32(out->extended_registers + 8, info.fpxregs.fip);
     154               0 :   U16(out->extended_registers + 12, info.fpxregs.fcs);
     155               0 :   U32(out->extended_registers + 16, info.fpregs.foo);
     156               0 :   U16(out->extended_registers + 20, info.fpregs.fos);
     157               0 :   U32(out->extended_registers + 24, info.fpxregs.mxcsr);
     158                 : 
     159               0 :   memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
     160               0 :   memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
     161               0 : }
     162                 : 
     163                 : // Juggle an x86 ucontext into minidump format
     164                 : //   out: the minidump structure
     165                 : //   info: the collection of register structures.
     166               0 : static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc,
     167                 :                                 const struct _libc_fpstate* fp) {
     168               0 :   const greg_t* regs = uc->uc_mcontext.gregs;
     169                 : 
     170                 :   out->context_flags = MD_CONTEXT_X86_FULL |
     171               0 :                        MD_CONTEXT_X86_FLOATING_POINT;
     172                 : 
     173               0 :   out->gs = regs[REG_GS];
     174               0 :   out->fs = regs[REG_FS];
     175               0 :   out->es = regs[REG_ES];
     176               0 :   out->ds = regs[REG_DS];
     177                 : 
     178               0 :   out->edi = regs[REG_EDI];
     179               0 :   out->esi = regs[REG_ESI];
     180               0 :   out->ebx = regs[REG_EBX];
     181               0 :   out->edx = regs[REG_EDX];
     182               0 :   out->ecx = regs[REG_ECX];
     183               0 :   out->eax = regs[REG_EAX];
     184                 : 
     185               0 :   out->ebp = regs[REG_EBP];
     186               0 :   out->eip = regs[REG_EIP];
     187               0 :   out->cs = regs[REG_CS];
     188               0 :   out->eflags = regs[REG_EFL];
     189               0 :   out->esp = regs[REG_UESP];
     190               0 :   out->ss = regs[REG_SS];
     191                 : 
     192               0 :   out->float_save.control_word = fp->cw;
     193               0 :   out->float_save.status_word = fp->sw;
     194               0 :   out->float_save.tag_word = fp->tag;
     195               0 :   out->float_save.error_offset = fp->ipoff;
     196               0 :   out->float_save.error_selector = fp->cssel;
     197               0 :   out->float_save.data_offset = fp->dataoff;
     198               0 :   out->float_save.data_selector = fp->datasel;
     199                 : 
     200                 :   // 8 registers * 10 bytes per register.
     201               0 :   memcpy(out->float_save.register_area, fp->_st, 10 * 8);
     202               0 : }
     203                 : 
     204               0 : static uintptr_t InstructionPointer(const ThreadInfo& info) {
     205               0 :   return info.regs.eip;
     206                 : }
     207                 : 
     208               0 : static uintptr_t StackPointer(const ThreadInfo& info) {
     209               0 :   return info.regs.esp;
     210                 : }
     211                 : 
     212               0 : static uintptr_t StackPointer(const ucontext* uc) {
     213               0 :   return uc->uc_mcontext.gregs[REG_ESP];
     214                 : }
     215                 : 
     216                 : #elif defined(__x86_64)
     217                 : typedef MDRawContextAMD64 RawContextCPU;
     218                 : 
     219                 : static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
     220                 :                                   const ThreadInfo &info) {
     221                 :   out->context_flags = MD_CONTEXT_AMD64_FULL |
     222                 :                        MD_CONTEXT_AMD64_SEGMENTS;
     223                 : 
     224                 :   out->cs = info.regs.cs;
     225                 : 
     226                 :   out->ds = info.regs.ds;
     227                 :   out->es = info.regs.es;
     228                 :   out->fs = info.regs.fs;
     229                 :   out->gs = info.regs.gs;
     230                 : 
     231                 :   out->ss = info.regs.ss;
     232                 :   out->eflags = info.regs.eflags;
     233                 : 
     234                 :   out->dr0 = info.dregs[0];
     235                 :   out->dr1 = info.dregs[1];
     236                 :   out->dr2 = info.dregs[2];
     237                 :   out->dr3 = info.dregs[3];
     238                 :   // 4 and 5 deliberatly omitted because they aren't included in the minidump
     239                 :   // format.
     240                 :   out->dr6 = info.dregs[6];
     241                 :   out->dr7 = info.dregs[7];
     242                 : 
     243                 :   out->rax = info.regs.rax;
     244                 :   out->rcx = info.regs.rcx;
     245                 :   out->rdx = info.regs.rdx;
     246                 :   out->rbx = info.regs.rbx;
     247                 : 
     248                 :   out->rsp = info.regs.rsp;
     249                 : 
     250                 :   out->rbp = info.regs.rbp;
     251                 :   out->rsi = info.regs.rsi;
     252                 :   out->rdi = info.regs.rdi;
     253                 :   out->r8 = info.regs.r8;
     254                 :   out->r9 = info.regs.r9;
     255                 :   out->r10 = info.regs.r10;
     256                 :   out->r11 = info.regs.r11;
     257                 :   out->r12 = info.regs.r12;
     258                 :   out->r13 = info.regs.r13;
     259                 :   out->r14 = info.regs.r14;
     260                 :   out->r15 = info.regs.r15;
     261                 : 
     262                 :   out->rip = info.regs.rip;
     263                 : 
     264                 :   out->flt_save.control_word = info.fpregs.cwd;
     265                 :   out->flt_save.status_word = info.fpregs.swd;
     266                 :   out->flt_save.tag_word = info.fpregs.ftw;
     267                 :   out->flt_save.error_opcode = info.fpregs.fop;
     268                 :   out->flt_save.error_offset = info.fpregs.rip;
     269                 :   out->flt_save.error_selector = 0; // We don't have this.
     270                 :   out->flt_save.data_offset = info.fpregs.rdp;
     271                 :   out->flt_save.data_selector = 0;  // We don't have this.
     272                 :   out->flt_save.mx_csr = info.fpregs.mxcsr;
     273                 :   out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
     274                 :   memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
     275                 :   memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
     276                 : }
     277                 : 
     278                 : static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
     279                 :                                 const struct _libc_fpstate* fpregs) {
     280                 :   const greg_t* regs = uc->uc_mcontext.gregs;
     281                 : 
     282                 :   out->context_flags = MD_CONTEXT_AMD64_FULL;
     283                 : 
     284                 :   out->cs = regs[REG_CSGSFS] & 0xffff;
     285                 : 
     286                 :   out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
     287                 :   out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
     288                 : 
     289                 :   out->eflags = regs[REG_EFL];
     290                 : 
     291                 :   out->rax = regs[REG_RAX];
     292                 :   out->rcx = regs[REG_RCX];
     293                 :   out->rdx = regs[REG_RDX];
     294                 :   out->rbx = regs[REG_RBX];
     295                 : 
     296                 :   out->rsp = regs[REG_RSP];
     297                 :   out->rbp = regs[REG_RBP];
     298                 :   out->rsi = regs[REG_RSI];
     299                 :   out->rdi = regs[REG_RDI];
     300                 :   out->r8 = regs[REG_R8];
     301                 :   out->r9 = regs[REG_R9];
     302                 :   out->r10 = regs[REG_R10];
     303                 :   out->r11 = regs[REG_R11];
     304                 :   out->r12 = regs[REG_R12];
     305                 :   out->r13 = regs[REG_R13];
     306                 :   out->r14 = regs[REG_R14];
     307                 :   out->r15 = regs[REG_R15];
     308                 : 
     309                 :   out->rip = regs[REG_RIP];
     310                 : 
     311                 :   out->flt_save.control_word = fpregs->cwd;
     312                 :   out->flt_save.status_word = fpregs->swd;
     313                 :   out->flt_save.tag_word = fpregs->ftw;
     314                 :   out->flt_save.error_opcode = fpregs->fop;
     315                 :   out->flt_save.error_offset = fpregs->rip;
     316                 :   out->flt_save.data_offset = fpregs->rdp;
     317                 :   out->flt_save.error_selector = 0; // We don't have this.
     318                 :   out->flt_save.data_selector = 0;  // We don't have this.
     319                 :   out->flt_save.mx_csr = fpregs->mxcsr;
     320                 :   out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
     321                 :   memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
     322                 :   memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
     323                 : }
     324                 : 
     325                 : static uintptr_t InstructionPointer(const ThreadInfo& info) {
     326                 :   return info.regs.rip;
     327                 : }
     328                 : 
     329                 : static uintptr_t StackPointer(const ThreadInfo& info) {
     330                 :   return info.regs.rsp;
     331                 : }
     332                 : 
     333                 : static uintptr_t StackPointer(const ucontext* uc) {
     334                 :   return uc->uc_mcontext.gregs[REG_RSP];
     335                 : }
     336                 : 
     337                 : #elif defined(__ARMEL__)
     338                 : typedef MDRawContextARM RawContextCPU;
     339                 : 
     340                 : static void CPUFillFromThreadInfo(MDRawContextARM *out,
     341                 :                                   const ThreadInfo &info) {
     342                 :   out->context_flags = MD_CONTEXT_ARM_FULL;
     343                 : 
     344                 :   for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
     345                 :     out->iregs[i] = info.regs.uregs[i];
     346                 :   // No CPSR register in ThreadInfo(it's not accessible via ptrace)
     347                 :   out->cpsr = 0;
     348                 : #if !defined(__ANDROID__)
     349                 :   out->float_save.fpscr = info.fpregs.fpsr |
     350                 :     (static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
     351                 :   //TODO: sort this out, actually collect floating point registers
     352                 :   memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
     353                 :   memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
     354                 : #endif
     355                 : }
     356                 : 
     357                 : static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
     358                 :                                 const struct _libc_fpstate* fpregs) {
     359                 :   out->context_flags = MD_CONTEXT_ARM_FULL;
     360                 : 
     361                 :   out->iregs[0] = uc->uc_mcontext.arm_r0;
     362                 :   out->iregs[1] = uc->uc_mcontext.arm_r1;
     363                 :   out->iregs[2] = uc->uc_mcontext.arm_r2;
     364                 :   out->iregs[3] = uc->uc_mcontext.arm_r3;
     365                 :   out->iregs[4] = uc->uc_mcontext.arm_r4;
     366                 :   out->iregs[5] = uc->uc_mcontext.arm_r5;
     367                 :   out->iregs[6] = uc->uc_mcontext.arm_r6;
     368                 :   out->iregs[7] = uc->uc_mcontext.arm_r7;
     369                 :   out->iregs[8] = uc->uc_mcontext.arm_r8;
     370                 :   out->iregs[9] = uc->uc_mcontext.arm_r9;
     371                 :   out->iregs[10] = uc->uc_mcontext.arm_r10;
     372                 : 
     373                 :   out->iregs[11] = uc->uc_mcontext.arm_fp;
     374                 :   out->iregs[12] = uc->uc_mcontext.arm_ip;
     375                 :   out->iregs[13] = uc->uc_mcontext.arm_sp;
     376                 :   out->iregs[14] = uc->uc_mcontext.arm_lr;
     377                 :   out->iregs[15] = uc->uc_mcontext.arm_pc;
     378                 : 
     379                 :   out->cpsr = uc->uc_mcontext.arm_cpsr;
     380                 : 
     381                 :   //TODO: fix this after fixing ExceptionHandler
     382                 :   out->float_save.fpscr = 0;
     383                 :   memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
     384                 :   memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
     385                 : }
     386                 : 
     387                 : static uintptr_t InstructionPointer(const ThreadInfo& info) {
     388                 :   return info.regs.ARM_ip;
     389                 : }
     390                 : 
     391                 : static uintptr_t StackPointer(const ThreadInfo& info) {
     392                 :   return info.regs.ARM_sp;
     393                 : }
     394                 : 
     395                 : static uintptr_t StackPointer(const ucontext* uc) {
     396                 :   return uc->uc_mcontext.arm_sp;
     397                 : }
     398                 : 
     399                 : #else
     400                 : #error "This code has not been ported to your platform yet."
     401                 : #endif
     402                 : 
     403                 : namespace google_breakpad {
     404                 : 
     405                 : // There are two uses of MinidumpWriter: 
     406                 : //
     407                 : //  (1) dumping a process in which a thread has crashed and is blocked
     408                 : //      in a signal handler
     409                 : //  (2) dumping a live process
     410                 : //
     411                 : // In case (1), we get the ucontext and fpstate of the crashing_tid_
     412                 : // from the signal handler.  In case (2), we have to extract it using
     413                 : // ptrace, and we can't assume that crashing_tid_ still exists (or
     414                 : // ever did).
     415                 : class MinidumpWriter {
     416                 :  public:
     417                 :   // case (1) above
     418               0 :   MinidumpWriter(const char* filename,
     419                 :                  pid_t crashing_pid,
     420                 :                  const ExceptionHandler::CrashContext* context,
     421                 :                  const MappingList& mappings,
     422                 :                  const AppMemoryList& appmem)
     423                 :       : filename_(filename),
     424                 :         siginfo_(&context->siginfo),
     425                 :         ucontext_(&context->context),
     426                 : #if !defined(__ARM_EABI__)
     427                 :         float_state_(&context->float_state),
     428                 : #else
     429                 :         //TODO: fix this after fixing ExceptionHandler
     430                 :         float_state_(NULL),
     431                 : #endif
     432                 :         crashing_tid_(context->tid),
     433                 :         crashing_tid_pc_(0),
     434                 :         dumper_(crashing_pid),
     435                 :         memory_blocks_(dumper_.allocator()),
     436                 :         mapping_info_(mappings),
     437               0 :         app_memory_info_(appmem) {
     438               0 :   }
     439                 : 
     440                 :   // case (2) above
     441               0 :   MinidumpWriter(const char* filename,
     442                 :                  pid_t pid,
     443                 :                  pid_t blame_thread,
     444                 :                  const MappingList& mappings,
     445                 :                  const AppMemoryList& appmem)
     446                 :       : filename_(filename),
     447                 :         siginfo_(NULL),         // we fill this in if we find blame_thread
     448                 :         ucontext_(NULL),
     449                 :         float_state_(NULL),
     450                 :         crashing_tid_(blame_thread),
     451                 :         crashing_tid_pc_(0),    // set if we find blame_thread
     452                 :         dumper_(pid),
     453                 :         memory_blocks_(dumper_.allocator()),
     454                 :         mapping_info_(mappings),
     455               0 :         app_memory_info_(appmem) {
     456               0 :   }
     457                 : 
     458               0 :   bool Init() {
     459               0 :     return dumper_.Init() && minidump_writer_.Open(filename_) &&
     460               0 :            dumper_.ThreadsAttach();
     461                 :   }
     462                 : 
     463               0 :   ~MinidumpWriter() {
     464               0 :     minidump_writer_.Close();
     465               0 :     dumper_.ThreadsDetach();
     466               0 :   }
     467                 : 
     468               0 :   bool HaveCrashedThread() const {
     469               0 :     return ucontext_ != NULL;
     470                 :   }
     471                 : 
     472               0 :   bool Dump() {
     473                 :     // The dynamic linker makes information available that helps gdb find all
     474                 :     // DSOs loaded into the program. If we can access this information, we dump
     475                 :     // it to a MD_LINUX_DSO_DEBUG stream.
     476               0 :     struct r_debug* r_debug = NULL;
     477               0 :     uint32_t dynamic_length = 0;
     478                 : #if !defined(__ANDROID__)
     479               0 :     for (int i = 0;;) {
     480                 :       ElfW(Dyn) dyn;
     481               0 :       dynamic_length += sizeof(dyn);
     482               0 :       dumper_.CopyFromProcess(&dyn, crashing_tid_, _DYNAMIC+i++, sizeof(dyn));
     483               0 :       if (dyn.d_tag == DT_DEBUG) {
     484               0 :         r_debug = (struct r_debug*)dyn.d_un.d_ptr;
     485               0 :         continue;
     486               0 :       } else if (dyn.d_tag == DT_NULL)
     487                 :         break;
     488                 :     }
     489                 : #endif
     490                 : 
     491                 :     // A minidump file contains a number of tagged streams. This is the number
     492                 :     // of stream which we write.
     493               0 :     unsigned kNumWriters = 12;
     494               0 :     if (r_debug)
     495               0 :       ++kNumWriters;
     496                 : 
     497               0 :     TypedMDRVA<MDRawHeader> header(&minidump_writer_);
     498               0 :     TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
     499               0 :     if (!header.Allocate())
     500               0 :       return false;
     501               0 :     if (!dir.AllocateArray(kNumWriters))
     502               0 :       return false;
     503               0 :     memset(header.get(), 0, sizeof(MDRawHeader));
     504                 : 
     505               0 :     header.get()->signature = MD_HEADER_SIGNATURE;
     506               0 :     header.get()->version = MD_HEADER_VERSION;
     507               0 :     header.get()->time_date_stamp = time(NULL);
     508               0 :     header.get()->stream_count = kNumWriters;
     509               0 :     header.get()->stream_directory_rva = dir.position();
     510                 : 
     511               0 :     unsigned dir_index = 0;
     512                 :     MDRawDirectory dirent;
     513                 : 
     514               0 :     if (!WriteThreadListStream(&dirent))
     515               0 :       return false;
     516               0 :     dir.CopyIndex(dir_index++, &dirent);
     517                 : 
     518               0 :     if (!WriteMappings(&dirent))
     519               0 :       return false;
     520               0 :     dir.CopyIndex(dir_index++, &dirent);
     521                 : 
     522               0 :     if (!WriteAppMemory())
     523               0 :       return false;
     524                 : 
     525               0 :     if (!WriteMemoryListStream(&dirent))
     526               0 :       return false;
     527               0 :     dir.CopyIndex(dir_index++, &dirent);
     528                 : 
     529               0 :     if (siginfo_ || crashing_tid_pc_) {
     530               0 :       if (!WriteExceptionStream(&dirent))
     531               0 :         return false;
     532               0 :       dir.CopyIndex(dir_index++, &dirent);
     533                 :     }
     534                 : 
     535               0 :     if (!WriteSystemInfoStream(&dirent))
     536               0 :       return false;
     537               0 :     dir.CopyIndex(dir_index++, &dirent);
     538                 : 
     539               0 :     dirent.stream_type = MD_LINUX_CPU_INFO;
     540               0 :     if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
     541               0 :       NullifyDirectoryEntry(&dirent);
     542               0 :     dir.CopyIndex(dir_index++, &dirent);
     543                 : 
     544               0 :     dirent.stream_type = MD_LINUX_PROC_STATUS;
     545               0 :     if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
     546               0 :       NullifyDirectoryEntry(&dirent);
     547               0 :     dir.CopyIndex(dir_index++, &dirent);
     548                 : 
     549               0 :     dirent.stream_type = MD_LINUX_LSB_RELEASE;
     550               0 :     if (!WriteFile(&dirent.location, "/etc/lsb-release"))
     551               0 :       NullifyDirectoryEntry(&dirent);
     552               0 :     dir.CopyIndex(dir_index++, &dirent);
     553                 : 
     554               0 :     dirent.stream_type = MD_LINUX_CMD_LINE;
     555               0 :     if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
     556               0 :       NullifyDirectoryEntry(&dirent);
     557               0 :     dir.CopyIndex(dir_index++, &dirent);
     558                 : 
     559               0 :     dirent.stream_type = MD_LINUX_ENVIRON;
     560               0 :     if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
     561               0 :       NullifyDirectoryEntry(&dirent);
     562               0 :     dir.CopyIndex(dir_index++, &dirent);
     563                 : 
     564               0 :     dirent.stream_type = MD_LINUX_AUXV;
     565               0 :     if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
     566               0 :       NullifyDirectoryEntry(&dirent);
     567               0 :     dir.CopyIndex(dir_index++, &dirent);
     568                 : 
     569               0 :     dirent.stream_type = MD_LINUX_MAPS;
     570               0 :     if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
     571               0 :       NullifyDirectoryEntry(&dirent);
     572               0 :     dir.CopyIndex(dir_index++, &dirent);
     573                 : 
     574               0 :     if (r_debug) {
     575               0 :       dirent.stream_type = MD_LINUX_DSO_DEBUG;
     576               0 :       if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length))
     577               0 :         NullifyDirectoryEntry(&dirent);
     578               0 :       dir.CopyIndex(dir_index++, &dirent);
     579                 :     }
     580                 : 
     581                 :     // If you add more directory entries, don't forget to update kNumWriters,
     582                 :     // above.
     583                 : 
     584               0 :     dumper_.ThreadsDetach();
     585               0 :     return true;
     586                 :   }
     587                 : 
     588                 :   // Check if the top of the stack is part of a system call that has been
     589                 :   // redirected by the seccomp sandbox. If so, try to pop the stack frames
     590                 :   // all the way back to the point where the interception happened.
     591               0 :   void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
     592                 :                             uint8_t* stack_copy) {
     593                 : #if defined(__x86_64)
     594                 :     u_int64_t bp = cpu->rbp;
     595                 :     u_int64_t top = thread.stack.start_of_memory_range;
     596                 :     for (int i = 4; i--; ) {
     597                 :       if (bp < top ||
     598                 :           bp + sizeof(bp) > thread.stack.start_of_memory_range +
     599                 :           thread.stack.memory.data_size ||
     600                 :           bp & 1) {
     601                 :         break;
     602                 :       }
     603                 :       uint64_t old_top = top;
     604                 :       top = bp;
     605                 :       u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
     606                 :       memcpy(&bp, bp_addr, sizeof(bp));
     607                 :       if (bp == 0xDEADBEEFDEADBEEFull) {
     608                 :         struct {
     609                 :           uint64_t r15;
     610                 :           uint64_t r14;
     611                 :           uint64_t r13;
     612                 :           uint64_t r12;
     613                 :           uint64_t r11;
     614                 :           uint64_t r10;
     615                 :           uint64_t r9;
     616                 :           uint64_t r8;
     617                 :           uint64_t rdi;
     618                 :           uint64_t rsi;
     619                 :           uint64_t rdx;
     620                 :           uint64_t rcx;
     621                 :           uint64_t rbx;
     622                 :           uint64_t deadbeef;
     623                 :           uint64_t rbp;
     624                 :           uint64_t fakeret;
     625                 :           uint64_t ret;
     626                 :           /* char redzone[128]; */
     627                 :         } seccomp_stackframe;
     628                 :         if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
     629                 :             top - offsetof(typeof(seccomp_stackframe), deadbeef) +
     630                 :             sizeof(seccomp_stackframe) >
     631                 :             thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
     632                 :           break;
     633                 :         }
     634                 :         memcpy(&seccomp_stackframe,
     635                 :                bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
     636                 :                sizeof(seccomp_stackframe));
     637                 :         cpu->rbx = seccomp_stackframe.rbx;
     638                 :         cpu->rcx = seccomp_stackframe.rcx;
     639                 :         cpu->rdx = seccomp_stackframe.rdx;
     640                 :         cpu->rsi = seccomp_stackframe.rsi;
     641                 :         cpu->rdi = seccomp_stackframe.rdi;
     642                 :         cpu->rbp = seccomp_stackframe.rbp;
     643                 :         cpu->rsp = top + 4*sizeof(uint64_t) + 128;
     644                 :         cpu->r8  = seccomp_stackframe.r8;
     645                 :         cpu->r9  = seccomp_stackframe.r9;
     646                 :         cpu->r10 = seccomp_stackframe.r10;
     647                 :         cpu->r11 = seccomp_stackframe.r11;
     648                 :         cpu->r12 = seccomp_stackframe.r12;
     649                 :         cpu->r13 = seccomp_stackframe.r13;
     650                 :         cpu->r14 = seccomp_stackframe.r14;
     651                 :         cpu->r15 = seccomp_stackframe.r15;
     652                 :         cpu->rip = seccomp_stackframe.fakeret;
     653                 :         return;
     654                 :       }
     655                 :     }
     656                 : #elif defined(__i386)
     657               0 :     u_int32_t bp = cpu->ebp;
     658               0 :     u_int32_t top = thread.stack.start_of_memory_range;
     659               0 :     for (int i = 4; i--; ) {
     660               0 :       if (bp < top ||
     661                 :           bp + sizeof(bp) > thread.stack.start_of_memory_range +
     662                 :           thread.stack.memory.data_size ||
     663                 :           bp & 1) {
     664               0 :         break;
     665                 :       }
     666               0 :       uint32_t old_top = top;
     667               0 :       top = bp;
     668               0 :       u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
     669               0 :       memcpy(&bp, bp_addr, sizeof(bp));
     670               0 :       if (bp == 0xDEADBEEFu) {
     671                 :         struct {
     672                 :           uint32_t edi;
     673                 :           uint32_t esi;
     674                 :           uint32_t edx;
     675                 :           uint32_t ecx;
     676                 :           uint32_t ebx;
     677                 :           uint32_t deadbeef;
     678                 :           uint32_t ebp;
     679                 :           uint32_t fakeret;
     680                 :           uint32_t ret;
     681                 :         } seccomp_stackframe;
     682               0 :         if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
     683                 :             top - offsetof(typeof(seccomp_stackframe), deadbeef) +
     684                 :             sizeof(seccomp_stackframe) >
     685                 :             thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
     686               0 :           break;
     687                 :         }
     688                 :         memcpy(&seccomp_stackframe,
     689                 :                bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
     690               0 :                sizeof(seccomp_stackframe));
     691               0 :         cpu->ebx = seccomp_stackframe.ebx;
     692               0 :         cpu->ecx = seccomp_stackframe.ecx;
     693               0 :         cpu->edx = seccomp_stackframe.edx;
     694               0 :         cpu->esi = seccomp_stackframe.esi;
     695               0 :         cpu->edi = seccomp_stackframe.edi;
     696               0 :         cpu->ebp = seccomp_stackframe.ebp;
     697               0 :         cpu->esp = top + 4*sizeof(void*);
     698               0 :         cpu->eip = seccomp_stackframe.fakeret;
     699               0 :         return;
     700                 :       }
     701                 :     }
     702                 : #endif
     703                 :   }
     704                 : 
     705                 :   // Write information about the threads.
     706               0 :   bool WriteThreadListStream(MDRawDirectory* dirent) {
     707               0 :     const unsigned num_threads = dumper_.threads().size();
     708                 : 
     709               0 :     TypedMDRVA<uint32_t> list(&minidump_writer_);
     710               0 :     if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
     711               0 :       return false;
     712                 : 
     713               0 :     dirent->stream_type = MD_THREAD_LIST_STREAM;
     714               0 :     dirent->location = list.location();
     715                 : 
     716               0 :     *list.get() = num_threads;
     717                 : 
     718               0 :     for (unsigned i = 0; i < num_threads; ++i) {
     719                 :       MDRawThread thread;
     720               0 :       my_memset(&thread, 0, sizeof(thread));
     721               0 :       thread.thread_id = dumper_.threads()[i];
     722                 :       // We have a different source of information for the crashing thread. If
     723                 :       // we used the actual state of the thread we would find it running in the
     724                 :       // signal handler with the alternative stack, which would be deeply
     725                 :       // unhelpful.
     726               0 :       if (HaveCrashedThread() &&
     727                 :           (pid_t)thread.thread_id == crashing_tid_) {
     728                 :         const void* stack;
     729                 :         size_t stack_len;
     730               0 :         if (!dumper_.GetStackInfo(&stack, &stack_len, StackPointer(ucontext_)))
     731               0 :           return false;
     732               0 :         UntypedMDRVA memory(&minidump_writer_);
     733               0 :         if (!memory.Allocate(stack_len))
     734               0 :           return false;
     735               0 :         uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
     736               0 :         dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
     737               0 :         memory.Copy(stack_copy, stack_len);
     738               0 :         thread.stack.start_of_memory_range = (uintptr_t) (stack);
     739               0 :         thread.stack.memory = memory.location();
     740               0 :         memory_blocks_.push_back(thread.stack);
     741                 : 
     742                 :         // Copy 256 bytes around crashing instruction pointer to minidump.
     743               0 :         const size_t kIPMemorySize = 256;
     744               0 :         u_int64_t ip = GetInstructionPointer();
     745                 :         // Bound it to the upper and lower bounds of the memory map
     746                 :         // it's contained within. If it's not in mapped memory,
     747                 :         // don't bother trying to write it.
     748               0 :         bool ip_is_mapped = false;
     749                 :         MDMemoryDescriptor ip_memory_d;
     750               0 :         for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
     751               0 :           const MappingInfo& mapping = *dumper_.mappings()[i];
     752               0 :           if (ip >= mapping.start_addr &&
     753                 :               ip < mapping.start_addr + mapping.size) {
     754               0 :             ip_is_mapped = true;
     755                 :             // Try to get 128 bytes before and after the IP, but
     756                 :             // settle for whatever's available.
     757                 :             ip_memory_d.start_of_memory_range =
     758                 :               std::max(mapping.start_addr,
     759               0 :                        uintptr_t(ip - (kIPMemorySize / 2)));
     760                 :             uintptr_t end_of_range = 
     761                 :               std::min(uintptr_t(ip + (kIPMemorySize / 2)),
     762               0 :                        uintptr_t(mapping.start_addr + mapping.size));
     763                 :             ip_memory_d.memory.data_size =
     764               0 :               end_of_range - ip_memory_d.start_of_memory_range;
     765               0 :             break;
     766                 :           }
     767                 :         }
     768                 : 
     769               0 :         if (ip_is_mapped) {
     770               0 :           UntypedMDRVA ip_memory(&minidump_writer_);
     771               0 :           if (!ip_memory.Allocate(ip_memory_d.memory.data_size))
     772               0 :             return false;
     773                 :           uint8_t* memory_copy =
     774               0 :             (uint8_t*) dumper_.allocator()->Alloc(ip_memory_d.memory.data_size);
     775                 :           dumper_.CopyFromProcess(
     776                 :             memory_copy,
     777                 :             thread.thread_id,
     778                 :             reinterpret_cast<void*>(ip_memory_d.start_of_memory_range),
     779               0 :             ip_memory_d.memory.data_size);
     780               0 :           ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size);
     781               0 :           ip_memory_d.memory = ip_memory.location();
     782               0 :           memory_blocks_.push_back(ip_memory_d);
     783                 :         }
     784                 : 
     785               0 :         TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
     786               0 :         if (!cpu.Allocate())
     787               0 :           return false;
     788               0 :         my_memset(cpu.get(), 0, sizeof(RawContextCPU));
     789               0 :         CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
     790               0 :         PopSeccompStackFrame(cpu.get(), thread, stack_copy);
     791               0 :         thread.thread_context = cpu.location();
     792               0 :         crashing_thread_context_ = cpu.location();
     793                 :       } else {
     794                 :         ThreadInfo info;
     795               0 :         info.tid = dumper_.threads()[i];
     796               0 :         if (!dumper_.ThreadInfoGet(&info))
     797               0 :           return false;
     798               0 :         UntypedMDRVA memory(&minidump_writer_);
     799               0 :         if (!memory.Allocate(info.stack_len))
     800               0 :           return false;
     801                 :         uint8_t* stack_copy =
     802               0 :             (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
     803                 :         dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
     804               0 :                                 info.stack_len);
     805               0 :         memory.Copy(stack_copy, info.stack_len);
     806               0 :         thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
     807               0 :         thread.stack.memory = memory.location();
     808               0 :         memory_blocks_.push_back(thread.stack);
     809                 : 
     810               0 :         TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
     811               0 :         if (!cpu.Allocate())
     812               0 :           return false;
     813               0 :         my_memset(cpu.get(), 0, sizeof(RawContextCPU));
     814               0 :         CPUFillFromThreadInfo(cpu.get(), info);
     815               0 :         PopSeccompStackFrame(cpu.get(), thread, stack_copy);
     816               0 :         thread.thread_context = cpu.location();
     817                 : 
     818               0 :         if ((pid_t)thread.thread_id == crashing_tid_) {
     819               0 :           assert(!HaveCrashedThread());
     820                 :           // we're dumping a live process and just found the thread
     821                 :           // that should be "blamed" for the dump.  Grab its PC so we
     822                 :           // can write it to the exception stream.
     823               0 :           crashing_tid_pc_ = InstructionPointer(info);
     824               0 :           crashing_thread_context_ = cpu.location();
     825                 :         }
     826                 :       }
     827                 : 
     828               0 :       list.CopyIndexAfterObject(i, &thread, sizeof(thread));
     829                 :     }
     830                 : 
     831               0 :     return true;
     832                 :   }
     833                 : 
     834               0 :   bool WriteAppMemory() {
     835               0 :     for (AppMemoryList::const_iterator iter = app_memory_info_.begin();
     836               0 :          iter != app_memory_info_.end();
     837                 :          ++iter) {
     838                 :       uint8_t* data_copy =
     839               0 :         (uint8_t*) dumper_.allocator()->Alloc(iter->length);
     840               0 :       dumper_.CopyFromProcess(data_copy, crashing_tid_, iter->ptr,
     841               0 :                               iter->length);
     842                 : 
     843               0 :       UntypedMDRVA memory(&minidump_writer_);
     844               0 :       if (!memory.Allocate(iter->length))
     845               0 :         return false;
     846               0 :       memory.Copy(data_copy, iter->length);
     847                 :       MDMemoryDescriptor desc;
     848               0 :       desc.start_of_memory_range = (uintptr_t)iter->ptr;
     849               0 :       desc.memory = memory.location();
     850               0 :       memory_blocks_.push_back(desc);
     851                 :     }
     852                 : 
     853               0 :     return true;
     854                 :   }
     855                 : 
     856               0 :   static bool ShouldIncludeMapping(const MappingInfo& mapping) {
     857               0 :     if (mapping.name[0] == 0 || // we only want modules with filenames.
     858                 :         mapping.offset || // we only want to include one mapping per shared lib.
     859                 :         mapping.size < 4096) {  // too small to get a signature for.
     860               0 :       return false;
     861                 :     }
     862                 : 
     863               0 :     return true;
     864                 :   }
     865                 : 
     866                 :   // If there is caller-provided information about this mapping
     867                 :   // in the mapping_info_ list, return true. Otherwise, return false.
     868               0 :   bool HaveMappingInfo(const MappingInfo& mapping) {
     869               0 :     for (MappingList::const_iterator iter = mapping_info_.begin();
     870               0 :          iter != mapping_info_.end();
     871                 :          ++iter) {
     872                 :       // Ignore any mappings that are wholly contained within
     873                 :       // mappings in the mapping_info_ list.
     874               0 :       if (mapping.start_addr >= iter->first.start_addr &&
     875                 :           (mapping.start_addr + mapping.size) <=
     876               0 :           (iter->first.start_addr + iter->first.size)) {
     877               0 :         return true;
     878                 :       }
     879                 :     }
     880               0 :     return false;
     881                 :   }
     882                 : 
     883                 :   // Write information about the mappings in effect. Because we are using the
     884                 :   // minidump format, the information about the mappings is pretty limited.
     885                 :   // Because of this, we also include the full, unparsed, /proc/$x/maps file in
     886                 :   // another stream in the file.
     887               0 :   bool WriteMappings(MDRawDirectory* dirent) {
     888               0 :     const unsigned num_mappings = dumper_.mappings().size();
     889               0 :     unsigned num_output_mappings = mapping_info_.size();
     890                 : 
     891               0 :     for (unsigned i = 0; i < dumper_.mappings().size(); ++i) {
     892               0 :       const MappingInfo& mapping = *dumper_.mappings()[i];
     893               0 :       if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping))
     894               0 :         num_output_mappings++;
     895                 :     }
     896                 : 
     897               0 :     TypedMDRVA<uint32_t> list(&minidump_writer_);
     898               0 :     if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE))
     899               0 :       return false;
     900                 : 
     901               0 :     dirent->stream_type = MD_MODULE_LIST_STREAM;
     902               0 :     dirent->location = list.location();
     903               0 :     *list.get() = num_output_mappings;
     904                 : 
     905                 :     // First write all the mappings from the dumper
     906               0 :     unsigned int j = 0;
     907               0 :     for (unsigned i = 0; i < num_mappings; ++i) {
     908               0 :       const MappingInfo& mapping = *dumper_.mappings()[i];
     909               0 :       if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping))
     910               0 :         continue;
     911                 : 
     912                 :       MDRawModule mod;
     913               0 :       if (!FillRawModule(mapping, mod, NULL))
     914               0 :         return false;
     915               0 :       list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
     916                 :     }
     917                 :     // Next write all the mappings provided by the caller
     918               0 :     for (MappingList::const_iterator iter = mapping_info_.begin();
     919               0 :          iter != mapping_info_.end();
     920                 :          ++iter) {
     921                 :       MDRawModule mod;
     922               0 :       if (!FillRawModule(iter->first, mod, iter->second))
     923               0 :           return false;
     924               0 :         list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
     925                 :     }
     926                 : 
     927               0 :     return true;
     928                 :   }
     929                 : 
     930                 :   // Fill the MDRawModule mod with information about the provided
     931                 :   // mapping. If identifier is non-NULL, use it instead of calculating
     932                 :   // a file ID from the mapping.
     933               0 :   bool FillRawModule(const MappingInfo& mapping,
     934                 :                      MDRawModule& mod,
     935                 :                      const u_int8_t* identifier) {
     936               0 :     my_memset(&mod, 0, MD_MODULE_SIZE);
     937                 : 
     938               0 :     mod.base_of_image = mapping.start_addr;
     939               0 :     mod.size_of_image = mapping.size;
     940               0 :     const size_t filepath_len = my_strlen(mapping.name);
     941                 : 
     942                 :     // Figure out file name from path
     943               0 :     const char* filename_ptr = mapping.name + filepath_len - 1;
     944               0 :     while (filename_ptr >= mapping.name) {
     945               0 :       if (*filename_ptr == '/')
     946               0 :         break;
     947               0 :       filename_ptr--;
     948                 :     }
     949               0 :     filename_ptr++;
     950                 : 
     951               0 :     size_t filename_len = mapping.name + filepath_len - filename_ptr;
     952                 : 
     953                 :     uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
     954               0 :     uint8_t* cv_ptr = cv_buf;
     955               0 :     UntypedMDRVA cv(&minidump_writer_);
     956               0 :     if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
     957               0 :       return false;
     958                 : 
     959               0 :     const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
     960               0 :     memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
     961               0 :     cv_ptr += sizeof(cv_signature);
     962               0 :     uint8_t* signature = cv_ptr;
     963               0 :     cv_ptr += sizeof(MDGUID);
     964               0 :     if (identifier) {
     965                 :       // GUID was provided by caller.
     966               0 :       memcpy(signature, identifier, sizeof(MDGUID));
     967                 :     } else {
     968               0 :       dumper_.ElfFileIdentifierForMapping(mapping, signature);
     969                 :     }
     970               0 :     my_memset(cv_ptr, 0, sizeof(uint32_t));  // Set age to 0 on Linux.
     971               0 :     cv_ptr += sizeof(uint32_t);
     972                 : 
     973                 :     // Write pdb_file_name
     974               0 :     memcpy(cv_ptr, filename_ptr, filename_len + 1);
     975               0 :     cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
     976                 : 
     977               0 :     mod.cv_record = cv.location();
     978                 : 
     979                 :     MDLocationDescriptor ld;
     980               0 :     if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
     981               0 :       return false;
     982               0 :     mod.module_name_rva = ld.rva;
     983               0 :     return true;
     984                 :   }
     985                 : 
     986               0 :   bool WriteMemoryListStream(MDRawDirectory* dirent) {
     987               0 :     TypedMDRVA<uint32_t> list(&minidump_writer_);
     988               0 :     if (!list.AllocateObjectAndArray(memory_blocks_.size(),
     989               0 :                                      sizeof(MDMemoryDescriptor)))
     990               0 :       return false;
     991                 : 
     992               0 :     dirent->stream_type = MD_MEMORY_LIST_STREAM;
     993               0 :     dirent->location = list.location();
     994                 : 
     995               0 :     *list.get() = memory_blocks_.size();
     996                 : 
     997               0 :     for (size_t i = 0; i < memory_blocks_.size(); ++i) {
     998               0 :       list.CopyIndexAfterObject(i, &memory_blocks_[i],
     999               0 :                                 sizeof(MDMemoryDescriptor));
    1000                 :     }
    1001               0 :     return true;
    1002                 :   }
    1003                 : 
    1004               0 :   bool WriteExceptionStream(MDRawDirectory* dirent) {
    1005               0 :     TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
    1006               0 :     if (!exc.Allocate())
    1007               0 :       return false;
    1008               0 :     my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
    1009                 : 
    1010               0 :     dirent->stream_type = MD_EXCEPTION_STREAM;
    1011               0 :     dirent->location = exc.location();
    1012                 : 
    1013               0 :     int signo = HaveCrashedThread() ? siginfo_->si_signo : SIGSTOP;
    1014               0 :     uintptr_t crash_addr = HaveCrashedThread() ?
    1015               0 :                            uintptr_t(siginfo_->si_addr) : crashing_tid_pc_;
    1016                 : 
    1017               0 :     exc.get()->thread_id = crashing_tid_;
    1018               0 :     exc.get()->exception_record.exception_code = signo;
    1019               0 :     exc.get()->exception_record.exception_address = crash_addr;
    1020               0 :     exc.get()->thread_context = crashing_thread_context_;
    1021                 : 
    1022               0 :     return true;
    1023                 :   }
    1024                 : 
    1025               0 :   bool WriteSystemInfoStream(MDRawDirectory* dirent) {
    1026               0 :     TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_);
    1027               0 :     if (!si.Allocate())
    1028               0 :       return false;
    1029               0 :     my_memset(si.get(), 0, sizeof(MDRawSystemInfo));
    1030                 : 
    1031               0 :     dirent->stream_type = MD_SYSTEM_INFO_STREAM;
    1032               0 :     dirent->location = si.location();
    1033                 : 
    1034               0 :     WriteCPUInformation(si.get());
    1035               0 :     WriteOSInformation(si.get());
    1036                 : 
    1037               0 :     return true;
    1038                 :   }
    1039                 : 
    1040               0 :   bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug,
    1041                 :                            uint32_t dynamic_length) {
    1042                 : #if defined(__ANDROID__)
    1043                 :     return false;
    1044                 : #else
    1045                 :     // The caller provided us with a pointer to "struct r_debug". We can
    1046                 :     // look up the "r_map" field to get a linked list of all loaded DSOs.
    1047                 :     // Our list of DSOs potentially is different from the ones in the crashing
    1048                 :     // process. So, we have to be careful to never dereference pointers
    1049                 :     // directly. Instead, we use CopyFromProcess() everywhere.
    1050                 :     // See <link.h> for a more detailed discussion of the how the dynamic
    1051                 :     // loader communicates with debuggers.
    1052                 : 
    1053                 :     // Count the number of loaded DSOs
    1054               0 :     int dso_count = 0;
    1055                 :     struct r_debug debug_entry;
    1056                 :     dumper_.CopyFromProcess(&debug_entry, crashing_tid_, r_debug,
    1057               0 :                             sizeof(debug_entry));
    1058               0 :     for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
    1059                 :       struct link_map map;
    1060               0 :       dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
    1061               0 :       ptr = map.l_next;
    1062               0 :       dso_count++;
    1063                 :     }
    1064                 : 
    1065               0 :     MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA;
    1066               0 :     if (dso_count > 0) {
    1067                 :       // If we have at least one DSO, create an array of MDRawLinkMap
    1068                 :       // entries in the minidump file.
    1069               0 :       TypedMDRVA<MDRawLinkMap> linkmap(&minidump_writer_);
    1070               0 :       if (!linkmap.AllocateArray(dso_count))
    1071               0 :         return false;
    1072               0 :       linkmap_rva = linkmap.location().rva;
    1073               0 :       int idx = 0;
    1074                 : 
    1075                 :       // Iterate over DSOs and write their information to mini dump
    1076               0 :       for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
    1077                 :         struct link_map map;
    1078               0 :         dumper_.CopyFromProcess(&map, crashing_tid_, ptr, sizeof(map));
    1079               0 :         ptr = map.l_next;
    1080               0 :         char filename[257] = { 0 };
    1081               0 :         if (map.l_name) {
    1082                 :           dumper_.CopyFromProcess(filename, crashing_tid_, map.l_name,
    1083               0 :                                   sizeof(filename) - 1);
    1084                 :         }
    1085                 :         MDLocationDescriptor location;
    1086               0 :         if (!minidump_writer_.WriteString(filename, 0, &location))
    1087               0 :           return false;
    1088                 :         MDRawLinkMap entry;
    1089               0 :         entry.name = location.rva;
    1090               0 :         entry.addr = (void*)map.l_addr;
    1091               0 :         entry.ld = (void*)map.l_ld;
    1092               0 :         linkmap.CopyIndex(idx++, &entry);
    1093                 :       }
    1094                 :     }
    1095                 : 
    1096                 :     // Write MD_LINUX_DSO_DEBUG record
    1097               0 :     TypedMDRVA<MDRawDebug> debug(&minidump_writer_);
    1098               0 :     if (!debug.AllocateObjectAndArray(1, dynamic_length))
    1099               0 :       return false;
    1100               0 :     my_memset(debug.get(), 0, sizeof(MDRawDebug));
    1101               0 :     dirent->stream_type = MD_LINUX_DSO_DEBUG;
    1102               0 :     dirent->location = debug.location();
    1103                 : 
    1104               0 :     debug.get()->version = debug_entry.r_version;
    1105               0 :     debug.get()->map = linkmap_rva;
    1106               0 :     debug.get()->dso_count = dso_count;
    1107               0 :     debug.get()->brk = (void*)debug_entry.r_brk;
    1108               0 :     debug.get()->ldbase = (void*)debug_entry.r_ldbase;
    1109               0 :     debug.get()->dynamic = (void*)&_DYNAMIC;
    1110                 : 
    1111               0 :     char *dso_debug_data = new char[dynamic_length];
    1112                 :     dumper_.CopyFromProcess(dso_debug_data, crashing_tid_, &_DYNAMIC,
    1113               0 :                             dynamic_length);
    1114               0 :     debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length);
    1115               0 :     delete[] dso_debug_data;
    1116                 : 
    1117               0 :     return true;
    1118                 : #endif  // __ANDROID__
    1119                 :   }
    1120                 : 
    1121                 :  private:
    1122                 : #if defined(__i386)
    1123               0 :   uintptr_t GetInstructionPointer() {
    1124               0 :     return ucontext_->uc_mcontext.gregs[REG_EIP];
    1125                 :   }
    1126                 : #elif defined(__x86_64)
    1127                 :   uintptr_t GetInstructionPointer() {
    1128                 :     return ucontext_->uc_mcontext.gregs[REG_RIP];
    1129                 :   }
    1130                 : #elif defined(__ARM_EABI__)
    1131                 :   uintptr_t GetInstructionPointer() {
    1132                 :     return ucontext_->uc_mcontext.arm_ip;
    1133                 :   }
    1134                 : #else
    1135                 : #error "This code has not been ported to your platform yet."
    1136                 : #endif
    1137                 : 
    1138               0 :   void NullifyDirectoryEntry(MDRawDirectory* dirent) {
    1139               0 :     dirent->stream_type = 0;
    1140               0 :     dirent->location.data_size = 0;
    1141               0 :     dirent->location.rva = 0;
    1142               0 :   }
    1143                 : 
    1144               0 :   bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
    1145               0 :     char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
    1146                 :     static const char vendor_id_name[] = "vendor_id";
    1147                 :     static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1;
    1148                 : 
    1149                 :     struct CpuInfoEntry {
    1150                 :       const char* info_name;
    1151                 :       int value;
    1152                 :       bool found;
    1153                 :     } cpu_info_table[] = {
    1154                 :       { "processor", -1, false },
    1155                 :       { "model", 0, false },
    1156                 :       { "stepping",  0, false },
    1157                 :       { "cpu family", 0, false },
    1158               0 :     };
    1159                 : 
    1160                 :     // processor_architecture should always be set, do this first
    1161                 :     sys_info->processor_architecture =
    1162                 : #if defined(__i386)
    1163               0 :         MD_CPU_ARCHITECTURE_X86;
    1164                 : #elif defined(__x86_64)
    1165                 :         MD_CPU_ARCHITECTURE_AMD64;
    1166                 : #elif defined(__arm__)
    1167                 :         MD_CPU_ARCHITECTURE_ARM;
    1168                 : #else
    1169                 : #error "Unknown CPU arch"
    1170                 : #endif
    1171                 : 
    1172               0 :     const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0);
    1173               0 :     if (fd < 0)
    1174               0 :       return false;
    1175                 : 
    1176                 :     {
    1177               0 :       PageAllocator allocator;
    1178               0 :       LineReader* const line_reader = new(allocator) LineReader(fd);
    1179                 :       const char* line;
    1180                 :       unsigned line_len;
    1181               0 :       while (line_reader->GetNextLine(&line, &line_len)) {
    1182               0 :         for (size_t i = 0;
    1183                 :              i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
    1184                 :              i++) {
    1185               0 :           CpuInfoEntry* entry = &cpu_info_table[i];
    1186               0 :           if (entry->found && i)
    1187               0 :             continue;
    1188               0 :           if (!strncmp(line, entry->info_name, strlen(entry->info_name))) {
    1189               0 :             const char* value = strchr(line, ':');
    1190               0 :             if (!value)
    1191               0 :               continue;
    1192                 : 
    1193                 :             // the above strncmp only matches the prefix, it might be the wrong
    1194                 :             // line. i.e. we matched "model name" instead of "model".
    1195                 :             // check and make sure there is only spaces between the prefix and
    1196                 :             // the colon.
    1197               0 :             const char* space_ptr = line + strlen(entry->info_name);
    1198               0 :             for (; space_ptr < value; space_ptr++) {
    1199               0 :               if (!isspace(*space_ptr)) {
    1200               0 :                 break;
    1201                 :               }
    1202                 :             }
    1203               0 :             if (space_ptr != value)
    1204               0 :               continue;
    1205                 : 
    1206               0 :             sscanf(++value, " %d", &(entry->value));
    1207               0 :             entry->found = true;
    1208                 :           }
    1209                 :         }
    1210                 : 
    1211                 :         // special case for vendor_id
    1212               0 :         if (!strncmp(line, vendor_id_name, vendor_id_name_length)) {
    1213               0 :           const char* value = strchr(line, ':');
    1214               0 :           if (!value)
    1215               0 :             goto popline;
    1216                 : 
    1217                 :           // skip ':" and all the spaces that follows
    1218               0 :           do {
    1219               0 :             value++;
    1220               0 :           } while (isspace(*value));
    1221                 : 
    1222               0 :           if (*value) {
    1223               0 :             size_t length = strlen(value);
    1224               0 :             if (length == 0)
    1225               0 :               goto popline;
    1226                 :             // we don't want the trailing newline
    1227               0 :             if (value[length - 1] == '\n')
    1228               0 :               length--;
    1229                 :             // ensure we have space for the value
    1230               0 :             if (length < sizeof(vendor_id))
    1231               0 :               strncpy(vendor_id, value, length);
    1232                 :           }
    1233                 :         }
    1234                 : 
    1235                 : popline:
    1236               0 :         line_reader->PopLine(line_len);
    1237                 :       }
    1238               0 :       sys_close(fd);
    1239                 :     }
    1240                 : 
    1241                 :     // make sure we got everything we wanted
    1242               0 :     for (size_t i = 0;
    1243                 :          i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
    1244                 :          i++) {
    1245               0 :       if (!cpu_info_table[i].found) {
    1246               0 :         return false;
    1247                 :       }
    1248                 :     }
    1249                 :     // /proc/cpuinfo contains cpu id, change it into number by adding one.
    1250               0 :     cpu_info_table[0].value++;
    1251                 : 
    1252               0 :     sys_info->number_of_processors = cpu_info_table[0].value;
    1253               0 :     sys_info->processor_level      = cpu_info_table[3].value;
    1254                 :     sys_info->processor_revision   = cpu_info_table[1].value << 8 |
    1255               0 :                                      cpu_info_table[2].value;
    1256                 : 
    1257               0 :     if (vendor_id[0] != '\0') {
    1258                 :       memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
    1259               0 :              sizeof(sys_info->cpu.x86_cpu_info.vendor_id));
    1260                 :     }
    1261               0 :     return true;
    1262                 :   }
    1263                 : 
    1264               0 :   bool WriteFile(MDLocationDescriptor* result, const char* filename) {
    1265               0 :     const int fd = sys_open(filename, O_RDONLY, 0);
    1266               0 :     if (fd < 0)
    1267               0 :       return false;
    1268                 : 
    1269                 :     // We can't stat the files because several of the files that we want to
    1270                 :     // read are kernel seqfiles, which always have a length of zero. So we have
    1271                 :     // to read as much as we can into a buffer.
    1272                 :     static const unsigned kBufSize = 1024 - 2*sizeof(void*);
    1273                 :     struct Buffers {
    1274                 :       struct Buffers* next;
    1275                 :       size_t len;
    1276                 :       uint8_t data[kBufSize];
    1277                 :     } *buffers =
    1278               0 :         (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
    1279               0 :     buffers->next = NULL;
    1280               0 :     buffers->len = 0;
    1281                 : 
    1282               0 :     size_t total = 0;
    1283               0 :     for (struct Buffers* bufptr = buffers;;) {
    1284                 :       ssize_t r;
    1285               0 :       do {
    1286               0 :         r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len);
    1287               0 :       } while (r == -1 && errno == EINTR);
    1288                 : 
    1289               0 :       if (r < 1)
    1290                 :         break;
    1291                 :       
    1292               0 :       total += r;
    1293               0 :       bufptr->len += r;
    1294               0 :       if (bufptr->len == kBufSize) {
    1295                 :         bufptr->next =
    1296               0 :           (struct Buffers*) dumper_.allocator()->Alloc(sizeof(struct Buffers));
    1297               0 :         bufptr = bufptr->next;
    1298               0 :         bufptr->next = NULL;
    1299               0 :         bufptr->len = 0;
    1300                 :       }
    1301                 :     }
    1302               0 :     sys_close(fd);
    1303                 : 
    1304               0 :     if (!total)
    1305               0 :       return false;
    1306                 : 
    1307               0 :     UntypedMDRVA memory(&minidump_writer_);
    1308               0 :     if (!memory.Allocate(total))
    1309               0 :       return false;
    1310               0 :     for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) {
    1311               0 :       memory.Copy(pos, &buffers->data, buffers->len);
    1312               0 :       pos += buffers->len;
    1313                 :     }
    1314               0 :     *result = memory.location();
    1315               0 :     return true;
    1316                 :   }
    1317                 : 
    1318               0 :   bool WriteOSInformation(MDRawSystemInfo* sys_info) {
    1319               0 :     sys_info->platform_id = MD_OS_LINUX;
    1320                 : 
    1321                 :     struct utsname uts;
    1322               0 :     if (uname(&uts))
    1323               0 :       return false;
    1324                 : 
    1325                 :     static const size_t buf_len = 512;
    1326               0 :     char buf[buf_len] = {0};
    1327               0 :     size_t space_left = buf_len - 1;
    1328                 :     const char* info_table[] = {
    1329                 :       uts.sysname,
    1330                 :       uts.release,
    1331                 :       uts.version,
    1332                 :       uts.machine,
    1333                 :       NULL
    1334               0 :     };
    1335               0 :     bool first_item = true;
    1336               0 :     for (const char** cur_info = info_table; *cur_info; cur_info++) {
    1337                 :       static const char* separator = " ";
    1338               0 :       size_t separator_len = strlen(separator);
    1339               0 :       size_t info_len = strlen(*cur_info);
    1340               0 :       if (info_len == 0)
    1341               0 :         continue;
    1342                 : 
    1343               0 :       if (space_left < info_len + (first_item ? 0 : separator_len))
    1344               0 :         break;
    1345                 : 
    1346               0 :       if (!first_item) {
    1347               0 :         strcat(buf, separator);
    1348               0 :         space_left -= separator_len;
    1349                 :       }
    1350                 : 
    1351               0 :       first_item = false;
    1352               0 :       strcat(buf, *cur_info);
    1353               0 :       space_left -= info_len;
    1354                 :     }
    1355                 : 
    1356                 :     MDLocationDescriptor location;
    1357               0 :     if (!minidump_writer_.WriteString(buf, 0, &location))
    1358               0 :       return false;
    1359               0 :     sys_info->csd_version_rva = location.rva;
    1360                 : 
    1361               0 :     return true;
    1362                 :   }
    1363                 : 
    1364               0 :   bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
    1365                 :                      const char* filename) {
    1366                 :     char buf[80];
    1367               0 :     memcpy(buf, "/proc/", 6);
    1368               0 :     const unsigned pid_len = my_int_len(pid);
    1369               0 :     my_itos(buf + 6, pid, pid_len);
    1370               0 :     buf[6 + pid_len] = '/';
    1371               0 :     memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
    1372               0 :     return WriteFile(result, buf);
    1373                 :   }
    1374                 : 
    1375                 :   const char* const filename_;  // output filename
    1376                 :   const siginfo_t* const siginfo_;  // from the signal handler (see sigaction)
    1377                 :   const struct ucontext* const ucontext_;  // also from the signal handler
    1378                 :   const struct _libc_fpstate* const float_state_;  // ditto
    1379                 :   const pid_t crashing_tid_;  // the process which actually crashed
    1380                 :   uintptr_t crashing_tid_pc_; // set if we're dumping a live process
    1381                 :                               // and find crashing_tid_.  used to
    1382                 :                               // write exception info.  (if we're
    1383                 :                               // dumping a crash, this stays 0 and we
    1384                 :                               // use siginfo_)
    1385                 :   LinuxDumper dumper_;
    1386                 :   MinidumpFileWriter minidump_writer_;
    1387                 :   MDLocationDescriptor crashing_thread_context_;
    1388                 :   // Blocks of memory written to the dump. These are all currently
    1389                 :   // written while writing the thread list stream, but saved here
    1390                 :   // so a memory list stream can be written afterwards.
    1391                 :   wasteful_vector<MDMemoryDescriptor> memory_blocks_;
    1392                 :   // Additional information about some mappings provided by the caller.
    1393                 :   const MappingList& mapping_info_;
    1394                 :   // Callers can request additional memory regions to be included in
    1395                 :   // the dump.
    1396                 :   const AppMemoryList& app_memory_info_;
    1397                 : };
    1398                 : 
    1399               0 : bool WriteMinidump(const char* filename, pid_t crashing_process,
    1400                 :                    const void* blob, size_t blob_size) {
    1401               0 :   MappingList m;
    1402               0 :   AppMemoryList a;
    1403               0 :   return WriteMinidump(filename, crashing_process, blob, blob_size, m, a);
    1404                 : }
    1405                 : 
    1406               0 : bool WriteMinidump(const char* filename, pid_t crashing_process,
    1407                 :                    const void* blob, size_t blob_size,
    1408                 :                    const MappingList& mappings,
    1409                 :                    const AppMemoryList& appmem) {
    1410               0 :   if (blob_size != sizeof(ExceptionHandler::CrashContext))
    1411               0 :     return false;
    1412                 :   const ExceptionHandler::CrashContext* context =
    1413               0 :       reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
    1414               0 :   MinidumpWriter writer(filename, crashing_process, context, mappings, appmem);
    1415               0 :   if (!writer.Init())
    1416               0 :     return false;
    1417               0 :   return writer.Dump();
    1418                 : }
    1419                 : 
    1420               0 : bool WriteMinidump(const char* filename, pid_t process,
    1421                 :                    pid_t process_blamed_thread) {
    1422                 :   //TODO: support mappings here
    1423               0 :   MappingList m;
    1424               0 :   AppMemoryList a;
    1425               0 :   MinidumpWriter writer(filename, process, process_blamed_thread, m, a);
    1426               0 :   if (!writer.Init())
    1427               0 :     return false;
    1428               0 :   return writer.Dump();
    1429                 : }
    1430                 : 
    1431                 : }  // namespace google_breakpad

Generated by: LCOV version 1.7