Logo AND Algorithmique Numérique Distribuée

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