Logo AND Algorithmique Numérique Distribuée

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