Logo AND Algorithmique Numérique Distribuée

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