Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / breakpad / src / client / linux / minidump_writer / linux_core_dumper.cc
1 // Copyright (c) 2012, 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 // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
31 // See linux_core_dumper.h for details.
32
33 #include "client/linux/minidump_writer/linux_core_dumper.h"
34
35 #include <asm/ptrace.h>
36 #include <assert.h>
37 #include <elf.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/procfs.h>
41
42 #include "common/linux/linux_libc_support.h"
43
44 namespace google_breakpad {
45
46 LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
47                                  const char* core_path,
48                                  const char* procfs_path)
49     : LinuxDumper(pid),
50       core_path_(core_path),
51       procfs_path_(procfs_path),
52       thread_infos_(&allocator_, 8) {
53   assert(core_path_);
54 }
55
56 bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
57                                     const char* node) const {
58   if (!path || !node)
59     return false;
60
61   size_t node_len = my_strlen(node);
62   if (node_len == 0)
63     return false;
64
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)
68     return false;
69
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';
74   return true;
75 }
76
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);
87   }
88 }
89
90 bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
91   if (index >= thread_infos_.size())
92     return false;
93
94   *info = thread_infos_[index];
95   const uint8_t* stack_pointer;
96 #if defined(__i386)
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__)
105   stack_pointer =
106       reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
107 #else
108 #error "This code hasn't been ported to your platform yet."
109 #endif
110   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
111   return true;
112 }
113
114 bool LinuxCoreDumper::IsPostMortem() const {
115   return true;
116 }
117
118 bool LinuxCoreDumper::ThreadsSuspend() {
119   return true;
120 }
121
122 bool LinuxCoreDumper::ThreadsResume() {
123   return true;
124 }
125
126 bool LinuxCoreDumper::EnumerateThreads() {
127   if (!mapped_core_file_.Map(core_path_, 0)) {
128     fprintf(stderr, "Could not map core dump file into memory\n");
129     return false;
130   }
131
132   core_.SetContent(mapped_core_file_.content());
133   if (!core_.IsValid()) {
134     fprintf(stderr, "Invalid core dump file\n");
135     return false;
136   }
137
138   ElfCoreDump::Note note = core_.GetFirstNote();
139   if (!note.IsValid()) {
140     fprintf(stderr, "PT_NOTE section not found\n");
141     return false;
142   }
143
144   bool first_thread = true;
145   do {
146     ElfCoreDump::Word type = note.GetType();
147     MemoryRange name = note.GetName();
148     MemoryRange description = note.GetDescription();
149
150     if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
151       fprintf(stderr, "Could not found a valid PT_NOTE.\n");
152       return false;
153     }
154
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):
157     //   Thread           Name          Type
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
165     //
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
170     //
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
175     //
176     // The following code only works if notes are ordered as expected.
177     switch (type) {
178       case NT_PRSTATUS: {
179         if (description.length() != sizeof(elf_prstatus)) {
180           fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
181           return false;
182         }
183
184         const elf_prstatus* status =
185             reinterpret_cast<const elf_prstatus*>(description.data());
186         pid_t pid = status->pr_pid;
187         ThreadInfo info;
188         memset(&info, 0, sizeof(ThreadInfo));
189         info.tgid = status->pr_pgrp;
190         info.ppid = status->pr_ppid;
191 #if defined(__mips__)
192         for (int i = EF_REG0; i <= EF_REG31; i++)
193           info.regs.regs[i - EF_REG0] = status->pr_reg[i];
194
195         info.regs.lo = status->pr_reg[EF_LO];
196         info.regs.hi = status->pr_reg[EF_HI];
197         info.regs.epc = status->pr_reg[EF_CP0_EPC];
198         info.regs.badvaddr = status->pr_reg[EF_CP0_BADVADDR];
199         info.regs.status = status->pr_reg[EF_CP0_STATUS];
200         info.regs.cause = status->pr_reg[EF_CP0_CAUSE];
201 #else
202         memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
203 #endif
204         if (first_thread) {
205           crash_thread_ = pid;
206           crash_signal_ = status->pr_info.si_signo;
207         }
208         first_thread = false;
209         threads_.push_back(pid);
210         thread_infos_.push_back(info);
211         break;
212       }
213 #if defined(__i386) || defined(__x86_64)
214       case NT_FPREGSET: {
215         if (thread_infos_.empty())
216           return false;
217
218         ThreadInfo* info = &thread_infos_.back();
219         if (description.length() != sizeof(info->fpregs)) {
220           fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
221           return false;
222         }
223
224         memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
225         break;
226       }
227 #endif
228 #if defined(__i386)
229       case NT_PRXFPREG: {
230         if (thread_infos_.empty())
231           return false;
232
233         ThreadInfo* info = &thread_infos_.back();
234         if (description.length() != sizeof(info->fpxregs)) {
235           fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
236           return false;
237         }
238
239         memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
240         break;
241       }
242 #endif
243     }
244     note = note.GetNextNote();
245   } while (note.IsValid());
246
247   return true;
248 }
249
250 }  // namespace google_breakpad