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