#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "compiler.h"
#include <errno.h>
#if HAVE_EXECINFO_H
# include <execinfo.h>
#else
extern int backtrace (void **, int);
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include <libunwind.h>
#include "ident.h"
#define panic(...) \
{ fprintf (stderr, __VA_ARGS__); exit (-1); }
#define SIG_STACK_SIZE 0x100000
int verbose;
int num_errors;
char buf[512], name[256];
void *addresses[3][128];
unw_cursor_t cursor;
unw_context_t uc;
static void
do_backtrace (void)
{
unw_word_t ip;
int ret = -UNW_ENOINFO;
int depth = 0;
int i, n, m;
if (verbose)
printf ("\tnormal trace:\n");
unw_getcontext (&uc);
if (unw_init_local (&cursor, &uc) < 0)
panic ("unw_init_local failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
addresses[0][depth] = (void *) ip;
}
while ((ret = unw_step (&cursor)) > 0 && ++depth < 128);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip);
++num_errors;
}
if (verbose)
for (i = 0; i < depth; ++i)
printf ("\t #%-3d ip=%p\n", i, addresses[0][i]);
if (verbose)
printf ("\n\tvia backtrace():\n");
n = backtrace (addresses[1], 128);
if (verbose)
for (i = 0; i < n; ++i)
printf ("\t #%-3d ip=%p\n", i, addresses[1][i]);
if (verbose)
printf ("\n\tvia unw_backtrace():\n");
m = unw_backtrace (addresses[2], 128);
if (verbose)
for (i = 0; i < m; ++i)
printf ("\t #%-3d ip=%p\n", i, addresses[2][i]);
if (m != depth+1)
{
printf ("FAILURE: unw_step() loop and unw_backtrace() depths differ: %d vs. %d\n", depth, m);
++num_errors;
}
if (n != depth+1)
{
printf ("FAILURE: unw_step() loop and backtrace() depths differ: %d vs. %d\n", depth, n);
++num_errors;
}
if (n == m)
for (i = 1; i < n; ++i)
if (labs (addresses[1][i] - addresses[2][i]) > 1)
{
printf ("FAILURE: backtrace() and unw_backtrace() addresses differ at %d: %p vs. %p\n",
i, addresses[1][i], addresses[2][i]);
++num_errors;
}
if (n == depth+1)
for (i = 1; i < depth; ++i)
if (labs (addresses[0][i] - addresses[1][i]) > 1)
{
printf ("FAILURE: unw_step() loop and backtrace() addresses differ at %d: %p vs. %p\n",
i, addresses[0][i], addresses[1][i]);
++num_errors;
}
}
void
do_backtrace_with_context(void *context)
{
unw_word_t ip;
int ret = -UNW_ENOINFO;
int depth = 0;
int i, m;
if (verbose)
printf ("\tnormal trace:\n");
if (unw_init_local2 (&cursor, (unw_context_t*)context, UNW_INIT_SIGNAL_FRAME) < 0)
panic ("unw_init_local2 failed!\n");
do
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
addresses[0][depth] = (void *) ip;
}
while ((ret = unw_step (&cursor)) > 0 && ++depth < 128);
if (ret < 0)
{
unw_get_reg (&cursor, UNW_REG_IP, &ip);
printf ("FAILURE: unw_step() returned %d for ip=%#010lx\n", ret, (long) ip);
++num_errors;
}
if (verbose)
for (i = 0; i < depth; ++i)
printf ("\t #%-3d ip=%p\n", i, addresses[0][i]);
if (verbose)
printf ("\n\tvia unw_backtrace2():\n");
m = unw_backtrace2 (addresses[1], 128, (unw_context_t*)context, UNW_INIT_SIGNAL_FRAME);
if (verbose)
for (i = 0; i < m; ++i)
printf ("\t #%-3d ip=%p\n", i, addresses[1][i]);
if (m != depth+1)
{
printf ("FAILURE: unw_step() loop and unw_backtrace2() depths differ: %d vs. %d\n", depth, m);
++num_errors;
}
if (m == depth + 1)
for (i = 0; i < depth; ++i)
if ( labs(addresses[0][i] - addresses[1][i]) > 1)
{
printf ("FAILURE: unw_step() loop and unw_backtrace2() addresses differ at %d: %p vs. %p\n",
i, addresses[0][i], addresses[1][i]);
++num_errors;
}
}
void
foo (long val UNUSED)
{
do_backtrace ();
}
void
bar (long v)
{
int arr[v];
arr[0] = 0;
foo (f (arr[0]) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v)
+ (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + (f (v) + f (v))
))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
)))))))))))))))))))))))))))))))))))))))))))))))))))))));
}
void
sighandler (int signal, siginfo_t *siginfo UNUSED, void *context)
{
ucontext_t *uc UNUSED;
int sp;
uc = context;
if (verbose)
{
printf ("sighandler: got signal %d, sp=%p", signal, (void *)&sp);
#if UNW_TARGET_IA64
# if defined(__linux__)
printf (" @ %#010lx", uc->uc_mcontext.sc_ip);
# else
{
uint16_t reason;
uint64_t ip;
__uc_get_reason (uc, &reason);
__uc_get_ip (uc, &ip);
printf (" @ %#010lx (reason=%d)", ip, reason);
}
# endif
#elif UNW_TARGET_X86
#if defined __linux__
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.gregs[REG_EIP]);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.mc_eip);
#endif
#elif UNW_TARGET_X86_64
#if defined __linux__ || defined __sun
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.gregs[REG_RIP]);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.mc_rip);
#endif
#elif defined UNW_TARGET_ARM
#if defined __linux__
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.arm_pc);
#elif defined __FreeBSD__
printf (" @ %#010lx", (unsigned long) uc->uc_mcontext.__gregs[_REG_PC]);
#endif
#endif
printf ("\n");
}
do_backtrace();
do_backtrace_with_context(context);
}
int
main (int argc, char **argv UNUSED)
{
struct sigaction act;
#ifdef HAVE_SIGALTSTACK
stack_t stk;
#endif
verbose = (argc > 1);
if (verbose)
printf ("Normal backtrace:\n");
bar (1);
memset (&act, 0, sizeof (act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
if (verbose)
printf ("\nBacktrace across signal handler:\n");
kill (getpid (), SIGTERM);
#ifdef HAVE_SIGALTSTACK
if (verbose)
printf ("\nBacktrace across signal handler on alternate stack:\n");
stk.ss_sp = malloc (SIG_STACK_SIZE);
if (!stk.ss_sp)
panic ("failed to allocate %u bytes\n", SIG_STACK_SIZE);
stk.ss_size = SIG_STACK_SIZE;
stk.ss_flags = 0;
if (sigaltstack (&stk, NULL) < 0)
panic ("sigaltstack: %s\n", strerror (errno));
memset (&act, 0, sizeof (act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
if (sigaction (SIGTERM, &act, NULL) < 0)
panic ("sigaction: %s\n", strerror (errno));
kill (getpid (), SIGTERM);
#endif
if (num_errors > 0)
{
fprintf (stderr, "FAILURE: detected %d errors\n", num_errors);
exit (-1);
}
if (verbose)
printf ("SUCCESS.\n");
signal (SIGTERM, SIG_DFL);
#ifdef HAVE_SIGALTSTACK
stk.ss_flags = SS_DISABLE;
sigaltstack (&stk, NULL);
free (stk.ss_sp);
#endif
return 0;
}