Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
argh, linkchecker needs --check-extern to be really useful
[simgrid.git] / src / mc / mc_unw.cpp
1 /* Copyright (c) 2015-2019. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 /** \file
8  *  Libunwind support for mc_address_space objects.
9  */
10
11 // We need this for the register indices:
12 // #define _GNU_SOURCE
13
14 #include <cstring>
15
16 // On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
17 #include <sys/types.h>
18 #include <sys/ucontext.h>
19 #ifdef __FreeBSD__
20 typedef register_t greg_t;
21 #endif
22
23 #include <libunwind.h>
24
25 #include "src/mc/Frame.hpp"
26 #include "src/mc/mc_unw.hpp"
27 #include "src/mc/remote/RemoteClient.hpp"
28
29 using simgrid::mc::remote;
30
31 namespace simgrid {
32 namespace mc {
33
34 // ***** Implementation
35
36 /** Get frame unwind information (libunwind method)
37  *
38  *  Delegates to the local/ptrace implementation.
39  */
40 int UnwindContext::find_proc_info(unw_addr_space_t /*as*/, unw_word_t ip, unw_proc_info_t* pip, int need_unwind_info,
41                                   void* arg) noexcept
42 {
43   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*) arg;
44   return unw_get_accessors(context->process_->unw_underlying_addr_space)->find_proc_info(
45     context->process_->unw_underlying_addr_space, ip, pip,
46     need_unwind_info, context->process_->unw_underlying_context
47   );
48 }
49
50 /** Release frame unwind information (libunwind method)
51  *
52  *  Delegates to the local/ptrace implementation.
53  */
54 void UnwindContext::put_unwind_info(unw_addr_space_t /*as*/, unw_proc_info_t* pip, void* arg) noexcept
55 {
56   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*) arg;
57   return unw_get_accessors(context->process_->unw_underlying_addr_space)->put_unwind_info(
58     context->process_->unw_underlying_addr_space, pip,
59     context->process_->unw_underlying_context
60   );
61 }
62
63 /** (libunwind method)
64  *
65  *  Not implemented.
66  */
67 int UnwindContext::get_dyn_info_list_addr(unw_addr_space_t /*as*/, unw_word_t* dilap, void* arg) noexcept
68 {
69   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*) arg;
70   return unw_get_accessors(context->process_->unw_underlying_addr_space)->get_dyn_info_list_addr(
71     context->process_->unw_underlying_addr_space,
72     dilap,
73     context->process_->unw_underlying_context
74   );
75 }
76
77 /** Read from the target address space memory (libunwind method)
78  *
79  *  Delegates to the `simgrid::mc::Process*`.
80  */
81 int UnwindContext::access_mem(unw_addr_space_t /*as*/, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept
82 {
83   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*) arg;
84   if (write)
85     return - UNW_EREADONLYREG;
86   context->addressSpace_->read_bytes(valp, sizeof(unw_word_t), remote(addr));
87   return 0;
88 }
89
90 void* UnwindContext::get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept
91 {
92 #ifdef __x86_64
93   mcontext_t* mcontext = &context->uc_mcontext;
94   switch (regnum) {
95 # ifdef __linux__
96   case UNW_X86_64_RAX: return &mcontext->gregs[REG_RAX];
97   case UNW_X86_64_RDX: return &mcontext->gregs[REG_RDX];
98   case UNW_X86_64_RCX: return &mcontext->gregs[REG_RCX];
99   case UNW_X86_64_RBX: return &mcontext->gregs[REG_RBX];
100   case UNW_X86_64_RSI: return &mcontext->gregs[REG_RSI];
101   case UNW_X86_64_RDI: return &mcontext->gregs[REG_RDI];
102   case UNW_X86_64_RBP: return &mcontext->gregs[REG_RBP];
103   case UNW_X86_64_RSP: return &mcontext->gregs[REG_RSP];
104   case UNW_X86_64_R8:  return &mcontext->gregs[REG_R8];
105   case UNW_X86_64_R9:  return &mcontext->gregs[REG_R9];
106   case UNW_X86_64_R10: return &mcontext->gregs[REG_R10];
107   case UNW_X86_64_R11: return &mcontext->gregs[REG_R11];
108   case UNW_X86_64_R12: return &mcontext->gregs[REG_R12];
109   case UNW_X86_64_R13: return &mcontext->gregs[REG_R13];
110   case UNW_X86_64_R14: return &mcontext->gregs[REG_R14];
111   case UNW_X86_64_R15: return &mcontext->gregs[REG_R15];
112   case UNW_X86_64_RIP: return &mcontext->gregs[REG_RIP];
113 # elif defined __FreeBSD__
114   case UNW_X86_64_RAX: return &mcontext->mc_rax;
115   case UNW_X86_64_RDX: return &mcontext->mc_rdx;
116   case UNW_X86_64_RCX: return &mcontext->mc_rcx;
117   case UNW_X86_64_RBX: return &mcontext->mc_rbx;
118   case UNW_X86_64_RSI: return &mcontext->mc_rsi;
119   case UNW_X86_64_RDI: return &mcontext->mc_rdi;
120   case UNW_X86_64_RBP: return &mcontext->mc_rbp;
121   case UNW_X86_64_RSP: return &mcontext->mc_rsp;
122   case UNW_X86_64_R8:  return &mcontext->mc_r8;
123   case UNW_X86_64_R9:  return &mcontext->mc_r9;
124   case UNW_X86_64_R10: return &mcontext->mc_r10;
125   case UNW_X86_64_R11: return &mcontext->mc_r11;
126   case UNW_X86_64_R12: return &mcontext->mc_r12;
127   case UNW_X86_64_R13: return &mcontext->mc_r13;
128   case UNW_X86_64_R14: return &mcontext->mc_r14;
129   case UNW_X86_64_R15: return &mcontext->mc_r15;
130   case UNW_X86_64_RIP: return &mcontext->mc_rip;
131 # else
132 #  error "Unable to get register from ucontext, please add your case"
133 # endif
134   default: return nullptr;
135   }
136 #else
137   return nullptr;
138 #endif
139 }
140
141 /** Read a standard register (libunwind method)
142  */
143 int UnwindContext::access_reg(unw_addr_space_t /*as*/, unw_regnum_t regnum, unw_word_t* valp, int write,
144                               void* arg) noexcept
145 {
146   simgrid::mc::UnwindContext* as_context = (simgrid::mc::UnwindContext*) arg;
147   unw_context_t* context = &as_context->unwindContext_;
148   if (write)
149     return -UNW_EREADONLYREG;
150   greg_t* preg = (greg_t*) get_reg(context, regnum);
151   if (not preg)
152     return -UNW_EBADREG;
153   *valp = *preg;
154   return 0;
155 }
156
157 /** Read a floating-point register (libunwind method)
158  *
159  *  FP registers are caller-saved. The values saved by functions such as
160  *  `getcontext()` is not relevant for the caller. It is not really necessary
161  *  to save and handle them.
162  */
163 int UnwindContext::access_fpreg(unw_addr_space_t /*as*/, unw_regnum_t /*regnum*/, unw_fpreg_t* /*fpvalp*/,
164                                 int /*write*/, void* /*arg*/) noexcept
165 {
166   return -UNW_EBADREG;
167 }
168
169 /** Resume the execution of the context (libunwind method)
170  *
171  * We don't use this.
172  */
173 int UnwindContext::resume(unw_addr_space_t /*as*/, unw_cursor_t* /*cp*/, void* /*arg*/) noexcept
174 {
175   return -UNW_EUNSPEC;
176 }
177
178 /** Find informations about a function (libunwind method)
179  */
180 int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
181                                  void* arg) noexcept
182 {
183   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*) arg;
184   simgrid::mc::Frame* frame = context->process_->find_function(remote(addr));
185   if (not frame)
186     return - UNW_ENOINFO;
187   *offp = (unw_word_t) frame->range.begin() - addr;
188
189   strncpy(bufp, frame->name.c_str(), buf_len);
190   if (bufp[buf_len - 1]) {
191     bufp[buf_len - 1] = 0;
192     return -UNW_ENOMEM;
193   }
194
195   return 0;
196 }
197
198 // ***** Init
199
200 /** Virtual table for our `libunwind` implementation
201  *
202  *  Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
203  *  and `ucontext_t` (for processor registers).
204  *
205  *  It works with the `simgrid::mc::UnwindContext` context.
206  */
207 unw_accessors_t UnwindContext::accessors = {
208   &find_proc_info,
209   &put_unwind_info,
210   &get_dyn_info_list_addr,
211   &access_mem,
212   &access_reg,
213   &access_fpreg,
214   &resume,
215   &get_proc_name
216 };
217
218 unw_addr_space_t UnwindContext::createUnwindAddressSpace()
219 {
220   return unw_create_addr_space(&UnwindContext::accessors, BYTE_ORDER);
221 }
222
223 void UnwindContext::initialize(simgrid::mc::RemoteClient* process, unw_context_t* c)
224 {
225   this->addressSpace_ = process;
226   this->process_ = process;
227
228   // Take a copy of the context for our own purpose:
229   this->unwindContext_ = *c;
230 #if SIMGRID_PROCESSOR_x86_64 || SIMGRID_PROCESSOR_i686
231 # ifdef __linux__
232   // On x86_64, ucontext_t contains a pointer to itself for FP registers.
233   // We don't really need support for FR registers as they are caller saved
234   // and probably never use those fields as libunwind-x86_64 does not read
235   // FP registers from the unw_context_t
236   // Let's ignore this and see what happens:
237   this->unwindContext_.uc_mcontext.fpregs = nullptr;
238 # endif
239 #else
240   // Do we need to do any fixup like this?
241 # error Target CPU type is not handled.
242 #endif
243 }
244
245 unw_cursor_t UnwindContext::cursor()
246 {
247   unw_cursor_t cursor;
248   if (process_ == nullptr
249     || addressSpace_ == nullptr
250     || unw_init_remote(&cursor, process_->unw_addr_space, this) != 0)
251     xbt_die("UnwindContext not initialized");
252   return cursor;
253 }
254
255 }
256 }