Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
add s4u-cloud-simple to the list of examples
[simgrid.git] / src / mc / inspect / 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)
45       ->find_proc_info(context->process_->unw_underlying_addr_space, ip, pip, need_unwind_info,
46                        context->process_->unw_underlying_context);
47 }
48
49 /** Release frame unwind information (libunwind method)
50  *
51  *  Delegates to the local/ptrace implementation.
52  */
53 void UnwindContext::put_unwind_info(unw_addr_space_t /*as*/, unw_proc_info_t* pip, void* arg) noexcept
54 {
55   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
56   return unw_get_accessors(context->process_->unw_underlying_addr_space)
57       ->put_unwind_info(context->process_->unw_underlying_addr_space, pip, context->process_->unw_underlying_context);
58 }
59
60 /** (libunwind method)
61  *
62  *  Not implemented.
63  */
64 int UnwindContext::get_dyn_info_list_addr(unw_addr_space_t /*as*/, unw_word_t* dilap, void* arg) noexcept
65 {
66   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
67   return unw_get_accessors(context->process_->unw_underlying_addr_space)
68       ->get_dyn_info_list_addr(context->process_->unw_underlying_addr_space, dilap,
69                                context->process_->unw_underlying_context);
70 }
71
72 /** Read from the target address space memory (libunwind method)
73  *
74  *  Delegates to the `simgrid::mc::Process*`.
75  */
76 int UnwindContext::access_mem(unw_addr_space_t /*as*/, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept
77 {
78   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
79   if (write)
80     return -UNW_EREADONLYREG;
81   context->addressSpace_->read_bytes(valp, sizeof(unw_word_t), remote(addr));
82   return 0;
83 }
84
85 void* UnwindContext::get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept
86 {
87 #ifdef __x86_64
88   mcontext_t* mcontext = &context->uc_mcontext;
89   switch (regnum) {
90 #ifdef __linux__
91     case UNW_X86_64_RAX:
92       return &mcontext->gregs[REG_RAX];
93     case UNW_X86_64_RDX:
94       return &mcontext->gregs[REG_RDX];
95     case UNW_X86_64_RCX:
96       return &mcontext->gregs[REG_RCX];
97     case UNW_X86_64_RBX:
98       return &mcontext->gregs[REG_RBX];
99     case UNW_X86_64_RSI:
100       return &mcontext->gregs[REG_RSI];
101     case UNW_X86_64_RDI:
102       return &mcontext->gregs[REG_RDI];
103     case UNW_X86_64_RBP:
104       return &mcontext->gregs[REG_RBP];
105     case UNW_X86_64_RSP:
106       return &mcontext->gregs[REG_RSP];
107     case UNW_X86_64_R8:
108       return &mcontext->gregs[REG_R8];
109     case UNW_X86_64_R9:
110       return &mcontext->gregs[REG_R9];
111     case UNW_X86_64_R10:
112       return &mcontext->gregs[REG_R10];
113     case UNW_X86_64_R11:
114       return &mcontext->gregs[REG_R11];
115     case UNW_X86_64_R12:
116       return &mcontext->gregs[REG_R12];
117     case UNW_X86_64_R13:
118       return &mcontext->gregs[REG_R13];
119     case UNW_X86_64_R14:
120       return &mcontext->gregs[REG_R14];
121     case UNW_X86_64_R15:
122       return &mcontext->gregs[REG_R15];
123     case UNW_X86_64_RIP:
124       return &mcontext->gregs[REG_RIP];
125 #elif defined __FreeBSD__
126     case UNW_X86_64_RAX:
127       return &mcontext->mc_rax;
128     case UNW_X86_64_RDX:
129       return &mcontext->mc_rdx;
130     case UNW_X86_64_RCX:
131       return &mcontext->mc_rcx;
132     case UNW_X86_64_RBX:
133       return &mcontext->mc_rbx;
134     case UNW_X86_64_RSI:
135       return &mcontext->mc_rsi;
136     case UNW_X86_64_RDI:
137       return &mcontext->mc_rdi;
138     case UNW_X86_64_RBP:
139       return &mcontext->mc_rbp;
140     case UNW_X86_64_RSP:
141       return &mcontext->mc_rsp;
142     case UNW_X86_64_R8:
143       return &mcontext->mc_r8;
144     case UNW_X86_64_R9:
145       return &mcontext->mc_r9;
146     case UNW_X86_64_R10:
147       return &mcontext->mc_r10;
148     case UNW_X86_64_R11:
149       return &mcontext->mc_r11;
150     case UNW_X86_64_R12:
151       return &mcontext->mc_r12;
152     case UNW_X86_64_R13:
153       return &mcontext->mc_r13;
154     case UNW_X86_64_R14:
155       return &mcontext->mc_r14;
156     case UNW_X86_64_R15:
157       return &mcontext->mc_r15;
158     case UNW_X86_64_RIP:
159       return &mcontext->mc_rip;
160 #else
161 #error "Unable to get register from ucontext, please add your case"
162 #endif
163     default:
164       return nullptr;
165   }
166 #else
167   return nullptr;
168 #endif
169 }
170
171 /** Read a standard register (libunwind method)
172  */
173 int UnwindContext::access_reg(unw_addr_space_t /*as*/, unw_regnum_t regnum, unw_word_t* valp, int write,
174                               void* arg) noexcept
175 {
176   simgrid::mc::UnwindContext* as_context = (simgrid::mc::UnwindContext*)arg;
177   unw_context_t* context                 = &as_context->unwindContext_;
178   if (write)
179     return -UNW_EREADONLYREG;
180   greg_t* preg = (greg_t*)get_reg(context, regnum);
181   if (not preg)
182     return -UNW_EBADREG;
183   *valp = *preg;
184   return 0;
185 }
186
187 /** Read a floating-point register (libunwind method)
188  *
189  *  FP registers are caller-saved. The values saved by functions such as
190  *  `getcontext()` is not relevant for the caller. It is not really necessary
191  *  to save and handle them.
192  */
193 int UnwindContext::access_fpreg(unw_addr_space_t /*as*/, unw_regnum_t /*regnum*/, unw_fpreg_t* /*fpvalp*/,
194                                 int /*write*/, void* /*arg*/) noexcept
195 {
196   return -UNW_EBADREG;
197 }
198
199 /** Resume the execution of the context (libunwind method)
200  *
201  * We don't use this.
202  */
203 int UnwindContext::resume(unw_addr_space_t /*as*/, unw_cursor_t* /*cp*/, void* /*arg*/) noexcept
204 {
205   return -UNW_EUNSPEC;
206 }
207
208 /** Find informations about a function (libunwind method)
209  */
210 int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
211                                  void* arg) noexcept
212 {
213   simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
214   simgrid::mc::Frame* frame           = context->process_->find_function(remote(addr));
215   if (not frame)
216     return -UNW_ENOINFO;
217   *offp = (unw_word_t)frame->range.begin() - addr;
218
219   strncpy(bufp, frame->name.c_str(), buf_len);
220   if (bufp[buf_len - 1]) {
221     bufp[buf_len - 1] = 0;
222     return -UNW_ENOMEM;
223   }
224
225   return 0;
226 }
227
228 // ***** Init
229
230 /** Virtual table for our `libunwind` implementation
231  *
232  *  Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
233  *  and `ucontext_t` (for processor registers).
234  *
235  *  It works with the `simgrid::mc::UnwindContext` context.
236  */
237 unw_accessors_t UnwindContext::accessors = {&find_proc_info, &put_unwind_info, &get_dyn_info_list_addr,
238                                             &access_mem,     &access_reg,      &access_fpreg,
239                                             &resume,         &get_proc_name};
240
241 unw_addr_space_t UnwindContext::createUnwindAddressSpace()
242 {
243   return unw_create_addr_space(&UnwindContext::accessors, BYTE_ORDER);
244 }
245
246 void UnwindContext::initialize(simgrid::mc::RemoteClient* process, unw_context_t* c)
247 {
248   this->addressSpace_ = process;
249   this->process_      = process;
250
251   // Take a copy of the context for our own purpose:
252   this->unwindContext_ = *c;
253 #if SIMGRID_PROCESSOR_x86_64 || SIMGRID_PROCESSOR_i686
254 #ifdef __linux__
255   // On x86_64, ucontext_t contains a pointer to itself for FP registers.
256   // We don't really need support for FR registers as they are caller saved
257   // and probably never use those fields as libunwind-x86_64 does not read
258   // FP registers from the unw_context_t
259   // Let's ignore this and see what happens:
260   this->unwindContext_.uc_mcontext.fpregs = nullptr;
261 #endif
262 #else
263   // Do we need to do any fixup like this?
264 #error Target CPU type is not handled.
265 #endif
266 }
267
268 unw_cursor_t UnwindContext::cursor()
269 {
270   unw_cursor_t cursor;
271   if (process_ == nullptr || addressSpace_ == nullptr || unw_init_remote(&cursor, process_->unw_addr_space, this) != 0)
272     xbt_die("UnwindContext not initialized");
273   return cursor;
274 }
275
276 } // namespace mc
277 } // namespace simgrid