Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Fix most of spelling mistakes in src/
[simgrid.git] / src / mc / inspect / mc_unw.cpp
1 /* Copyright (c) 2015-2020. 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/RemoteSimulation.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   const 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   const 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   const 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   const 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   const 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 /** Find information about a function (libunwind method)
187  */
188 int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
189                                  void* arg) noexcept
190 {
191   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
192   const simgrid::mc::Frame* frame           = context->process_->find_function(remote(addr));
193   if (not frame)
194     return -UNW_ENOINFO;
195   *offp = (unw_word_t)frame->range.begin() - addr;
196
197   strncpy(bufp, frame->name.c_str(), buf_len);
198   if (bufp[buf_len - 1]) {
199     bufp[buf_len - 1] = 0;
200     return -UNW_ENOMEM;
201   }
202
203   return 0;
204 }
205
206 // ***** Init
207
208 /** Virtual table for our `libunwind` implementation
209  *
210  *  Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
211  *  and `ucontext_t` (for processor registers).
212  *
213  * It works with the `simgrid::mc::UnwindContext` context.
214  *
215  * Use nullptr as access_fpreg and resume, as we don't need them.
216  */
217 unw_accessors_t UnwindContext::accessors = {&find_proc_info, &put_unwind_info, &get_dyn_info_list_addr,
218                                             &access_mem,     &access_reg,      nullptr,
219                                             nullptr,         &get_proc_name};
220
221 unw_addr_space_t UnwindContext::createUnwindAddressSpace()
222 {
223   return unw_create_addr_space(&UnwindContext::accessors, BYTE_ORDER);
224 }
225
226 void UnwindContext::initialize(simgrid::mc::RemoteSimulation* process, unw_context_t* c)
227 {
228   this->address_space_ = process;
229   this->process_      = process;
230
231   // Take a copy of the context for our own purpose:
232   this->unwind_context_ = *c;
233 #if SIMGRID_PROCESSOR_x86_64 || SIMGRID_PROCESSOR_i686
234 #ifdef __linux__
235   // On x86_64, ucontext_t contains a pointer to itself for FP registers.
236   // We don't really need support for FR registers as they are caller saved
237   // and probably never use those fields as libunwind-x86_64 does not read
238   // FP registers from the unw_context_t
239   // Let's ignore this and see what happens:
240   this->unwind_context_.uc_mcontext.fpregs = nullptr;
241 #endif
242 #else
243   // Do we need to do any fixup like this?
244 #error Target CPU type is not handled.
245 #endif
246 }
247
248 unw_cursor_t UnwindContext::cursor()
249 {
250   unw_cursor_t cursor;
251   xbt_assert(process_ != nullptr && address_space_ != nullptr &&
252                  unw_init_remote(&cursor, process_->unw_addr_space, this) == 0,
253              "UnwindContext not initialized");
254   return cursor;
255 }
256
257 } // namespace mc
258 } // namespace simgrid