Home | History | Annotate | Download | only in tests
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2003-2004 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) 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 */
    358