disable UNW_ARM_METHOD_FRAME routine
[external/libunwind.git] / tests / test-ptrace.c
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2003-2004 Hewlett-Packard Co
3         Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5 This file is part of libunwind.
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26 #include <config.h>
27
28 #ifdef HAVE_TTRACE
29
30 int
31 main (void)
32 {
33   printf ("FAILURE: ttrace() not supported yet\n");
34   return -1;
35 }
36
37 #else /* !HAVE_TTRACE */
38
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <libunwind-ptrace.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #include <sys/ptrace.h>
49 #include <sys/wait.h>
50
51 extern char **environ;
52
53 static const int nerrors_max = 100;
54
55 int nerrors;
56 int verbose;
57 int print_names = 1;
58
59 enum
60   {
61     INSTRUCTION,
62     SYSCALL,
63     TRIGGER
64   }
65 trace_mode = SYSCALL;
66
67 #define panic(args...)                                          \
68         do { fprintf (stderr, args); ++nerrors; } while (0)
69
70 static unw_addr_space_t as;
71 static struct UPT_info *ui;
72
73 static int killed;
74
75 void
76 do_backtrace (void)
77 {
78   unw_word_t ip, sp, start_ip = 0, off;
79   int n = 0, ret;
80   unw_proc_info_t pi;
81   unw_cursor_t c;
82   char buf[512];
83   size_t len;
84
85   ret = unw_init_remote (&c, as, ui);
86   if (ret < 0)
87     panic ("unw_init_remote() failed: ret=%d\n", ret);
88
89   do
90     {
91       if ((ret = unw_get_reg (&c, UNW_REG_IP, &ip)) < 0
92           || (ret = unw_get_reg (&c, UNW_REG_SP, &sp)) < 0)
93         panic ("unw_get_reg/unw_get_proc_name() failed: ret=%d\n", ret);
94
95       if (n == 0)
96         start_ip = ip;
97
98       buf[0] = '\0';
99       if (print_names)
100         unw_get_proc_name (&c, buf, sizeof (buf), &off);
101
102       if (verbose)
103         {
104           if (off)
105             {
106               len = strlen (buf);
107               if (len >= sizeof (buf) - 32)
108                 len = sizeof (buf) - 32;
109               sprintf (buf + len, "+0x%lx", (unsigned long) off);
110             }
111           printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
112         }
113
114       if ((ret = unw_get_proc_info (&c, &pi)) < 0)
115         panic ("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
116       else if (verbose)
117         printf ("\tproc=%016lx-%016lx\n\thandler=%lx lsda=%lx",
118                 (long) pi.start_ip, (long) pi.end_ip,
119                 (long) pi.handler, (long) pi.lsda);
120
121 #if UNW_TARGET_IA64
122       {
123         unw_word_t bsp;
124
125         if ((ret = unw_get_reg (&c, UNW_IA64_BSP, &bsp)) < 0)
126           panic ("unw_get_reg() failed: ret=%d\n", ret);
127         else if (verbose)
128           printf (" bsp=%lx", bsp);
129       }
130 #endif
131       if (verbose)
132         printf ("\n");
133
134       ret = unw_step (&c);
135       if (ret < 0)
136         {
137           unw_get_reg (&c, UNW_REG_IP, &ip);
138           panic ("FAILURE: unw_step() returned %d for ip=%lx (start ip=%lx)\n",
139                  ret, (long) ip, (long) start_ip);
140         }
141
142       if (++n > 64)
143         {
144           /* guard against bad unwind info in old libraries... */
145           panic ("too deeply nested---assuming bogus unwind (start ip=%lx)\n",
146                  (long) start_ip);
147           break;
148         }
149       if (nerrors > nerrors_max)
150         {
151           panic ("Too many errors (%d)!\n", nerrors);
152           break;
153         }
154     }
155   while (ret > 0);
156
157   if (ret < 0)
158     panic ("unwind failed with ret=%d\n", ret);
159
160   if (verbose)
161     printf ("================\n\n");
162 }
163
164 static pid_t target_pid;
165 static void target_pid_kill (void)
166 {
167   kill (target_pid, SIGKILL);
168 }
169
170 int
171 main (int argc, char **argv)
172 {
173   int status, pid, pending_sig, optind = 1, state = 1;
174
175   as = unw_create_addr_space (&_UPT_accessors, 0);
176   if (!as)
177     panic ("unw_create_addr_space() failed");
178
179   if (argc == 1)
180     {
181       static char *args[] = { "self", "/bin/ls", "/usr", NULL };
182
183       /* automated test case */
184       argv = args;
185     }
186   else if (argc > 1)
187     while (argv[optind][0] == '-')
188       {
189         if (strcmp (argv[optind], "-v") == 0)
190           ++optind, verbose = 1;
191         else if (strcmp (argv[optind], "-i") == 0)
192           ++optind, trace_mode = INSTRUCTION;   /* backtrace at each insn */
193         else if (strcmp (argv[optind], "-s") == 0)
194           ++optind, trace_mode = SYSCALL;       /* backtrace at each syscall */
195         else if (strcmp (argv[optind], "-t") == 0)
196           /* Execute until raise(SIGUSR1), then backtrace at each insn
197              until raise(SIGUSR2).  */
198           ++optind, trace_mode = TRIGGER;
199         else if (strcmp (argv[optind], "-c") == 0)
200           /* Enable caching of unwind-info.  */
201           ++optind, unw_set_caching_policy (as, UNW_CACHE_GLOBAL);
202         else if (strcmp (argv[optind], "-n") == 0)
203           /* Don't look-up and print symbol names.  */
204           ++optind, print_names = 0;
205         else
206           fprintf(stderr, "unrecognized option: %s\n", argv[optind++]);
207         if (optind >= argc)
208           break;
209       }
210
211   target_pid = fork ();
212   if (!target_pid)
213     {
214       /* child */
215
216       if (!verbose)
217         dup2 (open ("/dev/null", O_WRONLY), 1);
218
219 #if HAVE_DECL_PTRACE_TRACEME
220       ptrace (PTRACE_TRACEME, 0, 0, 0);
221 #elif HAVE_DECL_PT_TRACE_ME
222       ptrace (PT_TRACE_ME, 0, 0, 0);
223 #else
224 #error Trace me
225 #endif
226
227       if ((argc > 1) && (optind == argc)) {
228         fprintf(stderr, "Need to specify a command line for the child\n");
229         exit (-1);
230       }
231       execve (argv[optind], argv + optind, environ);
232       _exit (-1);
233     }
234   atexit (target_pid_kill);
235
236   ui = _UPT_create (target_pid);
237
238   while (nerrors <= nerrors_max)
239     {
240       pid = wait4 (-1, &status, 0, NULL);
241       if (pid == -1)
242         {
243           if (errno == EINTR)
244             continue;
245
246           panic ("wait4() failed (errno=%d)\n", errno);
247         }
248       pending_sig = 0;
249       if (WIFSIGNALED (status) || WIFEXITED (status)
250           || (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP))
251         {
252           if (WIFEXITED (status))
253             {
254               if (WEXITSTATUS (status) != 0)
255                 panic ("child's exit status %d\n", WEXITSTATUS (status));
256               break;
257             }
258           else if (WIFSIGNALED (status))
259             {
260               if (!killed)
261                 panic ("child terminated by signal %d\n", WTERMSIG (status));
262               break;
263             }
264           else
265             {
266               pending_sig = WSTOPSIG (status);
267               /* Avoid deadlock:  */
268               if (WSTOPSIG (status) == SIGKILL)
269                 break;
270               if (trace_mode == TRIGGER)
271                 {
272                   if (WSTOPSIG (status) == SIGUSR1)
273                     state = 0;
274                   else if  (WSTOPSIG (status) == SIGUSR2)
275                     state = 1;
276                 }
277               if (WSTOPSIG (status) != SIGUSR1 && WSTOPSIG (status) != SIGUSR2)
278                 {
279                   static int count = 0;
280
281                   if (count++ > 100)
282                     {
283                       panic ("Too many child unexpected signals (now %d)\n",
284                              WSTOPSIG (status));
285                         killed = 1;
286                     }
287                 }
288             }
289         }
290
291       switch (trace_mode)
292         {
293         case TRIGGER:
294           if (state)
295 #if HAVE_DECL_PTRACE_CONT
296             ptrace (PTRACE_CONT, target_pid, 0, 0);
297 #elif HAVE_DECL_PT_CONTINUE
298             ptrace (PT_CONTINUE, target_pid, (caddr_t)1, 0);
299 #else
300 #error Port me
301 #endif
302           else
303             {
304               do_backtrace ();
305 #if HAVE_DECL_PTRACE_SINGLESTEP
306               ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
307 #elif HAVE_DECL_PT_STEP
308               ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig);
309 #else
310 #error Singlestep me
311 #endif
312             }
313           break;
314
315         case SYSCALL:
316           if (!state)
317             do_backtrace ();
318           state ^= 1;
319 #if HAVE_DECL_PTRACE_SYSCALL
320           ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig);
321 #elif HAVE_DECL_PT_SYSCALL
322           ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig);
323 #else
324 #error Syscall me
325 #endif
326           break;
327
328         case INSTRUCTION:
329           do_backtrace ();
330 #if HAVE_DECL_PTRACE_SINGLESTEP
331               ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
332 #elif HAVE_DECL_PT_STEP
333               ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig);
334 #else
335 #error Singlestep me
336 #endif
337           break;
338         }
339       if (killed)
340         kill (target_pid, SIGKILL);
341     }
342
343   _UPT_destroy (ui);
344   unw_destroy_addr_space (as);
345
346   if (nerrors)
347     {
348       printf ("FAILURE: detected %d errors\n", nerrors);
349       exit (-1);
350     }
351   if (verbose)
352     printf ("SUCCESS\n");
353
354   return 0;
355 }
356
357 #endif /* !HAVE_TTRACE */