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