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
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#include <config.h>
27
28#ifdef HAVE_TTRACE
29
30int
31main (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
51extern char **environ;
52
53static const int nerrors_max = 100;
54
55int nerrors;
56int verbose;
57int print_names = 1;
58
59enum
60  {
61    INSTRUCTION,
62    SYSCALL,
63    TRIGGER
64  }
65trace_mode = SYSCALL;
66
67#define panic(args...)						\
68	do { fprintf (stderr, args); ++nerrors; } while (0)
69
70static unw_addr_space_t as;
71static struct UPT_info *ui;
72
73static int killed;
74
75void
76do_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
164static pid_t target_pid;
165static void target_pid_kill (void)
166{
167  kill (target_pid, SIGKILL);
168}
169
170int
171main (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