Home | History | Annotate | Download | only in tests
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2004 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining
      6 a copy of this software and associated documentation files (the
      7 "Software"), to deal in the Software without restriction, including
      8 without limitation the rights to use, copy, modify, merge, publish,
      9 distribute, sublicense, and/or sell copies of the Software, and to
     10 permit persons to whom the Software is furnished to do so, subject to
     11 the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be
     14 included in all copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     23 
     24 /* Check whether basic unwinding truly is async-signal safe.  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include "config.h"
     28 #endif
     29 
     30 #include "compiler.h"
     31 
     32 #include <signal.h>
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 
     38 #include <sys/time.h>
     39 
     40 #define UNW_LOCAL_ONLY
     41 #include <libunwind.h>
     42 
     43 static const int nerrors_max = 100;
     44 
     45 struct itimerval interval =
     46   {
     47     .it_interval = { .tv_sec = 0, .tv_usec = 0 },
     48     .it_value    = { .tv_sec = 0, .tv_usec = 1000 }
     49   };
     50 
     51 int verbose;
     52 int nerrors;
     53 int sigcount;
     54 
     55 #ifndef CONFIG_BLOCK_SIGNALS
     56 /* When libunwind is configured with --enable-block-signals=no, the caller
     57    is responsible for preventing recursion via signal handlers.
     58    We use a simple global here.  In a multithreaded program, one would use
     59    a thread-local variable.  */
     60 int recurcount;
     61 #endif
     62 
     63 #define panic(args...)					\
     64 	{ ++nerrors; fprintf (stderr, args); return; }
     65 
     66 static void
     67 do_backtrace (int may_print, int get_proc_name)
     68 {
     69   char buf[512], name[256];
     70   unw_cursor_t cursor;
     71   unw_word_t ip, sp, off;
     72   unw_context_t uc;
     73   int ret;
     74   int depth = 0;
     75 
     76 #ifndef CONFIG_BLOCK_SIGNALS
     77   if (recurcount > 0)
     78     return;
     79   recurcount += 1;
     80 #endif
     81 
     82   unw_getcontext (&uc);
     83   if (unw_init_local (&cursor, &uc) < 0)
     84     panic ("unw_init_local failed!\n");
     85 
     86   do
     87     {
     88       unw_get_reg (&cursor, UNW_REG_IP, &ip);
     89       unw_get_reg (&cursor, UNW_REG_SP, &sp);
     90 
     91       buf[0] = '\0';
     92       if (get_proc_name || (may_print && verbose))
     93 	{
     94 	  ret = unw_get_proc_name (&cursor, name, sizeof (name), &off);
     95 	  if (ret == 0 && (may_print && verbose))
     96 	    {
     97 	      if (off)
     98 		snprintf (buf, sizeof (buf), "<%s+0x%lx>", name, (long) off);
     99 	      else
    100 		{
    101 		  size_t len = strlen (name);
    102 		  buf[0] = '<';
    103 		  memcpy (buf + 1, name, len);
    104 		  buf[len + 1] = '>';
    105 		  buf[len + 2] = '\0';
    106 		}
    107 	    }
    108 	}
    109 
    110       if (may_print && verbose)
    111 	printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
    112 
    113       ret = unw_step (&cursor);
    114       if (ret < 0)
    115 	{
    116 	  unw_get_reg (&cursor, UNW_REG_IP, &ip);
    117 	  panic ("FAILURE: unw_step() returned %d for ip=%lx\n",
    118 		 ret, (long) ip);
    119 	}
    120       if (depth++ > 100)
    121         {
    122 	  panic ("FAILURE: unw_step() looping over %d iterations\n", depth);
    123 	  break;
    124         }
    125     }
    126   while (ret > 0);
    127 
    128 #ifndef CONFIG_BLOCK_SIGNALS
    129   recurcount -= 1;
    130 #endif
    131 }
    132 
    133 void
    134 sighandler (int signal)
    135 {
    136   if (verbose)
    137     printf ("sighandler(signal=%d, count=%d)\n", signal, sigcount);
    138 
    139   do_backtrace (1, 1);
    140 
    141   ++sigcount;
    142 
    143   if (sigcount == 100)
    144     unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
    145   else if (sigcount == 200)
    146     unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
    147   else if (sigcount == 300 || nerrors > nerrors_max)
    148     {
    149       if (nerrors > nerrors_max)
    150         panic ("Too many errors (%d)\n", nerrors);
    151       if (nerrors)
    152 	{
    153 	  fprintf (stderr, "FAILURE: detected %d errors\n", nerrors);
    154 	  exit (-1);
    155 	}
    156       if (verbose)
    157 	printf ("SUCCESS.\n");
    158       exit (0);
    159     }
    160   setitimer (ITIMER_VIRTUAL, &interval, NULL);
    161 }
    162 
    163 int
    164 main (int argc, char **argv UNUSED)
    165 {
    166   struct sigaction act;
    167   long i = 0;
    168 
    169   if (argc > 1)
    170     verbose = 1;
    171 
    172   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
    173 
    174   memset (&act, 0, sizeof (act));
    175   act.sa_handler = sighandler;
    176   act.sa_flags = SA_SIGINFO;
    177   sigaction (SIGVTALRM, &act, NULL);
    178 
    179   setitimer (ITIMER_VIRTUAL, &interval, NULL);
    180 
    181   while (1)
    182     {
    183       if (0 && verbose)
    184 	printf ("%s: starting backtrace\n", __FUNCTION__);
    185       do_backtrace (0, (i++ % 100) == 0);
    186       if (nerrors > nerrors_max)
    187         {
    188 	  fprintf (stderr, "Too many errors (%d)\n", nerrors);
    189 	  exit (-1);
    190         }
    191     }
    192   return (0);
    193 }
    194