Logo AND Algorithmique Numérique Distribuée

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