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
main(void)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
do_backtrace(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;
target_pid_kill(void)165 static void target_pid_kill (void)
166 {
167   kill (target_pid, SIGKILL);
168 }
169 
170 int
main(int argc,char ** argv)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 */
358