• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", "ls", "/", NULL };
182 
183       /* automated test case */
184       argv = args;
185 
186       /* Unless the args array is 'walked' the child
187          process is unable to access it and dies with a segfault */
188       fprintf(stderr, "Automated test (%s,%s,%s,%s)\n",
189               args[0],args[1],args[2],args[3]);
190     }
191   else if (argc > 1)
192     while (argv[optind][0] == '-')
193       {
194 	if (strcmp (argv[optind], "-v") == 0)
195 	  ++optind, verbose = 1;
196 	else if (strcmp (argv[optind], "-i") == 0)
197 	  ++optind, trace_mode = INSTRUCTION;	/* backtrace at each insn */
198 	else if (strcmp (argv[optind], "-s") == 0)
199 	  ++optind, trace_mode = SYSCALL;	/* backtrace at each syscall */
200 	else if (strcmp (argv[optind], "-t") == 0)
201 	  /* Execute until raise(SIGUSR1), then backtrace at each insn
202 	     until raise(SIGUSR2).  */
203 	  ++optind, trace_mode = TRIGGER;
204 	else if (strcmp (argv[optind], "-c") == 0)
205 	  /* Enable caching of unwind-info.  */
206 	  ++optind, unw_set_caching_policy (as, UNW_CACHE_GLOBAL);
207 	else if (strcmp (argv[optind], "-n") == 0)
208 	  /* Don't look-up and print symbol names.  */
209 	  ++optind, print_names = 0;
210 	else
211 	  fprintf(stderr, "unrecognized option: %s\n", argv[optind++]);
212         if (optind >= argc)
213           break;
214       }
215 
216   target_pid = fork ();
217   if (!target_pid)
218     {
219       /* child */
220 
221       if (!verbose)
222 	dup2 (open ("/dev/null", O_WRONLY), 1);
223 
224 #if HAVE_DECL_PTRACE_TRACEME
225       ptrace (PTRACE_TRACEME, 0, 0, 0);
226 #elif HAVE_DECL_PT_TRACE_ME
227       ptrace (PT_TRACE_ME, 0, 0, 0);
228 #else
229 #error Trace me
230 #endif
231 
232       if ((argc > 1) && (optind == argc)) {
233         fprintf(stderr, "Need to specify a command line for the child\n");
234         exit (-1);
235       }
236       execvpe (argv[optind], argv + optind, environ);
237       _exit (-1);
238     }
239   atexit (target_pid_kill);
240 
241   ui = _UPT_create (target_pid);
242 
243   while (nerrors <= nerrors_max)
244     {
245       pid = wait4 (-1, &status, 0, NULL);
246       if (pid == -1)
247 	{
248 	  if (errno == EINTR)
249 	    continue;
250 
251 	  panic ("wait4() failed (errno=%d)\n", errno);
252 	}
253       pending_sig = 0;
254       if (WIFSIGNALED (status) || WIFEXITED (status)
255 	  || (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP))
256 	{
257 	  if (WIFEXITED (status))
258 	    {
259 	      if (WEXITSTATUS (status) != 0)
260 		panic ("child's exit status %d\n", WEXITSTATUS (status));
261 	      break;
262 	    }
263 	  else if (WIFSIGNALED (status))
264 	    {
265 	      if (!killed)
266 		panic ("child terminated by signal %d\n", WTERMSIG (status));
267 	      break;
268 	    }
269 	  else
270 	    {
271 	      pending_sig = WSTOPSIG (status);
272 	      /* Avoid deadlock:  */
273 	      if (WSTOPSIG (status) == SIGKILL)
274 	        break;
275 	      if (trace_mode == TRIGGER)
276 		{
277 		  if (WSTOPSIG (status) == SIGUSR1)
278 		    state = 0;
279 		  else if  (WSTOPSIG (status) == SIGUSR2)
280 		    state = 1;
281 		}
282 	      if (WSTOPSIG (status) != SIGUSR1 && WSTOPSIG (status) != SIGUSR2)
283 	        {
284 		  static int count = 0;
285 
286 		  if (count++ > 100)
287 		    {
288 		      panic ("Too many child unexpected signals (now %d)\n",
289 			     WSTOPSIG (status));
290 			killed = 1;
291 		    }
292 	        }
293 	    }
294 	}
295 
296       switch (trace_mode)
297 	{
298 	case TRIGGER:
299 	  if (state)
300 #if HAVE_DECL_PTRACE_CONT
301 	    ptrace (PTRACE_CONT, target_pid, 0, 0);
302 #elif HAVE_DECL_PT_CONTINUE
303 	    ptrace (PT_CONTINUE, target_pid, (caddr_t)1, 0);
304 #else
305 #error Port me
306 #endif
307 	  else
308 	    {
309 	      do_backtrace ();
310 #if HAVE_DECL_PTRACE_SINGLESTEP
311 	      if (ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig) < 0)
312           {
313             panic ("ptrace(PTRACE_SINGLESTEP) failed (errno=%d)\n", errno);
314             killed = 1;
315           }
316 #elif HAVE_DECL_PT_STEP
317 	      if (ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig) < 0)
318           {
319             panic ("ptrace(PT_STEP) failed (errno=%d)\n", errno);
320             killed = 1;
321           }
322 #else
323 #error Singlestep me
324 #endif
325 	    }
326 	  break;
327 
328 	case SYSCALL:
329 	  if (!state)
330 	    do_backtrace ();
331 	  state ^= 1;
332 #if HAVE_DECL_PTRACE_SYSCALL
333 	  ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig);
334 #elif HAVE_DECL_PT_SYSCALL
335 	  ptrace (PT_SYSCALL, target_pid, (caddr_t)1, pending_sig);
336 #else
337 #error Syscall me
338 #endif
339 	  break;
340 
341 	case INSTRUCTION:
342 	  do_backtrace ();
343 #if HAVE_DECL_PTRACE_SINGLESTEP
344 	      ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
345 #elif HAVE_DECL_PT_STEP
346 	      ptrace (PT_STEP, target_pid, (caddr_t)1, pending_sig);
347 #else
348 #error Singlestep me
349 #endif
350 	  break;
351 	}
352       if (killed)
353         kill (target_pid, SIGKILL);
354     }
355 
356   _UPT_destroy (ui);
357   unw_destroy_addr_space (as);
358 
359   if (nerrors)
360     {
361       printf ("FAILURE: detected %d errors\n", nerrors);
362       exit (-1);
363     }
364   if (verbose)
365     printf ("SUCCESS\n");
366 
367   return 0;
368 }
369 
370 #endif /* !HAVE_TTRACE */
371