1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2010, 2011 by FERMI NATIONAL ACCELERATOR LABORATORY
3
4Permission is hereby granted, free of charge, to any person obtaining
5a copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions:
11
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#include "compiler.h"
28
29#include <errno.h>
30#if HAVE_EXECINFO_H
31# include <execinfo.h>
32#else
33  extern int backtrace (void **, int);
34#endif
35#include <signal.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <libunwind.h>
41
42#define panic(args...)				\
43	{ fprintf (stderr, args); exit (-1); }
44
45#define SIG_STACK_SIZE 0x100000
46
47int verbose;
48int num_errors;
49
50/* These variables are global because they
51 * cause the signal stack to overflow */
52char buf[512], name[256];
53void *addresses[3][128];
54unw_cursor_t cursor;
55unw_context_t uc;
56
57static void
58do_backtrace (void)
59{
60  unw_word_t ip;
61  int ret = -UNW_ENOINFO;
62  int depth = 0;
63  int i, n, m;
64
65  if (verbose)
66    printf ("\tnormal trace:\n");
67
68  unw_getcontext (&uc);
69  if (unw_init_local (&cursor, &uc) < 0)
70    panic ("unw_init_local failed!\n");
71
72  do
73    {
74      unw_get_reg (&cursor, UNW_REG_IP, &ip);
75      addresses[0][depth] = (void *) ip;
76    }
77  while ((ret = unw_step (&cursor)) > 0 && ++depth < 128);
78
79  if (ret < 0)
80    {
81      unw_get_reg (&cursor, UNW_REG_IP, &ip);
82      printf ("FAILURE: unw_step() returned %d for ip=%lx\n", ret, (long) ip);
83      ++num_errors;
84    }
85
86  if (verbose)
87    for (i = 0; i < depth; ++i)
88      printf ("\t #%-3d ip=%p\n", i, addresses[0][i]);
89
90  if (verbose)
91    printf ("\n\tvia backtrace():\n");
92
93  n = backtrace (addresses[1], 128);
94
95  if (verbose)
96    for (i = 0; i < n; ++i)
97	printf ("\t #%-3d ip=%p\n", i, addresses[1][i]);
98
99  if (verbose)
100    printf ("\n\tvia unw_backtrace():\n");
101
102  m = unw_backtrace (addresses[2], 128);
103
104  if (verbose)
105    for (i = 0; i < m; ++i)
106	printf ("\t #%-3d ip=%p\n", i, addresses[2][i]);
107
108  if (m != depth+1)
109    {
110      printf ("FAILURE: unw_step() loop and unw_backtrace() depths differ: %d vs. %d\n", depth, m);
111      ++num_errors;
112    }
113
114  if (n != depth+1)
115    {
116      printf ("FAILURE: unw_step() loop and backtrace() depths differ: %d vs. %d\n", depth, n);
117      ++num_errors;
118    }
119
120  if (n == m)
121    for (i = 1; i < n; ++i)
122      /* Allow one in difference in comparison, trace returns adjusted addresses. */
123      if (labs((unw_word_t) addresses[1][i] - (unw_word_t) addresses[2][i]) > 1)
124	{
125          printf ("FAILURE: backtrace() and unw_backtrace() addresses differ at %d: %p vs. %p\n",
126		  i, addresses[1][n], addresses[2][n]);
127          ++num_errors;
128	}
129
130  if (n == depth+1)
131    for (i = 1; i < depth; ++i)
132      /* Allow one in difference in comparison, trace returns adjusted addresses. */
133      if (labs((unw_word_t) addresses[0][i] - (unw_word_t) addresses[1][i]) > 1)
134	{
135          printf ("FAILURE: unw_step() loop and backtrace() addresses differ at %d: %p vs. %p\n",
136		  i, addresses[0][n], addresses[1][n]);
137          ++num_errors;
138	}
139}
140
141void
142foo (long val UNUSED)
143{
144  do_backtrace ();
145}
146
147void
148bar (long v)
149{
150  extern long f (long);
151  int arr[v];
152
153  /* This is a vain attempt to use up lots of registers to force
154     the frame-chain info to be saved on the memory stack on ia64.
155     It happens to work with gcc v3.3.4 and gcc v3.4.1 but perhaps
156     not with any other compiler.  */
157  foo (f (arr[0]) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
158       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
159       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
160       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
161       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
162       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
163       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
164       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
165       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
166       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
167       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
168       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
169       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
170       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
171       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
172       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
173       + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
174       ))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
175       )))))))))))))))))))))))))))))))))))))))))))))))))))))));
176}
177
178void
179sighandler (int signal, void *siginfo UNUSED, void *context)
180{
181  ucontext_t *uc UNUSED;
182  int sp;
183
184  uc = context;
185
186  if (verbose)
187    {
188      printf ("sighandler: got signal %d, sp=%p", signal, &sp);
189#if UNW_TARGET_IA64
190# if defined(__linux__)
191      printf (" @ %lx", uc->uc_mcontext.sc_ip);
192# else
193      {
194	uint16_t reason;
195	uint64_t ip;
196
197	__uc_get_reason (uc, &reason);
198	__uc_get_ip (uc, &ip);
199	printf (" @ %lx (reason=%d)", ip, reason);
200      }
201# endif
202#elif UNW_TARGET_X86
203#if defined __linux__
204      printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]);
205#elif defined __FreeBSD__
206      printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_eip);
207#endif
208#elif UNW_TARGET_X86_64
209#if defined __linux__
210      printf (" @ %lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
211#elif defined __FreeBSD__
212      printf (" @ %lx", (unsigned long) uc->uc_mcontext.mc_rip);
213#endif
214#elif defined UNW_TARGET_ARM
215      printf (" @ %lx", (unsigned long) uc->uc_mcontext.arm_pc);
216#endif
217      printf ("\n");
218    }
219  do_backtrace();
220}
221
222int
223main (int argc, char **argv UNUSED)
224{
225  struct sigaction act;
226  stack_t stk;
227
228  verbose = (argc > 1);
229
230  if (verbose)
231    printf ("Normal backtrace:\n");
232
233  bar (1);
234
235  memset (&act, 0, sizeof (act));
236  act.sa_handler = (void (*)(int)) sighandler;
237  act.sa_flags = SA_SIGINFO;
238  if (sigaction (SIGTERM, &act, NULL) < 0)
239    panic ("sigaction: %s\n", strerror (errno));
240
241  if (verbose)
242    printf ("\nBacktrace across signal handler:\n");
243  kill (getpid (), SIGTERM);
244
245  if (verbose)
246    printf ("\nBacktrace across signal handler on alternate stack:\n");
247  stk.ss_sp = malloc (SIG_STACK_SIZE);
248  if (!stk.ss_sp)
249    panic ("failed to allocate %u bytes\n", SIG_STACK_SIZE);
250  stk.ss_size = SIG_STACK_SIZE;
251  stk.ss_flags = 0;
252  if (sigaltstack (&stk, NULL) < 0)
253    panic ("sigaltstack: %s\n", strerror (errno));
254
255  memset (&act, 0, sizeof (act));
256  act.sa_handler = (void (*)(int)) sighandler;
257  act.sa_flags = SA_ONSTACK | SA_SIGINFO;
258  if (sigaction (SIGTERM, &act, NULL) < 0)
259    panic ("sigaction: %s\n", strerror (errno));
260  kill (getpid (), SIGTERM);
261
262  if (num_errors > 0)
263    {
264      fprintf (stderr, "FAILURE: detected %d errors\n", num_errors);
265      exit (-1);
266    }
267
268  if (verbose)
269    printf ("SUCCESS.\n");
270
271  signal (SIGTERM, SIG_DFL);
272  stk.ss_flags = SS_DISABLE;
273  sigaltstack (&stk, NULL);
274  free (stk.ss_sp);
275
276  return 0;
277}
278