1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2004 Hewlett-Packard Co
3 	Contributed by David Mosberger-Tang <davidm@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
do_backtrace(int may_print,int get_proc_name)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
sighandler(int signal)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
main(int argc,char ** argv UNUSED)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