Index: breakpad/linux/minidump-2-core.cc |
diff --git a/breakpad/linux/minidump-2-core.cc b/breakpad/linux/minidump-2-core.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..095ae5f52a275eb5109b11f89ab7f4bd807e7582 |
--- /dev/null |
+++ b/breakpad/linux/minidump-2-core.cc |
@@ -0,0 +1,601 @@ |
+// Copyright (c) 2009, Google Inc. |
+// All rights reserved. |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+// Converts a minidump file to a core file which gdb can read. |
+// Large parts lifted from the userspace core dumper: |
+// http://code.google.com/p/google-coredumper/ |
+// |
+// Usage: minidump-2-core 1234.dmp > core |
+ |
+#include <vector> |
+ |
+#include <stdio.h> |
+ |
+#include <elf.h> |
+#include <errno.h> |
+#include <unistd.h> |
+#include <fcntl.h> |
+#include <sys/user.h> |
+#include <sys/mman.h> |
+ |
+#include "google_breakpad/common/minidump_format.h" |
+#include "google_breakpad/common/minidump_cpu_x86.h" |
+#include "breakpad/linux/minidump_format_linux.h" |
+ |
+#if __WORDSIZE == 64 |
+ #define ELF_CLASS ELFCLASS64 |
+ #define Ehdr Elf64_Ehdr |
+ #define Phdr Elf64_Phdr |
+ #define Shdr Elf64_Shdr |
+ #define Nhdr Elf64_Nhdr |
+ #define auxv_t Elf64_auxv_t |
+#else |
+ #define ELF_CLASS ELFCLASS32 |
+ #define Ehdr Elf32_Ehdr |
+ #define Phdr Elf32_Phdr |
+ #define Shdr Elf32_Shdr |
+ #define Nhdr Elf32_Nhdr |
+ #define auxv_t Elf32_auxv_t |
+#endif |
+ |
+ |
+#if defined(__x86_64__) |
+ #define ELF_ARCH EM_X86_64 |
+#elif defined(__i386__) |
+ #define ELF_ARCH EM_386 |
+#elif defined(__ARM_ARCH_3__) |
+ #define ELF_ARCH EM_ARM |
+#elif defined(__mips__) |
+ #define ELF_ARCH EM_MIPS |
+#endif |
+ |
+static int usage(const char* argv0) { |
+ fprintf(stderr, "Usage: %s <minidump file>\n", argv0); |
+ return 1; |
+} |
+ |
+// Write all of the given buffer, handling short writes and EINTR. Return true |
+// iff successful. |
+static bool |
+writea(int fd, const void* idata, size_t length) { |
+ const uint8_t* data = (const uint8_t*) idata; |
+ |
+ size_t done = 0; |
+ while (done < length) { |
+ ssize_t r; |
+ do { |
+ r = write(fd, data + done, length - done); |
+ } while (r == -1 && errno == EINTR); |
+ |
+ if (r < 1) |
+ return false; |
+ done += r; |
+ } |
+ |
+ return true; |
+} |
+ |
+// A range of a mmaped file. |
+class MMappedRange { |
+ public: |
+ MMappedRange(const void* data, size_t length) |
+ : data_(reinterpret_cast<const uint8_t*>(data)), |
+ length_(length) { |
+ } |
+ |
+ // Get an object of |length| bytes at |offset| and return a pointer to it |
+ // unless it's out of bounds. |
+ const void* GetObject(size_t offset, size_t length) { |
+ if (offset + length < offset) |
+ return NULL; |
+ if (offset + length > length_) |
+ return NULL; |
+ return data_ + offset; |
+ } |
+ |
+ // Get element |index| of an array of objects of length |length| starting at |
+ // |offset| bytes. Return NULL if out of bounds. |
+ const void* GetArrayElement(size_t offset, size_t length, unsigned index) { |
+ const size_t element_offset = offset + index * length; |
+ return GetObject(element_offset, length); |
+ } |
+ |
+ // Return a new range which is a subset of this range. |
+ MMappedRange Subrange(const MDLocationDescriptor& location) const { |
+ if (location.rva > length_ || |
+ location.rva + location.data_size < location.rva || |
+ location.rva + location.data_size > length_) { |
+ return MMappedRange(NULL, 0); |
+ } |
+ |
+ return MMappedRange(data_ + location.rva, location.data_size); |
+ } |
+ |
+ const uint8_t* data() const { return data_; } |
+ size_t length() const { return length_; } |
+ |
+ private: |
+ const uint8_t* const data_; |
+ const size_t length_; |
+}; |
+ |
+/* Dynamically determines the byte sex of the system. Returns non-zero |
+ * for big-endian machines. |
+ */ |
+static inline int sex() { |
+ int probe = 1; |
+ return !*(char *)&probe; |
+} |
+ |
+typedef struct elf_timeval { /* Time value with microsecond resolution */ |
+ long tv_sec; /* Seconds */ |
+ long tv_usec; /* Microseconds */ |
+} elf_timeval; |
+ |
+typedef struct elf_siginfo { /* Information about signal (unused) */ |
+ int32_t si_signo; /* Signal number */ |
+ int32_t si_code; /* Extra code */ |
+ int32_t si_errno; /* Errno */ |
+} elf_siginfo; |
+ |
+typedef struct prstatus { /* Information about thread; includes CPU reg*/ |
+ elf_siginfo pr_info; /* Info associated with signal */ |
+ uint16_t pr_cursig; /* Current signal */ |
+ unsigned long pr_sigpend; /* Set of pending signals */ |
+ unsigned long pr_sighold; /* Set of held signals */ |
+ pid_t pr_pid; /* Process ID */ |
+ pid_t pr_ppid; /* Parent's process ID */ |
+ pid_t pr_pgrp; /* Group ID */ |
+ pid_t pr_sid; /* Session ID */ |
+ elf_timeval pr_utime; /* User time */ |
+ elf_timeval pr_stime; /* System time */ |
+ elf_timeval pr_cutime; /* Cumulative user time */ |
+ elf_timeval pr_cstime; /* Cumulative system time */ |
+ user_regs_struct pr_reg; /* CPU registers */ |
+ uint32_t pr_fpvalid; /* True if math co-processor being used */ |
+} prstatus; |
+ |
+typedef struct prpsinfo { /* Information about process */ |
+ unsigned char pr_state; /* Numeric process state */ |
+ char pr_sname; /* Char for pr_state */ |
+ unsigned char pr_zomb; /* Zombie */ |
+ signed char pr_nice; /* Nice val */ |
+ unsigned long pr_flag; /* Flags */ |
+#if defined(__x86_64__) || defined(__mips__) |
+ uint32_t pr_uid; /* User ID */ |
+ uint32_t pr_gid; /* Group ID */ |
+#else |
+ uint16_t pr_uid; /* User ID */ |
+ uint16_t pr_gid; /* Group ID */ |
+#endif |
+ pid_t pr_pid; /* Process ID */ |
+ pid_t pr_ppid; /* Parent's process ID */ |
+ pid_t pr_pgrp; /* Group ID */ |
+ pid_t pr_sid; /* Session ID */ |
+ char pr_fname[16]; /* Filename of executable */ |
+ char pr_psargs[80]; /* Initial part of arg list */ |
+} prpsinfo; |
+ |
+// We parse the minidump file and keep the parsed information in this structure. |
+struct CrashedProcess { |
+ CrashedProcess() |
+ : crashing_tid(-1), |
+ auxv(NULL), |
+ auxv_length(0) { |
+ memset(&prps, 0, sizeof(prps)); |
+ prps.pr_sname = 'R'; |
+ } |
+ |
+ struct Mapping { |
+ uint64_t start_address, end_address; |
+ }; |
+ std::vector<Mapping> mappings; |
+ |
+ pid_t crashing_tid; |
+ int fatal_signal; |
+ |
+ struct Thread { |
+ pid_t tid; |
+ user_regs_struct regs; |
+ user_fpregs_struct fpregs; |
+ user_fpxregs_struct fpxregs; |
+ uintptr_t stack_addr; |
+ const uint8_t* stack; |
+ size_t stack_length; |
+ }; |
+ std::vector<Thread> threads; |
+ |
+ const uint8_t* auxv; |
+ size_t auxv_length; |
+ |
+ prpsinfo prps; |
+}; |
+ |
+static uint32_t |
+U32(const uint8_t* data) { |
+ uint32_t v; |
+ memcpy(&v, data, sizeof(v)); |
+ return v; |
+} |
+ |
+static uint16_t |
+U16(const uint8_t* data) { |
+ uint16_t v; |
+ memcpy(&v, data, sizeof(v)); |
+ return v; |
+} |
+ |
+#if defined(__i386__) |
+static void |
+ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) { |
+ const MDRawContextX86* rawregs = |
+ (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86)); |
+ |
+ thread->regs.ebx = rawregs->ebx; |
+ thread->regs.ecx = rawregs->ecx; |
+ thread->regs.edx = rawregs->edx; |
+ thread->regs.esi = rawregs->esi; |
+ thread->regs.edi = rawregs->edi; |
+ thread->regs.ebp = rawregs->ebp; |
+ thread->regs.eax = rawregs->eax; |
+ thread->regs.xds = rawregs->ds; |
+ thread->regs.xes = rawregs->es; |
+ thread->regs.xfs = rawregs->fs; |
+ thread->regs.xgs = rawregs->gs; |
+ thread->regs.orig_eax = rawregs->eax; |
+ thread->regs.eip = rawregs->eip; |
+ thread->regs.xcs = rawregs->cs; |
+ thread->regs.eflags = rawregs->eflags; |
+ thread->regs.esp = rawregs->esp; |
+ thread->regs.xss = rawregs->ss; |
+ |
+ thread->fpregs.cwd = rawregs->float_save.control_word; |
+ thread->fpregs.swd = rawregs->float_save.status_word; |
+ thread->fpregs.twd = rawregs->float_save.tag_word; |
+ thread->fpregs.fip = rawregs->float_save.error_offset; |
+ thread->fpregs.fcs = rawregs->float_save.error_selector; |
+ thread->fpregs.foo = rawregs->float_save.data_offset; |
+ thread->fpregs.fos = rawregs->float_save.data_selector; |
+ memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, |
+ 10 * 8); |
+ |
+ thread->fpxregs.cwd = rawregs->float_save.control_word; |
+ thread->fpxregs.swd = rawregs->float_save.status_word; |
+ thread->fpxregs.twd = rawregs->float_save.tag_word; |
+ thread->fpxregs.fop = U16(rawregs->extended_registers + 6); |
+ thread->fpxregs.fip = U16(rawregs->extended_registers + 8); |
+ thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); |
+ thread->fpxregs.foo = U16(rawregs->extended_registers + 16); |
+ thread->fpxregs.fos = U16(rawregs->extended_registers + 20); |
+ thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); |
+ memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); |
+ memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); |
+} |
+#else |
+#error "This code has not been ported to your platform yet" |
+#endif |
+ |
+static void |
+ParseThreadList(CrashedProcess* crashinfo, MMappedRange range, |
+ const MMappedRange& full_file) { |
+ const uint32_t num_threads = |
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); |
+ for (unsigned i = 0; i < num_threads; ++i) { |
+ CrashedProcess::Thread thread; |
+ memset(&thread, 0, sizeof(thread)); |
+ const MDRawThread* rawthread = |
+ (MDRawThread*) range.GetArrayElement(sizeof(uint32_t), |
+ sizeof(MDRawThread), i); |
+ thread.tid = rawthread->thread_id; |
+ thread.stack_addr = rawthread->stack.start_of_memory_range; |
+ MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory); |
+ thread.stack = stack_range.data(); |
+ thread.stack_length = rawthread->stack.memory.data_size; |
+ |
+ ParseThreadRegisters(&thread, |
+ full_file.Subrange(rawthread->thread_context)); |
+ |
+ crashinfo->threads.push_back(thread); |
+ } |
+} |
+ |
+static void |
+ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) { |
+ crashinfo->auxv = range.data(); |
+ crashinfo->auxv_length = range.length(); |
+} |
+ |
+static void |
+ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) { |
+ const char* cmdline = (const char*) range.data(); |
+ for (size_t i = 0; i < range.length(); ++i) { |
+ if (cmdline[i] == 0) { |
+ static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; |
+ static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; |
+ memset(crashinfo->prps.pr_fname, 0, fname_len + 1); |
+ memset(crashinfo->prps.pr_psargs, 0, args_len + 1); |
+ const char* binary_name = strrchr(cmdline, '/'); |
+ if (binary_name) { |
+ binary_name++; |
+ const unsigned len = strlen(binary_name); |
+ memcpy(crashinfo->prps.pr_fname, binary_name, |
+ len > fname_len ? fname_len : len); |
+ } else { |
+ memcpy(crashinfo->prps.pr_fname, cmdline, |
+ i > fname_len ? fname_len : i); |
+ } |
+ |
+ const unsigned len = range.length() > args_len ? |
+ args_len : range.length(); |
+ memcpy(crashinfo->prps.pr_psargs, cmdline, len); |
+ for (unsigned i = 0; i < len; ++i) { |
+ if (crashinfo->prps.pr_psargs[i] == 0) |
+ crashinfo->prps.pr_psargs[i] = ' '; |
+ } |
+ } |
+ } |
+} |
+ |
+static void |
+ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) { |
+ const MDRawExceptionStream* exp = |
+ (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream)); |
+ crashinfo->crashing_tid = exp->thread_id; |
+ crashinfo->fatal_signal = (int) exp->exception_record.exception_code; |
+} |
+ |
+static bool |
+WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { |
+ struct prstatus pr; |
+ memset(&pr, 0, sizeof(pr)); |
+ |
+ pr.pr_info.si_signo = fatal_signal; |
+ pr.pr_cursig = fatal_signal; |
+ pr.pr_pid = thread.tid; |
+ memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); |
+ |
+ Nhdr nhdr; |
+ memset(&nhdr, 0, sizeof(nhdr)); |
+ nhdr.n_namesz = 5; |
+ nhdr.n_descsz = sizeof(struct prstatus); |
+ nhdr.n_type = NT_PRSTATUS; |
+ if (!writea(1, &nhdr, sizeof(nhdr)) || |
+ !writea(1, "CORE\0\0\0\0", 8) || |
+ !writea(1, &pr, sizeof(struct prstatus))) { |
+ return false; |
+ } |
+ |
+ nhdr.n_descsz = sizeof(user_fpregs_struct); |
+ nhdr.n_type = NT_FPREGSET; |
+ if (!writea(1, &nhdr, sizeof(nhdr)) || |
+ !writea(1, "CORE\0\0\0\0", 8) || |
+ !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { |
+ return false; |
+ } |
+ |
+ nhdr.n_descsz = sizeof(user_fpxregs_struct); |
+ nhdr.n_type = NT_PRXFPREG; |
+ if (!writea(1, &nhdr, sizeof(nhdr)) || |
+ !writea(1, "LINUX\0\0\0", 8) || |
+ !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+static void |
+ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) { |
+ const uint32_t num_mappings = |
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); |
+ for (unsigned i = 0; i < num_mappings; ++i) { |
+ CrashedProcess::Mapping mapping; |
+ const MDRawModule* rawmodule = |
+ (MDRawModule*) range.GetArrayElement(sizeof(uint32_t), |
+ sizeof(MDRawModule), i); |
+ mapping.start_address = rawmodule->base_of_image; |
+ mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; |
+ |
+ crashinfo->mappings.push_back(mapping); |
+ } |
+} |
+ |
+int |
+main(int argc, char** argv) { |
+ if (argc != 2) |
+ return usage(argv[0]); |
+ |
+ const int fd = open(argv[1], O_RDONLY); |
+ if (fd < 0) |
+ return usage(argv[0]); |
+ |
+ struct stat st; |
+ fstat(fd, &st); |
+ |
+ const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); |
+ close(fd); |
+ if (bytes == MAP_FAILED) { |
+ perror("Failed to mmap dump file"); |
+ return 1; |
+ } |
+ |
+ MMappedRange dump(bytes, st.st_size); |
+ |
+ const MDRawHeader* header = |
+ (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader)); |
+ |
+ CrashedProcess crashinfo; |
+ |
+ for (unsigned i = 0; i < header->stream_count; ++i) { |
+ const MDRawDirectory* dirent = |
+ (const MDRawDirectory*) dump.GetArrayElement( |
+ header->stream_directory_rva, sizeof(MDRawDirectory), i); |
+ switch (dirent->stream_type) { |
+ case MD_THREAD_LIST_STREAM: |
+ ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); |
+ break; |
+ case MD_LINUX_AUXV: |
+ ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); |
+ break; |
+ case MD_LINUX_CMD_LINE: |
+ ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); |
+ break; |
+ case MD_EXCEPTION_STREAM: |
+ ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); |
+ break; |
+ case MD_MODULE_LIST_STREAM: |
+ ParseModuleStream(&crashinfo, dump.Subrange(dirent->location)); |
+ default: |
+ fprintf(stderr, "Skipping %x\n", dirent->stream_type); |
+ } |
+ } |
+ |
+ // Write the ELF header. The file will look like: |
+ // ELF header |
+ // Phdr for the PT_NOTE |
+ // Phdr for each of the thread stacks |
+ // PT_NOTE |
+ // each of the thread stacks |
+ Ehdr ehdr; |
+ memset(&ehdr, 0, sizeof(Ehdr)); |
+ ehdr.e_ident[0] = ELFMAG0; |
+ ehdr.e_ident[1] = ELFMAG1; |
+ ehdr.e_ident[2] = ELFMAG2; |
+ ehdr.e_ident[3] = ELFMAG3; |
+ ehdr.e_ident[4] = ELF_CLASS; |
+ ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; |
+ ehdr.e_ident[6] = EV_CURRENT; |
+ ehdr.e_type = ET_CORE; |
+ ehdr.e_machine = ELF_ARCH; |
+ ehdr.e_version = EV_CURRENT; |
+ ehdr.e_phoff = sizeof(Ehdr); |
+ ehdr.e_ehsize = sizeof(Ehdr); |
+ ehdr.e_phentsize= sizeof(Phdr); |
+ ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size(); |
+ ehdr.e_shentsize= sizeof(Shdr); |
+ if (!writea(1, &ehdr, sizeof(Ehdr))) |
+ return 1; |
+ |
+ size_t offset = sizeof(Ehdr) + |
+ (1 + crashinfo.threads.size() + |
+ crashinfo.mappings.size()) * sizeof(Phdr); |
+ size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + |
+ // sizeof(Nhdr) + 8 + sizeof(user) + |
+ sizeof(Nhdr) + 8 + crashinfo.auxv_length + |
+ crashinfo.threads.size() * ( |
+ (sizeof(Nhdr) + 8 + sizeof(prstatus)) + |
+ sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) + |
+ sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)); |
+ |
+ Phdr phdr; |
+ memset(&phdr, 0, sizeof(Phdr)); |
+ phdr.p_type = PT_NOTE; |
+ phdr.p_offset = offset; |
+ phdr.p_filesz = filesz; |
+ if (!writea(1, &phdr, sizeof(phdr))) |
+ return 1; |
+ |
+ phdr.p_type = PT_LOAD; |
+ phdr.p_align = getpagesize(); |
+ size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); |
+ if (note_align == phdr.p_align) |
+ note_align = 0; |
+ offset += note_align; |
+ |
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { |
+ const CrashedProcess::Thread& thread = crashinfo.threads[i]; |
+ offset += filesz; |
+ filesz = thread.stack_length; |
+ phdr.p_offset = offset; |
+ phdr.p_vaddr = thread.stack_addr; |
+ phdr.p_filesz = phdr.p_memsz = filesz; |
+ phdr.p_flags = PF_R | PF_W; |
+ if (!writea(1, &phdr, sizeof(phdr))) |
+ return 1; |
+ } |
+ |
+ for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) { |
+ const CrashedProcess::Mapping& mapping = crashinfo.mappings[i]; |
+ phdr.p_offset = 0; |
+ phdr.p_vaddr = mapping.start_address; |
+ phdr.p_filesz = 0; |
+ phdr.p_flags = PF_R; |
+ phdr.p_memsz = mapping.end_address - mapping.start_address; |
+ if (!writea(1, &phdr, sizeof(phdr))) |
+ return 1; |
+ } |
+ |
+ Nhdr nhdr; |
+ memset(&nhdr, 0, sizeof(nhdr)); |
+ nhdr.n_namesz = 5; |
+ nhdr.n_descsz = sizeof(prpsinfo); |
+ nhdr.n_type = NT_PRPSINFO; |
+ if (!writea(1, &nhdr, sizeof(nhdr)) || |
+ !writea(1, "CORE\0\0\0\0", 8) || |
+ !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { |
+ return 1; |
+ } |
+ |
+ nhdr.n_descsz = crashinfo.auxv_length; |
+ nhdr.n_type = NT_AUXV; |
+ if (!writea(1, &nhdr, sizeof(nhdr)) || |
+ !writea(1, "CORE\0\0\0\0", 8) || |
+ !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) { |
+ return 1; |
+ } |
+ |
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { |
+ if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { |
+ WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); |
+ break; |
+ } |
+ } |
+ |
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { |
+ if (crashinfo.threads[i].tid != crashinfo.crashing_tid) |
+ WriteThread(crashinfo.threads[i], 0); |
+ } |
+ |
+ if (note_align) { |
+ char scratch[note_align]; |
+ memset(scratch, 0, sizeof(scratch)); |
+ if (!writea(1, scratch, sizeof(scratch))) |
+ return 1; |
+ } |
+ |
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { |
+ const CrashedProcess::Thread& thread = crashinfo.threads[i]; |
+ if (!writea(1, thread.stack, thread.stack_length)) |
+ return 1; |
+ } |
+ |
+ munmap(const_cast<void*>(bytes), st.st_size); |
+ |
+ return 0; |
+} |