HPCToolkit
ppc64-unwind.c
Go to the documentation of this file.
1 // -*-Mode: C++;-*- // technically C99
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //***************************************************************************
48 //
49 // HPCToolkit's PPC64 Unwinder
50 //
51 //***************************************************************************
52 
53 
54 //************************* System Include Files ****************************
55 
56 #include <stddef.h>
57 #include <stdbool.h>
58 #include <ucontext.h>
59 #include <assert.h>
60 
61 
62 //************************ External Include Files *************************
63 
64 #include <monitor.h>
65 
66 
67 //*************************** User Include Files ****************************
68 
69 #include <unwind/common/unwind.h>
72 
73 #include "ppc64-unwind-interval.h"
74 
75 #include "sample_event.h"
76 
77 #include "uw_recipe_map.h"
78 
79 #include <messages/messages.h>
80 
83 
86 
87 
88 
89 //***************************************************************************
90 // macros
91 //***************************************************************************
92 
93 #define MYDBG 0
94 
95 
96 
97 //***************************************************************************
98 // type declarations
99 //***************************************************************************
100 
101 //
102 // register codes (only 1 at the moment)
103 //
104 typedef unw_frame_regnum_t unw_reg_code_t;
105 
106 typedef enum {
109 } unw_flag_t;
110 
111 
112 
113 //***************************************************************************
114 // forward declarations
115 //***************************************************************************
116 
117 static fence_enum_t
118 hpcrun_check_fence(void* ip);
119 
120 
121 
122 //***************************************************************************
123 // private functions
124 //***************************************************************************
125 
126 static void
127 save_registers(hpcrun_unw_cursor_t* cursor, void *pc, void *bp, void *sp,
128  void *ra)
129 {
130  cursor->pc_unnorm = pc;
131  cursor->bp = bp;
132  cursor->sp = sp;
133  cursor->ra = ra;
134 }
135 
136 static void
138 {
139  void *func_start_pc = (void*) cursor->unwr_info.interval.start;
140  load_module_t* lm = cursor->unwr_info.lm;
141 
142  cursor->pc_norm = hpcrun_normalize_ip(cursor->pc_unnorm, lm);
143  cursor->the_function = hpcrun_normalize_ip(func_start_pc, lm);
144 }
145 
146 
147 
148 static bool
150 {
151  return (fence == FENCE_MAIN) || (fence == FENCE_THREAD);
152 }
153 
154 
155 static fence_enum_t
157 {
158  fence_enum_t rv = FENCE_NONE;
159  if (monitor_unwind_process_bottom_frame(ip))
160  rv = FENCE_MAIN;
161  else if (monitor_unwind_thread_bottom_frame(ip))
162  rv = FENCE_THREAD;
163 
164  if (ENABLED(FENCE_UNW) && rv != FENCE_NONE)
165  TMSG(FENCE_UNW, "%s", fence_enum_name(rv));
166  return rv;
167 }
168 
169 
170 static int
172  void** reg_value)
173 {
174  assert(reg_id == UNW_REG_IP);
175  *reg_value = cursor->pc_unnorm;
176 
177  return 0;
178 }
179 
180 
181 static int
183  ip_normalized_t* reg_value)
184 {
185  assert(reg_id == UNW_REG_IP);
186  *reg_value = cursor->pc_norm;
187 
188  return 0;
189 }
190 
191 
192 //***************************************************************************
193 // interface functions
194 //***************************************************************************
195 
196 void
198 {
200 }
201 
202 int
204 {
205  return hpcrun_unw_get_norm_reg(c, UNW_REG_IP, reg_value);
206 }
207 
208 
209 int
211 {
212  return hpcrun_unw_get_unnorm_reg(c, UNW_REG_IP, reg_value);
213 }
214 
215 
216 //
217 // unimplemented for now
218 // fix when trampoline support is added
219 //
220 void*
222 {
223  return NULL;
224 }
225 
226 
227 void
229 {
230  ucontext_t* ctxt = (ucontext_t*)context;
231 
232  save_registers(cursor, ucontext_pc(ctxt), NULL, ucontext_sp(ctxt), NULL);
234 
235  cursor->flags = UnwFlg_StackTop;
236  bitree_uwi_t* intvl = NULL;
237  bool found = uw_recipe_map_lookup(cursor->pc_unnorm, NATIVE_UNWINDER, &(cursor->unwr_info));
238  if (found) {
239  intvl = cursor->unwr_info.btuwi;
240  if (intvl && UWI_RECIPE(intvl)->ra_ty == RATy_Reg) {
241  if (UWI_RECIPE(intvl)->ra_arg == PPC_REG_LR) {
242  cursor->ra = (void*)(ctxt->uc_mcontext.regs->link);
243  }
244  else {
245  cursor->ra = (void*)(ctxt->uc_mcontext.regs->gpr[UWI_RECIPE(intvl)->ra_arg]);
246  }
247  }
248  }
249  else {
250  EMSG("unw_init: cursor could NOT build an interval for initial pc = %p",
251  cursor->pc_unnorm);
252  }
253 
254  compute_normalized_ips(cursor);
255 
256  TMSG(UNW, "init: pc=%p, ra=%p, sp=%p, fp=%p",
257  cursor->pc_unnorm, cursor->ra, cursor->sp, cursor->bp);
258  if (MYDBG) { ui_dump(intvl); }
259 }
260 
261 
262 // --FIXME--: add advanced fence processing and enclosing function to cursor here
263 //
264 
267 {
268  // current frame
269  void* pc = cursor->pc_unnorm;
270  void** sp = cursor->sp;
271  void** fp = cursor->bp; // unused
272  unwind_interval* intvl = (unwind_interval*)(cursor->unwr_info.btuwi);
273 
274  bool isInteriorFrm = (cursor->flags != UnwFlg_StackTop);
275 
276  // next (parent) frame
277  void* nxt_pc = NULL;
278  void** nxt_sp = NULL;
279  void** nxt_fp = NULL; // unused
280  void* nxt_ra = NULL; // always NULL unless we go through a signal handler
281  unwind_interval* nxt_intvl = NULL;
282 
283  if (!intvl) {
284  TMSG(UNW, "error: missing interval for pc=%p", pc);
285  return STEP_ERROR;
286  }
287 
288 
289  cursor->fence = hpcrun_check_fence(cursor->pc_unnorm);
290 
291  //-----------------------------------------------------------
292  // check if we have reached the end of our unwind, which is
293  // demarcated with a fence.
294  //-----------------------------------------------------------
295  if (fence_stop(cursor->fence)) {
296  TMSG(UNW,"unw_step: STEP_STOP, current pc in monitor fence pc=%p\n", cursor->pc_unnorm);
297  return STEP_STOP;
298  }
299 
300  if ((void*)sp >= monitor_stack_bottom()) {
301  TMSG(FENCE_UNW, "stop: sp (%p) >= unw_stack_bottom", sp);
302  cursor->fence = FENCE_MAIN;
303  return STEP_STOP;
304  }
305 
306  //-----------------------------------------------------------
307  // compute SP (stack pointer) for the caller's frame. Do this first
308  // because we rely on the invariant that an interior frame contains
309  // a stack pointer and, above the stack pointer, a return address.
310  //-----------------------------------------------------------
311  if (UWI_RECIPE(intvl)->sp_ty == SPTy_Reg) {
312  // SP already points to caller's stack frame
313  nxt_sp = sp;
314 
315  // consistency check: interior frames should not have type SPTy_Reg
316  if (isInteriorFrm) {
317  nxt_sp = *sp;
318  TMSG(UNW, "warning: correcting sp: %p -> %p", sp, nxt_sp);
319  }
320  }
321  else if (UWI_RECIPE(intvl)->sp_ty == SPTy_SPRel) {
322  // SP points to parent's SP
323  nxt_sp = *sp;
324  }
325  else {
326  // assert(0);
327  EMSG("unwind failure computing SP at pc: %p, sp: %p", pc, sp);
328  return STEP_ERROR;
329  }
330 
331 
332  //-----------------------------------------------------------
333  // compute RA (return address) for the caller's frame
334  //-----------------------------------------------------------
335  if (UWI_RECIPE(intvl)->ra_ty == RATy_Reg) {
336  nxt_pc = cursor->ra;
337 
338  // consistency check: interior frames should not have type RATy_Reg
339  if (isInteriorFrm) {
340  nxt_pc = getNxtPCFromSP(nxt_sp);
341  TMSG(UNW, "warning: correcting pc: %p -> %p", cursor->ra, nxt_pc);
342  }
343  }
344  else if (UWI_RECIPE(intvl)->ra_ty == RATy_SPRel) {
345  nxt_pc = getNxtPCFromSP(nxt_sp);
346  }
347  else {
348  // assert(0);
349  EMSG("unwind failure computing RA at pc: %p, sp: %p", pc, sp);
350  return STEP_ERROR;
351  }
352 
353 
354  //-----------------------------------------------------------
355  // compute unwind information for the caller's pc
356  //-----------------------------------------------------------
357  bool found = uw_recipe_map_lookup(nxt_pc, NATIVE_UNWINDER, &(cursor->unwr_info));
358  if (found) {
359  nxt_intvl = cursor->unwr_info.btuwi;
360  }
361 
362  // if nxt_pc is invalid for some reason...
363  if (!nxt_intvl) {
364  TMSG(UNW, "warning: bad nxt pc=%p; sp=%p, fp=%p...", nxt_pc, sp, fp);
365 
366  //-------------------------------------------------------------------
367  // If this is a leaf frame, assume the interval didn't correctly
368  // track the return address. Try one frame deeper.
369  //-------------------------------------------------------------------
370  void** try_sp = NULL;
371  if (!isInteriorFrm) {
372  try_sp = *nxt_sp;
373 
374  // Sanity check SP: Once in a while SP is clobbered.
375  if (isPossibleParentSP(nxt_sp, try_sp)) {
376  nxt_pc = getNxtPCFromSP(try_sp);
377  bool found2 = uw_recipe_map_lookup(nxt_pc, NATIVE_UNWINDER, &(cursor->unwr_info));
378  if (found2) {
379  nxt_intvl = cursor->unwr_info.btuwi;
380  }
381  }
382  }
383 
384  if (!nxt_intvl) {
385  TMSG(UNW, "error: skip-frame failed: nxt pc=%p, sp=%p; try sp=%p",
386  nxt_pc, nxt_sp, try_sp);
387  return STEP_ERROR;
388  }
389 
390  // INVARIANT: 'try_sp' is valid
391  nxt_sp = try_sp;
392  TMSG(UNW, "skip-frame: nxt pc=%p, sp=%p", nxt_pc, nxt_sp);
393  }
394  // INVARIANT: At this point, 'nxt_intvl' is valid
395 
396 
397  // INVARIANT: Ensure we always make progress unwinding the stack...
398  bool mayFrameSizeBe0 = (UWI_RECIPE(intvl)->sp_ty == SPTy_Reg && !isInteriorFrm);
399  if (!mayFrameSizeBe0 && !isPossibleParentSP(sp, nxt_sp)) {
400  // TMSG(UNW, " warning: adjust sp b/c nxt_sp=%p < sp=%p", nxt_sp, sp);
401  // nxt_sp = sp + 1
402  TMSG(UNW, "error: loop! nxt_sp=%p, sp=%p", nxt_sp, sp);
403  return STEP_ERROR;
404  }
405 
406 
407  TMSG(UNW, "next: pc=%p, sp=%p, fp=%p", nxt_pc, nxt_sp, nxt_fp);
408  if (MYDBG) { ui_dump(nxt_intvl); }
409 
410  save_registers(cursor, nxt_pc, nxt_fp, nxt_sp, nxt_ra);
411  cursor->flags = UnwFlg_NULL;
412 
413  compute_normalized_ips(cursor);
414 
415  return STEP_OK;
416 }
ip_normalized_t hpcrun_normalize_ip(void *unnormalized_ip, load_module_t *lm)
Definition: ip-normalized.c:70
static void ** ucontext_sp(ucontext_t *context)
Definition: _mcontext.h:84
#define ip_normalized_NULL
Definition: ip-normalized.h:83
static void compute_normalized_ips(hpcrun_unw_cursor_t *cursor)
Definition: ppc64-unwind.c:137
static fence_enum_t hpcrun_check_fence(void *ip)
Definition: ppc64-unwind.c:156
void hpcrun_unw_init_cursor(hpcrun_unw_cursor_t *cursor, void *context)
Definition: ppc64-unwind.c:228
fence_enum_t fence
struct ip_normalized_t ip_normalized_t
static void * getNxtPCFromSP(void **sp)
Definition: _mcontext.h:93
static bool isPossibleParentSP(void **sp, void **parent_sp)
Definition: _mcontext.h:105
static bool fence_stop(fence_enum_t fence)
Definition: ppc64-unwind.c:149
interval_t interval
Definition: unwindr_info.h:100
static void save_registers(hpcrun_unw_cursor_t *cursor, void *pc, void *bp, void *sp, void *ra)
Definition: ppc64-unwind.c:127
static int hpcrun_unw_get_unnorm_reg(hpcrun_unw_cursor_t *cursor, unw_reg_code_t reg_id, void **reg_value)
Definition: ppc64-unwind.c:171
unw_flag_t
Definition: ppc64-unwind.c:106
fence_enum_t
Definition: fence_enum.h:11
#define UWI_RECIPE(btuwi)
bitree_uwi_t * btuwi
Definition: unwindr_info.h:103
bool uw_recipe_map_lookup(void *addr, unwinder_t uw, unwindr_info_t *unwr_info)
step_state hpcrun_unw_step(hpcrun_unw_cursor_t *cursor)
Definition: ppc64-unwind.c:266
static int hpcrun_unw_get_norm_reg(hpcrun_unw_cursor_t *cursor, unw_reg_code_t reg_id, ip_normalized_t *reg_value)
Definition: ppc64-unwind.c:182
ip_normalized_t pc_norm
#define EMSG
Definition: messages.h:70
int hpcrun_unw_get_ip_unnorm_reg(hpcrun_unw_cursor_t *c, void **reg_value)
Definition: ppc64-unwind.c:210
struct bitree_uwi_s bitree_uwi_t
int hpcrun_unw_get_ip_norm_reg(hpcrun_unw_cursor_t *c, ip_normalized_t *reg_value)
Definition: ppc64-unwind.c:203
void hpcrun_unw_init(void)
Definition: ppc64-unwind.c:197
bool found
Definition: cct.c:129
#define TMSG(f,...)
Definition: messages.h:93
unw_frame_regnum_t unw_reg_code_t
Definition: ppc64-unwind.c:104
ip_normalized_t the_function
void ui_dump(unwind_interval *u)
void * hpcrun_unw_get_ra_loc(hpcrun_unw_cursor_t *cursor)
Definition: ppc64-unwind.c:221
#define NULL
Definition: ElfHelper.cpp:85
load_module_t * lm
Definition: unwindr_info.h:101
#define PPC_REG_LR
bitree_uwi_t unwind_interval
static void * ucontext_pc(ucontext_t *context)
Definition: _mcontext.h:7
#define MYDBG
Definition: ppc64-unwind.c:93
uintptr_t start
Definition: interval_t.h:27
static char * fence_enum_name(fence_enum_t f)
Definition: fence_enum.h:24
step_state
Definition: unwind.h:124
unwindr_info_t unwr_info
#define ENABLED(f)
Definition: debug-flag.h:76
void uw_recipe_map_init(void)