Logo AND Algorithmique Numérique Distribuée

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