| OLD | NEW |
| (Empty) |
| 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 "breakpad/linux/minidump_writer.h" | |
| 47 #include "client/minidump_file_writer-inl.h" | |
| 48 | |
| 49 #include <errno.h> | |
| 50 #include <fcntl.h> | |
| 51 #include <stdio.h> | |
| 52 #include <unistd.h> | |
| 53 #include <sys/ucontext.h> | |
| 54 #include <sys/user.h> | |
| 55 #include <sys/utsname.h> | |
| 56 | |
| 57 #include "client/minidump_file_writer.h" | |
| 58 #include "google_breakpad/common/minidump_format.h" | |
| 59 #include "google_breakpad/common/minidump_cpu_amd64.h" | |
| 60 #include "google_breakpad/common/minidump_cpu_x86.h" | |
| 61 | |
| 62 #include "breakpad/linux/exception_handler.h" | |
| 63 #include "breakpad/linux/line_reader.h" | |
| 64 #include "breakpad/linux/linux_dumper.h" | |
| 65 #include "breakpad/linux/linux_libc_support.h" | |
| 66 #include "breakpad/linux/linux_syscall_support.h" | |
| 67 #include "breakpad/linux/minidump_format_linux.h" | |
| 68 | |
| 69 // Minidump defines register structures which are different from the raw | |
| 70 // structures which we get from the kernel. These are platform specific | |
| 71 // functions to juggle the ucontext and user structures into minidump format. | |
| 72 #if defined(__i386) | |
| 73 typedef MDRawContextX86 RawContextCPU; | |
| 74 | |
| 75 // Write a uint16_t to memory | |
| 76 // out: memory location to write to | |
| 77 // v: value to write. | |
| 78 static void U16(void* out, uint16_t v) { | |
| 79 memcpy(out, &v, sizeof(v)); | |
| 80 } | |
| 81 | |
| 82 // Write a uint32_t to memory | |
| 83 // out: memory location to write to | |
| 84 // v: value to write. | |
| 85 static void U32(void* out, uint32_t v) { | |
| 86 memcpy(out, &v, sizeof(v)); | |
| 87 } | |
| 88 | |
| 89 // Juggle an x86 user_(fp|fpx|)regs_struct into minidump format | |
| 90 // out: the minidump structure | |
| 91 // info: the collection of register structures. | |
| 92 static void CPUFillFromThreadInfo(MDRawContextX86 *out, | |
| 93 const google_breakpad::ThreadInfo &info) { | |
| 94 out->context_flags = MD_CONTEXT_X86_ALL; | |
| 95 | |
| 96 out->dr0 = info.dregs[0]; | |
| 97 out->dr1 = info.dregs[1]; | |
| 98 out->dr2 = info.dregs[2]; | |
| 99 out->dr3 = info.dregs[3]; | |
| 100 // 4 and 5 deliberatly omitted because they aren't included in the minidump | |
| 101 // format. | |
| 102 out->dr6 = info.dregs[6]; | |
| 103 out->dr7 = info.dregs[7]; | |
| 104 | |
| 105 out->gs = info.regs.xgs; | |
| 106 out->fs = info.regs.xfs; | |
| 107 out->es = info.regs.xes; | |
| 108 out->ds = info.regs.xds; | |
| 109 | |
| 110 out->edi = info.regs.edi; | |
| 111 out->esi = info.regs.esi; | |
| 112 out->ebx = info.regs.ebx; | |
| 113 out->edx = info.regs.edx; | |
| 114 out->ecx = info.regs.ecx; | |
| 115 out->eax = info.regs.eax; | |
| 116 | |
| 117 out->ebp = info.regs.ebp; | |
| 118 out->eip = info.regs.eip; | |
| 119 out->cs = info.regs.xcs; | |
| 120 out->eflags = info.regs.eflags; | |
| 121 out->esp = info.regs.esp; | |
| 122 out->ss = info.regs.xss; | |
| 123 | |
| 124 out->float_save.control_word = info.fpregs.cwd; | |
| 125 out->float_save.status_word = info.fpregs.swd; | |
| 126 out->float_save.tag_word = info.fpregs.twd; | |
| 127 out->float_save.error_offset = info.fpregs.fip; | |
| 128 out->float_save.error_selector = info.fpregs.fcs; | |
| 129 out->float_save.data_offset = info.fpregs.foo; | |
| 130 out->float_save.data_selector = info.fpregs.fos; | |
| 131 | |
| 132 // 8 registers * 10 bytes per register. | |
| 133 memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); | |
| 134 | |
| 135 // This matches the Intel fpsave format. | |
| 136 U16(out->extended_registers + 0, info.fpregs.cwd); | |
| 137 U16(out->extended_registers + 2, info.fpregs.swd); | |
| 138 U16(out->extended_registers + 4, info.fpregs.twd); | |
| 139 U16(out->extended_registers + 6, info.fpxregs.fop); | |
| 140 U32(out->extended_registers + 8, info.fpxregs.fip); | |
| 141 U16(out->extended_registers + 12, info.fpxregs.fcs); | |
| 142 U32(out->extended_registers + 16, info.fpregs.foo); | |
| 143 U16(out->extended_registers + 20, info.fpregs.fos); | |
| 144 U32(out->extended_registers + 24, info.fpxregs.mxcsr); | |
| 145 | |
| 146 memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); | |
| 147 memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); | |
| 148 } | |
| 149 | |
| 150 // Juggle an x86 ucontext into minidump format | |
| 151 // out: the minidump structure | |
| 152 // info: the collection of register structures. | |
| 153 static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, | |
| 154 const struct _libc_fpstate* fp) { | |
| 155 const greg_t* regs = uc->uc_mcontext.gregs; | |
| 156 | |
| 157 out->context_flags = MD_CONTEXT_X86_FULL | | |
| 158 MD_CONTEXT_X86_FLOATING_POINT; | |
| 159 | |
| 160 out->gs = regs[REG_GS]; | |
| 161 out->fs = regs[REG_FS]; | |
| 162 out->es = regs[REG_ES]; | |
| 163 out->ds = regs[REG_DS]; | |
| 164 | |
| 165 out->edi = regs[REG_EDI]; | |
| 166 out->esi = regs[REG_ESI]; | |
| 167 out->ebx = regs[REG_EBX]; | |
| 168 out->edx = regs[REG_EDX]; | |
| 169 out->ecx = regs[REG_ECX]; | |
| 170 out->eax = regs[REG_EAX]; | |
| 171 | |
| 172 out->ebp = regs[REG_EBP]; | |
| 173 out->eip = regs[REG_EIP]; | |
| 174 out->cs = regs[REG_CS]; | |
| 175 out->eflags = regs[REG_EFL]; | |
| 176 out->esp = regs[REG_UESP]; | |
| 177 out->ss = regs[REG_SS]; | |
| 178 | |
| 179 out->float_save.control_word = fp->cw; | |
| 180 out->float_save.status_word = fp->sw; | |
| 181 out->float_save.tag_word = fp->tag; | |
| 182 out->float_save.error_offset = fp->ipoff; | |
| 183 out->float_save.error_selector = fp->cssel; | |
| 184 out->float_save.data_offset = fp->dataoff; | |
| 185 out->float_save.data_selector = fp->datasel; | |
| 186 | |
| 187 // 8 registers * 10 bytes per register. | |
| 188 memcpy(out->float_save.register_area, fp->_st, 10 * 8); | |
| 189 } | |
| 190 | |
| 191 #elif defined(__x86_64) | |
| 192 typedef MDRawContextAMD64 RawContextCPU; | |
| 193 | |
| 194 static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, | |
| 195 const google_breakpad::ThreadInfo &info) { | |
| 196 out->context_flags = MD_CONTEXT_AMD64_FULL | | |
| 197 MD_CONTEXT_AMD64_SEGMENTS; | |
| 198 | |
| 199 out->cs = info.regs.cs; | |
| 200 | |
| 201 out->ds = info.regs.ds; | |
| 202 out->es = info.regs.es; | |
| 203 out->fs = info.regs.fs; | |
| 204 out->gs = info.regs.gs; | |
| 205 | |
| 206 out->ss = info.regs.ss; | |
| 207 out->eflags = info.regs.eflags; | |
| 208 | |
| 209 out->dr0 = info.dregs[0]; | |
| 210 out->dr1 = info.dregs[1]; | |
| 211 out->dr2 = info.dregs[2]; | |
| 212 out->dr3 = info.dregs[3]; | |
| 213 // 4 and 5 deliberatly omitted because they aren't included in the minidump | |
| 214 // format. | |
| 215 out->dr6 = info.dregs[6]; | |
| 216 out->dr7 = info.dregs[7]; | |
| 217 | |
| 218 out->rax = info.regs.rax; | |
| 219 out->rcx = info.regs.rcx; | |
| 220 out->rdx = info.regs.rdx; | |
| 221 out->rbx = info.regs.rbx; | |
| 222 | |
| 223 out->rsp = info.regs.rsp; | |
| 224 | |
| 225 out->rbp = info.regs.rbp; | |
| 226 out->rsi = info.regs.rsi; | |
| 227 out->rdi = info.regs.rdi; | |
| 228 out->r8 = info.regs.r8; | |
| 229 out->r9 = info.regs.r9; | |
| 230 out->r10 = info.regs.r10; | |
| 231 out->r11 = info.regs.r11; | |
| 232 out->r12 = info.regs.r12; | |
| 233 out->r13 = info.regs.r13; | |
| 234 out->r14 = info.regs.r14; | |
| 235 out->r15 = info.regs.r15; | |
| 236 | |
| 237 out->rip = info.regs.rip; | |
| 238 | |
| 239 out->flt_save.control_word = info.fpregs.cwd; | |
| 240 out->flt_save.status_word = info.fpregs.swd; | |
| 241 out->flt_save.tag_word = info.fpregs.ftw; | |
| 242 out->flt_save.error_opcode = info.fpregs.fop; | |
| 243 out->flt_save.error_offset = info.fpregs.rip; | |
| 244 out->flt_save.error_selector = 0; // We don't have this. | |
| 245 out->flt_save.data_offset = info.fpregs.rdp; | |
| 246 out->flt_save.data_selector = 0; // We don't have this. | |
| 247 out->flt_save.mx_csr = info.fpregs.mxcsr; | |
| 248 out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; | |
| 249 memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); | |
| 250 memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); | |
| 251 } | |
| 252 | |
| 253 static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, | |
| 254 const struct _libc_fpstate* fpregs) { | |
| 255 const greg_t* regs = uc->uc_mcontext.gregs; | |
| 256 | |
| 257 out->context_flags = MD_CONTEXT_AMD64_FULL; | |
| 258 | |
| 259 out->cs = regs[REG_CSGSFS] & 0xffff; | |
| 260 | |
| 261 out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; | |
| 262 out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; | |
| 263 | |
| 264 out->eflags = regs[REG_EFL]; | |
| 265 | |
| 266 out->rax = regs[REG_RAX]; | |
| 267 out->rcx = regs[REG_RCX]; | |
| 268 out->rdx = regs[REG_RDX]; | |
| 269 out->rbx = regs[REG_RBX]; | |
| 270 | |
| 271 out->rsp = regs[REG_RSP]; | |
| 272 out->rbp = regs[REG_RBP]; | |
| 273 out->rsi = regs[REG_RSI]; | |
| 274 out->rdi = regs[REG_RDI]; | |
| 275 out->r8 = regs[REG_R8]; | |
| 276 out->r9 = regs[REG_R9]; | |
| 277 out->r10 = regs[REG_R10]; | |
| 278 out->r11 = regs[REG_R11]; | |
| 279 out->r12 = regs[REG_R12]; | |
| 280 out->r13 = regs[REG_R13]; | |
| 281 out->r14 = regs[REG_R14]; | |
| 282 out->r15 = regs[REG_R15]; | |
| 283 | |
| 284 out->rip = regs[REG_RIP]; | |
| 285 | |
| 286 out->flt_save.control_word = fpregs->cwd; | |
| 287 out->flt_save.status_word = fpregs->swd; | |
| 288 out->flt_save.tag_word = fpregs->ftw; | |
| 289 out->flt_save.error_opcode = fpregs->fop; | |
| 290 out->flt_save.error_offset = fpregs->rip; | |
| 291 out->flt_save.data_offset = fpregs->rdp; | |
| 292 out->flt_save.error_selector = 0; // We don't have this. | |
| 293 out->flt_save.data_selector = 0; // We don't have this. | |
| 294 out->flt_save.mx_csr = fpregs->mxcsr; | |
| 295 out->flt_save.mx_csr_mask = fpregs->mxcr_mask; | |
| 296 memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); | |
| 297 memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); | |
| 298 } | |
| 299 | |
| 300 #else | |
| 301 #error "This code has not been ported to your platform yet." | |
| 302 #endif | |
| 303 | |
| 304 namespace google_breakpad { | |
| 305 | |
| 306 class MinidumpWriter { | |
| 307 public: | |
| 308 MinidumpWriter(const char* filename, | |
| 309 pid_t crashing_pid, | |
| 310 const ExceptionHandler::CrashContext* context) | |
| 311 : filename_(filename), | |
| 312 siginfo_(&context->siginfo), | |
| 313 ucontext_(&context->context), | |
| 314 float_state_(&context->float_state), | |
| 315 crashing_tid_(context->tid), | |
| 316 dumper_(crashing_pid) { | |
| 317 } | |
| 318 | |
| 319 bool Init() { | |
| 320 return dumper_.Init() && minidump_writer_.Open(filename_) && | |
| 321 dumper_.ThreadsSuspend(); | |
| 322 } | |
| 323 | |
| 324 ~MinidumpWriter() { | |
| 325 minidump_writer_.Close(); | |
| 326 dumper_.ThreadsResume(); | |
| 327 } | |
| 328 | |
| 329 bool Dump() { | |
| 330 // A minidump file contains a number of tagged streams. This is the number | |
| 331 // of stream which we write. | |
| 332 static const unsigned kNumWriters = 11; | |
| 333 | |
| 334 TypedMDRVA<MDRawHeader> header(&minidump_writer_); | |
| 335 TypedMDRVA<MDRawDirectory> dir(&minidump_writer_); | |
| 336 if (!header.Allocate()) | |
| 337 return false; | |
| 338 if (!dir.AllocateArray(kNumWriters)) | |
| 339 return false; | |
| 340 memset(header.get(), 0, sizeof(MDRawHeader)); | |
| 341 | |
| 342 header.get()->signature = MD_HEADER_SIGNATURE; | |
| 343 header.get()->version = MD_HEADER_VERSION; | |
| 344 header.get()->time_date_stamp = time(NULL); | |
| 345 header.get()->stream_count = kNumWriters; | |
| 346 header.get()->stream_directory_rva = dir.position(); | |
| 347 | |
| 348 unsigned dir_index = 0; | |
| 349 MDRawDirectory dirent; | |
| 350 | |
| 351 if (!WriteThreadListStream(&dirent)) | |
| 352 return false; | |
| 353 dir.CopyIndex(dir_index++, &dirent); | |
| 354 | |
| 355 if (!WriteMappings(&dirent)) | |
| 356 return false; | |
| 357 dir.CopyIndex(dir_index++, &dirent); | |
| 358 | |
| 359 if (!WriteExceptionStream(&dirent)) | |
| 360 return false; | |
| 361 dir.CopyIndex(dir_index++, &dirent); | |
| 362 | |
| 363 if (!WriteSystemInfoStream(&dirent)) | |
| 364 return false; | |
| 365 dir.CopyIndex(dir_index++, &dirent); | |
| 366 | |
| 367 dirent.stream_type = MD_LINUX_CPU_INFO; | |
| 368 if (!WriteFile(&dirent.location, "/proc/cpuinfo")) | |
| 369 NullifyDirectoryEntry(&dirent); | |
| 370 dir.CopyIndex(dir_index++, &dirent); | |
| 371 | |
| 372 dirent.stream_type = MD_LINUX_PROC_STATUS; | |
| 373 if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) | |
| 374 NullifyDirectoryEntry(&dirent); | |
| 375 dir.CopyIndex(dir_index++, &dirent); | |
| 376 | |
| 377 dirent.stream_type = MD_LINUX_LSB_RELEASE; | |
| 378 if (!WriteFile(&dirent.location, "/etc/lsb-release")) | |
| 379 NullifyDirectoryEntry(&dirent); | |
| 380 dir.CopyIndex(dir_index++, &dirent); | |
| 381 | |
| 382 dirent.stream_type = MD_LINUX_CMD_LINE; | |
| 383 if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) | |
| 384 NullifyDirectoryEntry(&dirent); | |
| 385 dir.CopyIndex(dir_index++, &dirent); | |
| 386 | |
| 387 dirent.stream_type = MD_LINUX_ENVIRON; | |
| 388 if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) | |
| 389 NullifyDirectoryEntry(&dirent); | |
| 390 dir.CopyIndex(dir_index++, &dirent); | |
| 391 | |
| 392 dirent.stream_type = MD_LINUX_AUXV; | |
| 393 if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) | |
| 394 NullifyDirectoryEntry(&dirent); | |
| 395 dir.CopyIndex(dir_index++, &dirent); | |
| 396 | |
| 397 dirent.stream_type = MD_LINUX_AUXV; | |
| 398 if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) | |
| 399 NullifyDirectoryEntry(&dirent); | |
| 400 dir.CopyIndex(dir_index++, &dirent); | |
| 401 | |
| 402 // If you add more directory entries, don't forget to update kNumWriters, | |
| 403 // above. | |
| 404 | |
| 405 dumper_.ThreadsResume(); | |
| 406 return true; | |
| 407 } | |
| 408 | |
| 409 // Write information about the threads. | |
| 410 bool WriteThreadListStream(MDRawDirectory* dirent) { | |
| 411 const unsigned num_threads = dumper_.threads().size(); | |
| 412 | |
| 413 TypedMDRVA<uint32_t> list(&minidump_writer_); | |
| 414 if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) | |
| 415 return false; | |
| 416 | |
| 417 dirent->stream_type = MD_THREAD_LIST_STREAM; | |
| 418 dirent->location = list.location(); | |
| 419 | |
| 420 *list.get() = num_threads; | |
| 421 | |
| 422 for (unsigned i = 0; i < num_threads; ++i) { | |
| 423 MDRawThread thread; | |
| 424 my_memset(&thread, 0, sizeof(thread)); | |
| 425 thread.thread_id = dumper_.threads()[i]; | |
| 426 // We have a different source of information for the crashing thread. If | |
| 427 // we used the actual state of the thread we would find it running in the | |
| 428 // signal handler with the alternative stack, which would be deeply | |
| 429 // unhelpful. | |
| 430 if (thread.thread_id == crashing_tid_) { | |
| 431 const void* stack; | |
| 432 size_t stack_len; | |
| 433 if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer())) | |
| 434 return false; | |
| 435 UntypedMDRVA memory(&minidump_writer_); | |
| 436 if (!memory.Allocate(stack_len)) | |
| 437 return false; | |
| 438 uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); | |
| 439 dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); | |
| 440 memory.Copy(stack_copy, stack_len); | |
| 441 thread.stack.start_of_memory_range = (uintptr_t) (stack); | |
| 442 thread.stack.memory = memory.location(); | |
| 443 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); | |
| 444 if (!cpu.Allocate()) | |
| 445 return false; | |
| 446 my_memset(cpu.get(), 0, sizeof(RawContextCPU)); | |
| 447 CPUFillFromUContext(cpu.get(), ucontext_, float_state_); | |
| 448 thread.thread_context = cpu.location(); | |
| 449 crashing_thread_context_ = cpu.location(); | |
| 450 } else { | |
| 451 ThreadInfo info; | |
| 452 if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info)) | |
| 453 return false; | |
| 454 UntypedMDRVA memory(&minidump_writer_); | |
| 455 if (!memory.Allocate(info.stack_len)) | |
| 456 return false; | |
| 457 uint8_t* stack_copy = | |
| 458 (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); | |
| 459 dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, | |
| 460 info.stack_len); | |
| 461 memory.Copy(stack_copy, info.stack_len); | |
| 462 thread.stack.start_of_memory_range = (uintptr_t)(info.stack); | |
| 463 thread.stack.memory = memory.location(); | |
| 464 TypedMDRVA<RawContextCPU> cpu(&minidump_writer_); | |
| 465 if (!cpu.Allocate()) | |
| 466 return false; | |
| 467 my_memset(cpu.get(), 0, sizeof(RawContextCPU)); | |
| 468 CPUFillFromThreadInfo(cpu.get(), info); | |
| 469 thread.thread_context = cpu.location(); | |
| 470 } | |
| 471 | |
| 472 list.CopyIndexAfterObject(i, &thread, sizeof(thread)); | |
| 473 } | |
| 474 | |
| 475 return true; | |
| 476 } | |
| 477 | |
| 478 static bool ShouldIncludeMapping(const MappingInfo& mapping) { | |
| 479 if (mapping.name[0] == 0 || // we only want modules with filenames. | |
| 480 mapping.offset || // we only want to include one mapping per shared lib. | |
| 481 mapping.size < 4096) { // too small to get a signature for. | |
| 482 return false; | |
| 483 } | |
| 484 | |
| 485 return true; | |
| 486 } | |
| 487 | |
| 488 // Write information about the mappings in effect. Because we are using the | |
| 489 // minidump format, the information about the mappings is pretty limited. | |
| 490 // Because of this, we also include the full, unparsed, /proc/$x/maps file in | |
| 491 // another stream in the file. | |
| 492 bool WriteMappings(MDRawDirectory* dirent) { | |
| 493 const unsigned num_mappings = dumper_.mappings().size(); | |
| 494 unsigned num_output_mappings = 0; | |
| 495 | |
| 496 for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { | |
| 497 const MappingInfo& mapping = *dumper_.mappings()[i]; | |
| 498 if (ShouldIncludeMapping(mapping)) | |
| 499 num_output_mappings++; | |
| 500 } | |
| 501 | |
| 502 TypedMDRVA<uint32_t> list(&minidump_writer_); | |
| 503 if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) | |
| 504 return false; | |
| 505 | |
| 506 dirent->stream_type = MD_MODULE_LIST_STREAM; | |
| 507 dirent->location = list.location(); | |
| 508 *list.get() = num_output_mappings; | |
| 509 | |
| 510 for (unsigned i = 0, j = 0; i < num_mappings; ++i) { | |
| 511 const MappingInfo& mapping = *dumper_.mappings()[i]; | |
| 512 if (!ShouldIncludeMapping(mapping)) | |
| 513 continue; | |
| 514 | |
| 515 MDRawModule mod; | |
| 516 my_memset(&mod, 0, MD_MODULE_SIZE); | |
| 517 mod.base_of_image = mapping.start_addr; | |
| 518 mod.size_of_image = mapping.size; | |
| 519 const size_t filepath_len = my_strlen(mapping.name); | |
| 520 | |
| 521 // Figure out file name from path | |
| 522 const char* filename_ptr = mapping.name + filepath_len - 1; | |
| 523 while (filename_ptr >= mapping.name) { | |
| 524 if (*filename_ptr == '/') | |
| 525 break; | |
| 526 filename_ptr--; | |
| 527 } | |
| 528 filename_ptr++; | |
| 529 const size_t filename_len = mapping.name + filepath_len - filename_ptr; | |
| 530 | |
| 531 uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; | |
| 532 uint8_t* cv_ptr = cv_buf; | |
| 533 UntypedMDRVA cv(&minidump_writer_); | |
| 534 if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) | |
| 535 return false; | |
| 536 | |
| 537 const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; | |
| 538 memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); | |
| 539 cv_ptr += sizeof(cv_signature); | |
| 540 | |
| 541 { | |
| 542 // We XOR the first page of the file to get a signature for it. | |
| 543 uint8_t xor_buf[sizeof(MDGUID)]; | |
| 544 size_t done = 0; | |
| 545 uint8_t* signature = cv_ptr; | |
| 546 cv_ptr += sizeof(xor_buf); | |
| 547 | |
| 548 my_memset(signature, 0, sizeof(xor_buf)); | |
| 549 while (done < 4096) { | |
| 550 dumper_.CopyFromProcess(xor_buf, crashing_tid_, | |
| 551 (void *) (mod.base_of_image + done), | |
| 552 sizeof(xor_buf)); | |
| 553 for (unsigned i = 0; i < sizeof(xor_buf); ++i) | |
| 554 signature[i] ^= xor_buf[i]; | |
| 555 done += sizeof(xor_buf); | |
| 556 } | |
| 557 my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. | |
| 558 cv_ptr += sizeof(uint32_t); | |
| 559 } | |
| 560 | |
| 561 // Write pdb_file_name | |
| 562 memcpy(cv_ptr, filename_ptr, filename_len + 1); | |
| 563 cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); | |
| 564 | |
| 565 mod.cv_record = cv.location(); | |
| 566 | |
| 567 MDLocationDescriptor ld; | |
| 568 if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) | |
| 569 return false; | |
| 570 mod.module_name_rva = ld.rva; | |
| 571 | |
| 572 list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); | |
| 573 } | |
| 574 | |
| 575 return true; | |
| 576 } | |
| 577 | |
| 578 bool WriteExceptionStream(MDRawDirectory* dirent) { | |
| 579 TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_); | |
| 580 if (!exc.Allocate()) | |
| 581 return false; | |
| 582 my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); | |
| 583 | |
| 584 dirent->stream_type = MD_EXCEPTION_STREAM; | |
| 585 dirent->location = exc.location(); | |
| 586 | |
| 587 exc.get()->thread_id = crashing_tid_; | |
| 588 exc.get()->exception_record.exception_code = siginfo_->si_signo; | |
| 589 exc.get()->exception_record.exception_address = | |
| 590 (uintptr_t) siginfo_->si_addr; | |
| 591 exc.get()->thread_context = crashing_thread_context_; | |
| 592 | |
| 593 return true; | |
| 594 } | |
| 595 | |
| 596 bool WriteSystemInfoStream(MDRawDirectory* dirent) { | |
| 597 TypedMDRVA<MDRawSystemInfo> si(&minidump_writer_); | |
| 598 if (!si.Allocate()) | |
| 599 return false; | |
| 600 my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); | |
| 601 | |
| 602 dirent->stream_type = MD_SYSTEM_INFO_STREAM; | |
| 603 dirent->location = si.location(); | |
| 604 | |
| 605 WriteCPUInformation(si.get()); | |
| 606 WriteOSInformation(si.get()); | |
| 607 | |
| 608 return true; | |
| 609 } | |
| 610 | |
| 611 private: | |
| 612 #if defined(__i386) | |
| 613 uintptr_t GetStackPointer() { | |
| 614 return ucontext_->uc_mcontext.gregs[REG_ESP]; | |
| 615 } | |
| 616 #elif defined(__x86_64) | |
| 617 uintptr_t GetStackPointer() { | |
| 618 return ucontext_->uc_mcontext.gregs[REG_RSP]; | |
| 619 } | |
| 620 #else | |
| 621 #error "This code has not been ported to your platform yet." | |
| 622 #endif | |
| 623 | |
| 624 void NullifyDirectoryEntry(MDRawDirectory* dirent) { | |
| 625 dirent->stream_type = 0; | |
| 626 dirent->location.data_size = 0; | |
| 627 dirent->location.rva = 0; | |
| 628 } | |
| 629 | |
| 630 bool WriteCPUInformation(MDRawSystemInfo* sys_info) { | |
| 631 char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; | |
| 632 static const char vendor_id_name[] = "vendor_id"; | |
| 633 static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; | |
| 634 | |
| 635 struct CpuInfoEntry { | |
| 636 const char* info_name; | |
| 637 int value; | |
| 638 bool found; | |
| 639 } cpu_info_table[] = { | |
| 640 { "processor", -1, false }, | |
| 641 { "model", 0, false }, | |
| 642 { "stepping", 0, false }, | |
| 643 { "cpu family", 0, false }, | |
| 644 }; | |
| 645 | |
| 646 // processor_architecture should always be set, do this first | |
| 647 sys_info->processor_architecture = | |
| 648 #if defined(__i386) | |
| 649 MD_CPU_ARCHITECTURE_X86; | |
| 650 #elif defined(__x86_64) | |
| 651 MD_CPU_ARCHITECTURE_AMD64; | |
| 652 #else | |
| 653 #error "Unknown CPU arch" | |
| 654 #endif | |
| 655 | |
| 656 const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); | |
| 657 if (fd < 0) | |
| 658 return false; | |
| 659 | |
| 660 { | |
| 661 PageAllocator allocator; | |
| 662 LineReader* const line_reader = new(allocator) LineReader(fd); | |
| 663 const char* line; | |
| 664 unsigned line_len; | |
| 665 while (line_reader->GetNextLine(&line, &line_len)) { | |
| 666 for (size_t i = 0; | |
| 667 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); | |
| 668 i++) { | |
| 669 CpuInfoEntry* entry = &cpu_info_table[i]; | |
| 670 if (entry->found) | |
| 671 continue; | |
| 672 if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { | |
| 673 const char* value = strchr(line, ':'); | |
| 674 if (!value) | |
| 675 continue; | |
| 676 | |
| 677 // the above strncmp only matches the prefix, it might be the wrong | |
| 678 // line. i.e. we matched "model name" instead of "model". | |
| 679 // check and make sure there is only spaces between the prefix and | |
| 680 // the colon. | |
| 681 const char* space_ptr = line + strlen(entry->info_name); | |
| 682 for (; space_ptr < value; space_ptr++) { | |
| 683 if (!isspace(*space_ptr)) { | |
| 684 break; | |
| 685 } | |
| 686 } | |
| 687 if (space_ptr != value) | |
| 688 continue; | |
| 689 | |
| 690 sscanf(++value, " %d", &(entry->value)); | |
| 691 entry->found = true; | |
| 692 } | |
| 693 } | |
| 694 | |
| 695 // special case for vendor_id | |
| 696 if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { | |
| 697 const char* value = strchr(line, ':'); | |
| 698 if (!value) | |
| 699 goto popline; | |
| 700 | |
| 701 // skip ':" and all the spaces that follows | |
| 702 do { | |
| 703 value++; | |
| 704 } while (isspace(*value)); | |
| 705 | |
| 706 if (*value) { | |
| 707 size_t length = strlen(value); | |
| 708 if (length == 0) | |
| 709 goto popline; | |
| 710 // we don't want the trailing newline | |
| 711 if (value[length - 1] == '\n') | |
| 712 length--; | |
| 713 // ensure we have space for the value | |
| 714 if (length < sizeof(vendor_id)) | |
| 715 strncpy(vendor_id, value, length); | |
| 716 } | |
| 717 } | |
| 718 | |
| 719 popline: | |
| 720 line_reader->PopLine(line_len); | |
| 721 } | |
| 722 sys_close(fd); | |
| 723 } | |
| 724 | |
| 725 // make sure we got everything we wanted | |
| 726 for (size_t i = 0; | |
| 727 i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); | |
| 728 i++) { | |
| 729 if (!cpu_info_table[i].found) { | |
| 730 return false; | |
| 731 } | |
| 732 } | |
| 733 // /proc/cpuinfo contains cpu id, change it into number by adding one. | |
| 734 cpu_info_table[0].value++; | |
| 735 | |
| 736 sys_info->number_of_processors = cpu_info_table[0].value; | |
| 737 sys_info->processor_level = cpu_info_table[3].value; | |
| 738 sys_info->processor_revision = cpu_info_table[1].value << 8 | | |
| 739 cpu_info_table[2].value; | |
| 740 | |
| 741 if (vendor_id[0] != '\0') { | |
| 742 memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, | |
| 743 sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); | |
| 744 } | |
| 745 return true; | |
| 746 } | |
| 747 | |
| 748 bool WriteFile(MDLocationDescriptor* result, const char* filename) { | |
| 749 const int fd = sys_open(filename, O_RDONLY, 0); | |
| 750 if (fd < 0) | |
| 751 return false; | |
| 752 | |
| 753 // We can't stat the files because several of the files that we want to | |
| 754 // read are kernel seqfiles, which always have a length of zero. So we have | |
| 755 // to read as much as we can into a buffer. | |
| 756 static const unsigned kMaxFileSize = 1024; | |
| 757 uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize); | |
| 758 | |
| 759 size_t done = 0; | |
| 760 while (done < kMaxFileSize) { | |
| 761 ssize_t r; | |
| 762 do { | |
| 763 r = sys_read(fd, data + done, kMaxFileSize - done); | |
| 764 } while (r == -1 && errno == EINTR); | |
| 765 | |
| 766 if (r < 1) | |
| 767 break; | |
| 768 done += r; | |
| 769 } | |
| 770 sys_close(fd); | |
| 771 | |
| 772 if (!done) | |
| 773 return false; | |
| 774 | |
| 775 UntypedMDRVA memory(&minidump_writer_); | |
| 776 if (!memory.Allocate(done)) | |
| 777 return false; | |
| 778 memory.Copy(data, done); | |
| 779 *result = memory.location(); | |
| 780 return true; | |
| 781 } | |
| 782 | |
| 783 bool WriteOSInformation(MDRawSystemInfo* sys_info) { | |
| 784 sys_info->platform_id = MD_OS_LINUX; | |
| 785 | |
| 786 struct utsname uts; | |
| 787 if (uname(&uts)) | |
| 788 return false; | |
| 789 | |
| 790 static const size_t buf_len = 512; | |
| 791 char buf[buf_len] = {0}; | |
| 792 size_t space_left = buf_len - 1; | |
| 793 const char* info_table[] = { | |
| 794 uts.sysname, | |
| 795 uts.release, | |
| 796 uts.version, | |
| 797 uts.machine, | |
| 798 NULL | |
| 799 }; | |
| 800 bool first_item = true; | |
| 801 for (const char** cur_info = info_table; *cur_info; cur_info++) { | |
| 802 static const char* separator = " "; | |
| 803 size_t separator_len = strlen(separator); | |
| 804 size_t info_len = strlen(*cur_info); | |
| 805 if (info_len == 0) | |
| 806 continue; | |
| 807 | |
| 808 if (space_left < info_len + (first_item ? 0 : separator_len)) | |
| 809 break; | |
| 810 | |
| 811 if (!first_item) { | |
| 812 strcat(buf, separator); | |
| 813 space_left -= separator_len; | |
| 814 } | |
| 815 | |
| 816 first_item = false; | |
| 817 strcat(buf, *cur_info); | |
| 818 space_left -= info_len; | |
| 819 } | |
| 820 | |
| 821 MDLocationDescriptor location; | |
| 822 if (!minidump_writer_.WriteString(buf, 0, &location)) | |
| 823 return false; | |
| 824 sys_info->csd_version_rva = location.rva; | |
| 825 | |
| 826 return true; | |
| 827 } | |
| 828 | |
| 829 bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, | |
| 830 const char* filename) { | |
| 831 char buf[80]; | |
| 832 memcpy(buf, "/proc/", 6); | |
| 833 const unsigned pid_len = my_int_len(pid); | |
| 834 my_itos(buf + 6, pid, pid_len); | |
| 835 buf[6 + pid_len] = '/'; | |
| 836 memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1); | |
| 837 return WriteFile(result, buf); | |
| 838 } | |
| 839 | |
| 840 const char* const filename_; // output filename | |
| 841 const siginfo_t* const siginfo_; // from the signal handler (see sigaction) | |
| 842 const struct ucontext* const ucontext_; // also from the signal handler | |
| 843 const struct _libc_fpstate* const float_state_; // ditto | |
| 844 const pid_t crashing_tid_; // the process which actually crashed | |
| 845 LinuxDumper dumper_; | |
| 846 MinidumpFileWriter minidump_writer_; | |
| 847 MDLocationDescriptor crashing_thread_context_; | |
| 848 }; | |
| 849 | |
| 850 bool WriteMinidump(const char* filename, pid_t crashing_process, | |
| 851 const void* blob, size_t blob_size) { | |
| 852 if (blob_size != sizeof(ExceptionHandler::CrashContext)) | |
| 853 return false; | |
| 854 const ExceptionHandler::CrashContext* context = | |
| 855 reinterpret_cast<const ExceptionHandler::CrashContext*>(blob); | |
| 856 MinidumpWriter writer(filename, crashing_process, context); | |
| 857 if (!writer.Init()) | |
| 858 return false; | |
| 859 return writer.Dump(); | |
| 860 } | |
| 861 | |
| 862 } // namespace google_breakpad | |
| OLD | NEW |