Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
c93f1950fa77d104fc734b0ed7c54131d978da36
[simgrid.git] / src / mc / inspect / mc_unw.cpp
1 /* Copyright (c) 2015-2023. 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 Libunwind support for mc_address_space objects. */
7
8 // We need this for the register indices:
9 // #define _GNU_SOURCE
10
11 #include "src/mc/inspect/mc_unw.hpp"
12 #include "src/mc/inspect/Frame.hpp"
13 #include "src/mc/sosp/RemoteProcessMemory.hpp"
14
15 #include <cstring>
16
17 // On x86_64, libunwind unw_context_t has the same layout as ucontext_t:
18 #include <sys/types.h>
19 #include <sys/ucontext.h>
20 #ifdef __FreeBSD__
21 typedef register_t greg_t;
22 #endif
23
24 #include <libunwind.h>
25
26 namespace simgrid::mc {
27
28 // ***** Implementation
29
30 /** Get frame unwind information (libunwind method)
31  *
32  *  Delegates to the local/ptrace implementation.
33  */
34 int UnwindContext::find_proc_info(unw_addr_space_t /*as*/, unw_word_t ip, unw_proc_info_t* pip, int need_unwind_info,
35                                   void* arg) noexcept
36 {
37   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
38   return unw_get_accessors(context->process_->unw_underlying_addr_space)
39       ->find_proc_info(context->process_->unw_underlying_addr_space, ip, pip, need_unwind_info,
40                        context->process_->unw_underlying_context);
41 }
42
43 /** Release frame unwind information (libunwind method)
44  *
45  *  Delegates to the local/ptrace implementation.
46  */
47 void UnwindContext::put_unwind_info(unw_addr_space_t /*as*/, unw_proc_info_t* pip, void* arg) noexcept
48 {
49   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
50   return unw_get_accessors(context->process_->unw_underlying_addr_space)
51       ->put_unwind_info(context->process_->unw_underlying_addr_space, pip, context->process_->unw_underlying_context);
52 }
53
54 /** (libunwind method)
55  *
56  *  Not implemented.
57  */
58 int UnwindContext::get_dyn_info_list_addr(unw_addr_space_t /*as*/, unw_word_t* dilap, void* arg) noexcept
59 {
60   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
61   return unw_get_accessors(context->process_->unw_underlying_addr_space)
62       ->get_dyn_info_list_addr(context->process_->unw_underlying_addr_space, dilap,
63                                context->process_->unw_underlying_context);
64 }
65
66 /** Read from the target address space memory (libunwind method)
67  *
68  *  Delegates to the `simgrid::mc::Process*`.
69  */
70 int UnwindContext::access_mem(unw_addr_space_t /*as*/, unw_word_t addr, unw_word_t* valp, int write, void* arg) noexcept
71 {
72   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
73   if (write)
74     return -UNW_EREADONLYREG;
75   context->address_space_->read_bytes(valp, sizeof(unw_word_t), remote(addr));
76   return 0;
77 }
78
79 void* UnwindContext::get_reg(unw_context_t* context, unw_regnum_t regnum) noexcept
80 {
81 #ifdef __x86_64
82   mcontext_t* mcontext = &context->uc_mcontext;
83   switch (regnum) {
84 #ifdef __linux__
85     case UNW_X86_64_RAX:
86       return &mcontext->gregs[REG_RAX];
87     case UNW_X86_64_RDX:
88       return &mcontext->gregs[REG_RDX];
89     case UNW_X86_64_RCX:
90       return &mcontext->gregs[REG_RCX];
91     case UNW_X86_64_RBX:
92       return &mcontext->gregs[REG_RBX];
93     case UNW_X86_64_RSI:
94       return &mcontext->gregs[REG_RSI];
95     case UNW_X86_64_RDI:
96       return &mcontext->gregs[REG_RDI];
97     case UNW_X86_64_RBP:
98       return &mcontext->gregs[REG_RBP];
99     case UNW_X86_64_RSP:
100       return &mcontext->gregs[REG_RSP];
101     case UNW_X86_64_R8:
102       return &mcontext->gregs[REG_R8];
103     case UNW_X86_64_R9:
104       return &mcontext->gregs[REG_R9];
105     case UNW_X86_64_R10:
106       return &mcontext->gregs[REG_R10];
107     case UNW_X86_64_R11:
108       return &mcontext->gregs[REG_R11];
109     case UNW_X86_64_R12:
110       return &mcontext->gregs[REG_R12];
111     case UNW_X86_64_R13:
112       return &mcontext->gregs[REG_R13];
113     case UNW_X86_64_R14:
114       return &mcontext->gregs[REG_R14];
115     case UNW_X86_64_R15:
116       return &mcontext->gregs[REG_R15];
117     case UNW_X86_64_RIP:
118       return &mcontext->gregs[REG_RIP];
119 #elif defined __FreeBSD__
120     case UNW_X86_64_RAX:
121       return &mcontext->mc_rax;
122     case UNW_X86_64_RDX:
123       return &mcontext->mc_rdx;
124     case UNW_X86_64_RCX:
125       return &mcontext->mc_rcx;
126     case UNW_X86_64_RBX:
127       return &mcontext->mc_rbx;
128     case UNW_X86_64_RSI:
129       return &mcontext->mc_rsi;
130     case UNW_X86_64_RDI:
131       return &mcontext->mc_rdi;
132     case UNW_X86_64_RBP:
133       return &mcontext->mc_rbp;
134     case UNW_X86_64_RSP:
135       return &mcontext->mc_rsp;
136     case UNW_X86_64_R8:
137       return &mcontext->mc_r8;
138     case UNW_X86_64_R9:
139       return &mcontext->mc_r9;
140     case UNW_X86_64_R10:
141       return &mcontext->mc_r10;
142     case UNW_X86_64_R11:
143       return &mcontext->mc_r11;
144     case UNW_X86_64_R12:
145       return &mcontext->mc_r12;
146     case UNW_X86_64_R13:
147       return &mcontext->mc_r13;
148     case UNW_X86_64_R14:
149       return &mcontext->mc_r14;
150     case UNW_X86_64_R15:
151       return &mcontext->mc_r15;
152     case UNW_X86_64_RIP:
153       return &mcontext->mc_rip;
154 #else
155 #error "Unable to get register from ucontext, please add your case"
156 #endif
157     default:
158       return nullptr;
159   }
160 #else
161   return nullptr;
162 #endif
163 }
164
165 /** Read a standard register (libunwind method)
166  */
167 int UnwindContext::access_reg(unw_addr_space_t /*as*/, unw_regnum_t regnum, unw_word_t* valp, int write,
168                               void* arg) noexcept
169 {
170   auto* as_context       = static_cast<simgrid::mc::UnwindContext*>(arg);
171   unw_context_t* context = &as_context->unwind_context_;
172   if (write)
173     return -UNW_EREADONLYREG;
174   const greg_t* preg = (greg_t*)get_reg(context, regnum);
175   if (not preg)
176     return -UNW_EBADREG;
177   *valp = *preg;
178   return 0;
179 }
180
181 /** Find information about a function (libunwind method)
182  */
183 int UnwindContext::get_proc_name(unw_addr_space_t /*as*/, unw_word_t addr, char* bufp, size_t buf_len, unw_word_t* offp,
184                                  void* arg) noexcept
185 {
186   const simgrid::mc::UnwindContext* context = (simgrid::mc::UnwindContext*)arg;
187   const simgrid::mc::Frame* frame           = context->process_->find_function(remote(addr));
188   if (not frame)
189     return -UNW_ENOINFO;
190   *offp = (unw_word_t)frame->range.begin() - addr;
191
192   strncpy(bufp, frame->name.c_str(), buf_len);
193   if (bufp[buf_len - 1]) {
194     bufp[buf_len - 1] = 0;
195     return -UNW_ENOMEM;
196   }
197
198   return 0;
199 }
200
201 // ***** Init
202
203 unw_addr_space_t UnwindContext::createUnwindAddressSpace()
204 {
205   /** Virtual table for our `libunwind` implementation
206    *
207    *  Stack unwinding on a `simgrid::mc::Process*` (for memory, unwinding information)
208    *  and `ucontext_t` (for processor registers).
209    *
210    * It works with the `simgrid::mc::UnwindContext` context.
211    *
212    * Use nullptr as access_fpreg and resume, as we don't need them.
213    */
214   unw_accessors_t accessors        = {};
215   accessors.find_proc_info         = &find_proc_info;
216   accessors.put_unwind_info        = &put_unwind_info;
217   accessors.get_dyn_info_list_addr = &get_dyn_info_list_addr;
218   accessors.access_mem             = &access_mem;
219   accessors.access_reg             = &access_reg;
220   accessors.access_fpreg           = nullptr;
221   accessors.resume                 = nullptr;
222   accessors.get_proc_name          = &get_proc_name;
223   return unw_create_addr_space(&accessors, BYTE_ORDER);
224 }
225
226 void UnwindContext::initialize(simgrid::mc::RemoteProcessMemory& process_memory, const unw_context_t* c)
227 {
228   this->address_space_ = &process_memory;
229   this->process_       = &process_memory;
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 #elif SIMGRID_PROCESSOR_arm64
243 #ifdef __linux__
244   // On ARM64, ucontext_t doesn't contain `fpregs` and the FP registers
245   // are instead held in the `__reserved` field of the struct. It doesn't
246   // appear anything needs to be done here, although this should be verified
247 #endif
248 #else
249   // Do we need to do any fixup like this?
250 #error Target CPU type is not handled.
251 #endif
252 }
253
254 unw_cursor_t UnwindContext::cursor()
255 {
256   unw_cursor_t cursor;
257   xbt_assert(process_ != nullptr && address_space_ != nullptr &&
258                  unw_init_remote(&cursor, process_->unw_addr_space, this) == 0,
259              "UnwindContext not initialized");
260   return cursor;
261 }
262
263 } // namespace simgrid::mc