Initial import
[external/libunwind.git] / tests / test-async-sig.c
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
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 }