HPCToolkit
backtrace.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 // system include files
50 //***************************************************************************
51 
52 #include <stdbool.h>
53 #include <assert.h>
54 #include <stdlib.h>
55 
56 #include <sys/types.h>
57 #include <ucontext.h>
58 
59 #include <string.h>
60 
61 
62 //***************************************************************************
63 // local include files
64 //***************************************************************************
65 
67 #include <hpcrun/hpcrun_stats.h>
68 
69 #include <monitor.h>
70 
72 #include <dbg_backtrace.h>
73 
74 //***************************************************************************
75 // local constants & macros
76 //***************************************************************************
77 
78 //***************************************************************************
79 // forward declarations
80 //***************************************************************************
81 
82 static void lush_assoc_info2str(char* buf, size_t len, lush_assoc_info_t info);
83 static void lush_lip2str(char* buf, size_t len, lush_lip_t* lip);
84 
85 //***************************************************************************
86 // interface functions
87 //***************************************************************************
88 
89 void
90 hpcrun_bt_dump(frame_t* unwind, const char* tag)
91 {
92  static const int msg_limit = 100;
93  int msg_cnt = 0;
94 
95  char as_str[LUSH_ASSOC_INFO_STR_MIN_LEN];
96  char lip_str[LUSH_LIP_STR_MIN_LEN];
97 
98  const char* mytag = (tag) ? tag : "";
99  EMSG("-- begin new backtrace (innermost first) [%s] ----------", mytag);
100 
102  if (unwind) {
103  for (frame_t* x = td->btbuf_beg; x < unwind; ++x) {
104  lush_assoc_info2str(as_str, sizeof(as_str), x->as_info);
105  lush_lip2str(lip_str, sizeof(lip_str), x->lip);
106 
107  void* ip;
108  hpcrun_unw_get_ip_unnorm_reg(&(x->cursor), &ip);
109 
110  load_module_t* lm = hpcrun_loadmap_findById(x->ip_norm.lm_id);
111  const char* lm_name = (lm) ? lm->name : "(null)";
112 
113  EMSG("%s: ip = %p (%p), load module = %s | lip %s", as_str, ip, x->ip_norm.lm_ip, lm_name, lip_str);
114 
115  msg_cnt++;
116  if (msg_cnt > msg_limit) {
117  EMSG("!!! message limit !!!");
118  break;
119  }
120  }
121  }
122 
123  if (msg_cnt <= msg_limit && td->btbuf_sav != td->btbuf_end) {
124  EMSG("-- begin cached backtrace ---------------------------");
125  for (frame_t* x = td->btbuf_sav; x < td->btbuf_end; ++x) {
126  lush_assoc_info2str(as_str, sizeof(as_str), x->as_info);
127  lush_lip2str(lip_str, sizeof(lip_str), x->lip);
128  EMSG("%s: ip.lm_id = %d | ip.lm_ip = %p | lip %s", as_str,
129  x->ip_norm.lm_id, x->ip_norm.lm_ip, lip_str);
130  msg_cnt++;
131  if (msg_cnt > msg_limit) {
132  EMSG("!!! message limit !!!");
133  break;
134  }
135  }
136  }
137 
138  EMSG("-- end backtrace ------------------------------------\n");
139 }
140 
141 void
142 hpcrun_bt_init(backtrace_t* bt, size_t size)
143 {
144  bt->beg = (frame_t*) hpcrun_malloc(sizeof(frame_t) * size);
145  bt->end = bt->beg + (size - 1);
146  bt->size = size;
147  bt->cur = bt->beg;
148  bt->len = 0;
149 }
150 
151 frame_t*
152 hpcrun_skip_chords(frame_t* bt_outer, frame_t* bt_inner,
153  int skip)
154 {
155  // N.B.: INVARIANT: bt_inner < bt_outer
156  int nFrames = bt_outer - bt_inner;
157  if (skip > nFrames)
158  skip = nFrames;
159  for (int i = 0; i < skip; ++i) {
160  // for now, do not support M chords
161  lush_assoc_t as = lush_assoc_info__get_assoc(bt_inner[i].as_info);
162  assert(as == LUSH_ASSOC_NULL || as == LUSH_ASSOC_1_to_1 ||
163  as == LUSH_ASSOC_1_to_0);
164  }
165  return &bt_inner[skip];
166 }
167 
168 //
169 // Generate a backtrace, store it in the thread local data
170 // Return true/false success code
171 // Also, return (via reference params) backtrace beginning, backtrace end,
172 // and whether or not a trampoline was found.
173 //
174 // NOTE: This routine will stop the backtrace if a trampoline is encountered,
175 // but it will NOT update the trampoline data structures.
176 //
177 bool
179  ucontext_t* context,
180  int skipInner)
181 {
182  TMSG(BT, "Generate backtrace (no tramp), skip inner = %d", skipInner);
183  bt->has_tramp = false;
184  bt->n_trolls = 0;
185  bt->fence = FENCE_BAD;
186  bt->bottom_frame_elided = false;
187  bt->partial_unwind = true;
188 
189  step_state ret = STEP_ERROR; // default return value from stepper
190 
191  //--------------------------------------------------------------------
192  // note: these variables are not local variables so that if a SIGSEGV
193  // occurs and control returns up several procedure frames, the values
194  // are accessible to a dumping routine that will tell us where we ran
195  // into a problem.
196  //--------------------------------------------------------------------
197 
199  td->btbuf_cur = td->btbuf_beg; // innermost
200  td->btbuf_sav = td->btbuf_end;
201 
202  hpcrun_unw_cursor_t cursor;
203  hpcrun_unw_init_cursor(&cursor, context);
204 
205  do {
206  void* ip;
207  hpcrun_unw_get_ip_unnorm_reg(&cursor, &ip);
208 
209  if (hpcrun_trampoline_interior(ip)) {
210  // bail; we shouldn't be unwinding here. hpcrun is in the midst of
211  // counting a return from a sampled frame using a trampoline.
212  // drop the sample.
213  // FIXME: sharpen the information to indicate why the sample is
214  // being dropped.
215  hpcrun_unw_drop();
216  }
217 
218  else if (hpcrun_trampoline_at_entry(ip)) {
219  if (ret == STEP_ERROR){
220  // we are about to enter the trampoline code to synchronously
221  // record a return. for now, simply do nothing ...
222  // FIXME: with a bit more effort, we could charge
223  // the sample to the return address in the caller.
224  hpcrun_unw_drop();
225  }
226  else {
227  // we have encountered a trampoline in the middle of an unwind.
228  bt->has_tramp = true;
229  // no need to unwind further. the outer frames are already known.
230 
231  bt->fence = FENCE_TRAMP;
232  ret = STEP_STOP;
233  break;
234  }
235  }
236 
238 
239  td->btbuf_cur->cursor = cursor;
240  //Broken if HPC_UNW_LITE defined
242  &td->btbuf_cur->ip_norm);
243  td->btbuf_cur->ra_loc = NULL;
244 
245  td->btbuf_cur->the_function = cursor.the_function;
246 
247  frame_t* prev = td->btbuf_cur++;
248 
249  ret = hpcrun_unw_step(&cursor);
250  switch (ret) {
251  case STEP_TROLL:
252  bt->n_trolls++;
253  /* fallthrough */
254  default:
255  prev->ra_loc = hpcrun_unw_get_ra_loc(&cursor);
256  break;
257 
258  case STEP_ERROR:
260  break;
261  case STEP_STOP:
262  bt->fence = cursor.fence;
263  break;
264  }
265  } while (ret != STEP_ERROR && ret != STEP_STOP);
266 
267  TMSG(FENCE, "backtrace generation detects fence = %s", fence_enum_name(bt->fence));
268 
269  frame_t* bt_beg = td->btbuf_beg; // innermost, inclusive
270  frame_t* bt_last = td->btbuf_cur - 1; // outermost, inclusive
271 
272  if (skipInner) {
273  if (ENABLED(USE_TRAMP)){
274  //
275  // FIXME: For the moment, ignore skipInner issues with trampolines.
276  // Eventually, this will need to be addressed
277  //
278  TMSG(TRAMP, "WARNING: backtrace detects skipInner != 0 (skipInner = %d)",
279  skipInner);
280  }
281  TMSG(BT, "* BEFORE Skip inner correction, bt_beg = %p", bt_beg);
282  // adjust the returned backtrace according to the skipInner
283  bt_beg = hpcrun_skip_chords(bt_last, bt_beg, skipInner);
284  TMSG(BT, "* AFTER Skip inner correction, bt_beg = %p", bt_beg);
285  }
286 
287  bt->begin = bt_beg; // returned backtrace begin
288  // is buffer beginning
289  bt->last = bt_last; // returned backtrace last is
290  // last recorded element
291  // soft error mandates returning false
292  if (! (ret == STEP_STOP)) {
293  TMSG(BT, "** Soft Failure **");
294  return false;
295  }
296 
297  TMSG(BT, "succeeds");
298  bt->partial_unwind = false;
299  return true;
300 }
301 
302 //
303 // Do all of the raw backtrace generation, plus
304 // update the trampoline cached backtrace.
305 //
306 bool
308  ucontext_t* context, int skipInner)
309 {
311  context,
312  skipInner);
313  if (! ret ) return false;
314 
316 
317  bool tramp_found = bt->has_tramp;
318  frame_t* bt_beg = bt->begin;
319  frame_t* bt_last = bt->last;
320 
321  frame_t* bt_end = bt_last + 1; // outermost, exclusive
322  size_t new_frame_count = bt_end - bt_beg;
323 
324  if (ENABLED(USE_TRAMP)) {
325  if (tramp_found) {
326  TMSG(BACKTRACE, "tramp stop: conjoining backtraces");
327  TMSG(TRAMP, " FOUND TRAMP: constructing cached backtrace");
328  //
329  // join current backtrace fragment to previous trampoline-marked prefix
330  // and make this new conjoined backtrace the cached-backtrace
331  //
332  frame_t* prefix = td->tramp_frame;
333  TMSG(TRAMP, "Check: tramp prefix ra_loc = %p, addr@ra_loc = %p (?= %p tramp), retn_addr = %p",
334  prefix->ra_loc, *((void**) prefix->ra_loc), hpcrun_trampoline, td->tramp_retn_addr);
335  size_t old_frame_count = td->cached_bt_end - prefix;
336  TMSG(TRAMP, "Check: Old frame count = %d ?= %d (computed frame count)",
337  old_frame_count, td->cached_frame_count);
338 
339  size_t n_cached_frames = new_frame_count - 1;
340  hpcrun_cached_bt_adjust_size(n_cached_frames + old_frame_count);
341  TMSG(TRAMP, "cached trace size = (new frames) %d + (old frames) %d = %d",
342  n_cached_frames, old_frame_count, n_cached_frames + old_frame_count);
343  // put the old prefix in place
344  memmove(td->cached_bt + n_cached_frames, prefix,
345  sizeof(frame_t) * old_frame_count);
346 
347  // put the new suffix in place
348  memcpy(td->cached_bt, bt_beg, sizeof(frame_t) * n_cached_frames);
349 
350  // update the length of the conjoined backtrace
351  td->cached_bt_end = td->cached_bt + n_cached_frames + old_frame_count;
352  // maintain invariants
353  td->cached_frame_count = n_cached_frames + old_frame_count;
354  td->tramp_frame = td->cached_bt + n_cached_frames;
355  TMSG(TRAMP, "Check: tramp prefix ra_loc = %p, addr@ra_loc = %p (?= %p tramp), retn_addr = %p",
356  prefix->ra_loc, *((void**) prefix->ra_loc), hpcrun_trampoline, td->tramp_retn_addr);
357  }
358  else {
359  TMSG(TRAMP, "No tramp found: cached backtrace size = %d", new_frame_count);
360  hpcrun_cached_bt_adjust_size(new_frame_count);
361  size_t n_cached_frames = new_frame_count - 1;
362  TMSG(TRAMP, "Confirm: ra_loc(last bt frame) = %p", (bt_beg + n_cached_frames)->ra_loc);
363  memmove(td->cached_bt, bt_beg, sizeof(frame_t) * n_cached_frames);
364 
365  td->cached_bt_end = td->cached_bt + n_cached_frames;
366  td->cached_frame_count = n_cached_frames;
367  }
368  if (ENABLED(TRAMP)) {
369  TMSG(TRAMP, "Dump cached backtrace from backtrace construction");
371  }
372  }
373 
374  return true;
375 }
376 
377 
378 //***************************************************************************
379 // private operations
380 //***************************************************************************
381 
382 static void
383 lush_assoc_info2str(char* buf, size_t len, lush_assoc_info_t info)
384 {
385  // INVARIANT: buf must have at least LUSH_ASSOC_INFO_STR_MIN_LEN slots
386 
387  lush_assoc_t as = info.u.as;
388  unsigned info_len = info.u.len;
389 
390  const char* as_str = lush_assoc_tostr(as);
391  hpcrun_msg_ns(buf, LUSH_ASSOC_INFO_STR_MIN_LEN, "%s (%u)", as_str, info_len);
392  buf[LUSH_ASSOC_INFO_STR_MIN_LEN - 1] = '\0';
393 }
394 
395 static void
396 lush_lip2str(char* buf, size_t len, lush_lip_t* lip)
397 {
398  *buf = '\0';
399 
400  if (lip) {
401  for (int i = 0; i < LUSH_LIP_DATA8_SZ; ++i) {
402  if (i != 0) {
403  *(buf++) = ' ';
404  *(buf) = '\0';
405  len--;
406  }
407  int num = hpcrun_msg_ns(buf, len, "0x%"PRIx64, lip->data8[i]);
408  buf += num;
409  len -= num;
410  }
411  }
412 }
fence_enum_t fence
size_t cached_frame_count
Definition: thread_data.h:201
void hpcrun_stats_num_samples_dropped_inc(void)
Definition: hpcrun_stats.c:187
void hpcrun_cached_bt_adjust_size(size_t n)
Definition: thread_data.c:373
bool hpcrun_generate_backtrace(backtrace_info_t *bt, ucontext_t *context, int skipInner)
Definition: backtrace.c:307
void * ra_loc
Definition: frame.h:63
#define LUSH_LIP_DATA8_SZ
Definition: lush-support.h:309
fence_enum_t fence
size_t len
Definition: backtrace.h:84
frame_t * beg
Definition: backtrace.h:85
struct lush_assoc_info_u::lush_assoc_info_s u
void hpcrun_ensure_btbuf_avail(void)
Definition: thread_data.c:426
frame_t * end
Definition: backtrace.h:86
frame_t * cached_bt_end
Definition: thread_data.h:203
ip_normalized_t ip_norm
Definition: frame.h:61
frame_t * btbuf_cur
Definition: thread_data.h:184
ip_normalized_t the_function
Definition: frame.h:62
Definition: fmt.c:108
void hpcrun_trampoline(void)
Definition: aarch64-tramp.c:4
void * tramp_retn_addr
Definition: thread_data.h:199
bool hpcrun_generate_backtrace_no_trampoline(backtrace_info_t *bt, ucontext_t *context, int skipInner)
Definition: backtrace.c:178
char * name
Definition: loadmap.h:128
void hpcrun_bt_init(backtrace_t *bt, size_t size)
Definition: backtrace.c:142
frame_t * cached_bt
Definition: thread_data.h:202
static void lush_lip2str(char *buf, size_t len, lush_lip_t *lip)
Definition: backtrace.c:396
static char * prefix
Definition: common.c:164
frame_t * tramp_frame
Definition: thread_data.h:205
void hpcrun_unw_drop(void)
Definition: unw-throw.c:87
step_state hpcrun_unw_step(hpcrun_unw_cursor_t *c)
frame_t * btbuf_sav
Definition: thread_data.h:190
#define LUSH_LIP_STR_MIN_LEN
Definition: lush-support.h:356
int hpcrun_msg_ns(char *buf, size_t len, const char *fmt,...)
Definition: fmt.c:466
#define EMSG
Definition: messages.h:70
load_module_t * hpcrun_loadmap_findById(uint16_t id)
Definition: loadmap.c:301
static char buf[32]
Definition: StrUtil.cpp:240
void * hpcrun_malloc(size_t size)
Definition: mem.c:275
bool hpcrun_trampoline_at_entry(void *addr)
Definition: trampoline.c:118
hpcrun_unw_cursor_t cursor
Definition: frame.h:59
static lush_assoc_t lush_assoc_info__get_assoc(lush_assoc_info_t x)
Definition: lush-support.h:215
frame_t * hpcrun_skip_chords(frame_t *bt_outer, frame_t *bt_inner, int skip)
Definition: backtrace.c:152
enum lush_assoc lush_assoc_t
Definition: lush-support.h:164
#define TMSG(f,...)
Definition: messages.h:93
ip_normalized_t the_function
const char * lush_assoc_tostr(lush_assoc_t as)
Definition: lush-support.c:86
size_t size
Definition: backtrace.h:83
#define NULL
Definition: ElfHelper.cpp:85
static void lush_assoc_info2str(char *buf, size_t len, lush_assoc_info_t info)
Definition: backtrace.c:383
void hpcrun_trampoline_bt_dump(void)
Definition: trampoline.c:82
frame_t * btbuf_beg
Definition: thread_data.h:185
#define LUSH_ASSOC_INFO_STR_MIN_LEN
Definition: lush-support.h:295
int hpcrun_unw_get_ip_norm_reg(hpcrun_unw_cursor_t *c, ip_normalized_t *reg_value)
Definition: frame.h:58
void hpcrun_bt_dump(frame_t *unwind, const char *tag)
Definition: backtrace.c:90
frame_t * cur
Definition: backtrace.h:87
static char * fence_enum_name(fence_enum_t f)
Definition: fence_enum.h:24
static const unsigned int msg_limit
void hpcrun_unw_init_cursor(hpcrun_unw_cursor_t *cursor, void *context)
int hpcrun_unw_get_ip_unnorm_reg(hpcrun_unw_cursor_t *c, void **reg_value)
bool hpcrun_trampoline_interior(void *addr)
Definition: trampoline.c:109
void * hpcrun_unw_get_ra_loc(hpcrun_unw_cursor_t *c)
frame_t * btbuf_end
Definition: thread_data.h:189
step_state
Definition: unwind.h:124
uint64_t data8[LUSH_LIP_DATA8_SZ]
Definition: lush-support.h:312
#define ENABLED(f)
Definition: debug-flag.h:76
thread_data_t *(* hpcrun_get_thread_data)(void)
Definition: thread_data.c:168