1 // Copyright (c) 2012, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
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.
30 // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
31 // See linux_core_dumper.h for details.
33 #include "client/linux/minidump_writer/linux_core_dumper.h"
35 #include <asm/ptrace.h>
40 #include <sys/procfs.h>
42 #include "common/linux/linux_libc_support.h"
44 namespace google_breakpad {
46 LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
47 const char* core_path,
48 const char* procfs_path)
50 core_path_(core_path),
51 procfs_path_(procfs_path),
52 thread_infos_(&allocator_, 8) {
56 bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
57 const char* node) const {
61 size_t node_len = my_strlen(node);
65 size_t procfs_path_len = my_strlen(procfs_path_);
66 size_t total_length = procfs_path_len + 1 + node_len;
67 if (total_length >= NAME_MAX)
70 memcpy(path, procfs_path_, procfs_path_len);
71 path[procfs_path_len] = '/';
72 memcpy(path + procfs_path_len + 1, node, node_len);
73 path[total_length] = '\0';
77 void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
78 const void* src, size_t length) {
79 ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
80 // TODO(benchan): Investigate whether the data to be copied could span
81 // across multiple segments in the core dump file. ElfCoreDump::CopyData
82 // and this method do not handle that case yet.
83 if (!core_.CopyData(dest, virtual_address, length)) {
84 // If the data segment is not found in the core dump, fill the result
85 // with marker characters.
86 memset(dest, 0xab, length);
90 bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
91 if (index >= thread_infos_.size())
94 *info = thread_infos_[index];
95 const uint8_t* stack_pointer;
97 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
98 #elif defined(__x86_64)
99 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
100 #elif defined(__ARM_EABI__)
101 memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
102 #elif defined(__aarch64__)
103 memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
104 #elif defined(__mips__)
106 reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
108 #error "This code hasn't been ported to your platform yet."
110 info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
114 bool LinuxCoreDumper::IsPostMortem() const {
118 bool LinuxCoreDumper::ThreadsSuspend() {
122 bool LinuxCoreDumper::ThreadsResume() {
126 bool LinuxCoreDumper::EnumerateThreads() {
127 if (!mapped_core_file_.Map(core_path_)) {
128 fprintf(stderr, "Could not map core dump file into memory\n");
132 core_.SetContent(mapped_core_file_.content());
133 if (!core_.IsValid()) {
134 fprintf(stderr, "Invalid core dump file\n");
138 ElfCoreDump::Note note = core_.GetFirstNote();
139 if (!note.IsValid()) {
140 fprintf(stderr, "PT_NOTE section not found\n");
144 bool first_thread = true;
146 ElfCoreDump::Word type = note.GetType();
147 MemoryRange name = note.GetName();
148 MemoryRange description = note.GetDescription();
150 if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
151 fprintf(stderr, "Could not found a valid PT_NOTE.\n");
155 // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
156 // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
158 // -------------------------------------------------------------------
159 // 1st thread CORE NT_PRSTATUS
160 // process-wide CORE NT_PRPSINFO
161 // process-wide CORE NT_AUXV
162 // 1st thread CORE NT_FPREGSET
163 // 1st thread LINUX NT_PRXFPREG
164 // 1st thread LINUX NT_386_TLS
166 // 2nd thread CORE NT_PRSTATUS
167 // 2nd thread CORE NT_FPREGSET
168 // 2nd thread LINUX NT_PRXFPREG
169 // 2nd thread LINUX NT_386_TLS
171 // 3rd thread CORE NT_PRSTATUS
172 // 3rd thread CORE NT_FPREGSET
173 // 3rd thread LINUX NT_PRXFPREG
174 // 3rd thread LINUX NT_386_TLS
176 // The following code only works if notes are ordered as expected.
179 if (description.length() != sizeof(elf_prstatus)) {
180 fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
184 const elf_prstatus* status =
185 reinterpret_cast<const elf_prstatus*>(description.data());
186 pid_t pid = status->pr_pid;
188 memset(&info, 0, sizeof(ThreadInfo));
189 info.tgid = status->pr_pgrp;
190 info.ppid = status->pr_ppid;
191 memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
194 crash_signal_ = status->pr_info.si_signo;
196 first_thread = false;
197 threads_.push_back(pid);
198 thread_infos_.push_back(info);
201 #if defined(__i386) || defined(__x86_64)
203 if (thread_infos_.empty())
206 ThreadInfo* info = &thread_infos_.back();
207 if (description.length() != sizeof(info->fpregs)) {
208 fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
212 memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
218 if (thread_infos_.empty())
221 ThreadInfo* info = &thread_infos_.back();
222 if (description.length() != sizeof(info->fpxregs)) {
223 fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
227 memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
232 note = note.GetNextNote();
233 } while (note.IsValid());
238 } // namespace google_breakpad