test-ptrace.c revision b0048ee76213f9ff6d6e24a91eeeb124aa30d248
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 (int argc, char **argv)
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 <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include <sys/ptrace.h>
48#include <sys/wait.h>
49
50int nerrors;
51int verbose;
52int print_names = 1;
53
54enum
55  {
56    INSTRUCTION,
57    SYSCALL,
58    TRIGGER
59  }
60trace_mode = SYSCALL;
61
62#define panic(args...)						\
63	do { fprintf (stderr, args); ++nerrors; } while (0)
64
65static unw_addr_space_t as;
66static struct UPT_info *ui;
67
68void
69do_backtrace (pid_t target_pid)
70{
71  int n = 0, ret;
72  unw_proc_info_t pi;
73  unw_word_t ip, sp, start_ip;
74  unw_cursor_t c;
75  char buf[512];
76
77  ret = unw_init_remote (&c, as, ui);
78  if (ret < 0)
79    panic ("unw_init_remote() failed: ret=%d\n", ret);
80
81  do
82    {
83      if ((ret = unw_get_reg (&c, UNW_REG_IP, &ip)) < 0
84	  || (ret = unw_get_reg (&c, UNW_REG_SP, &sp)) < 0)
85	panic ("unw_get_reg/unw_get_proc_name() failed: ret=%d\n", ret);
86
87      if (n == 0)
88	start_ip = ip;
89
90      buf[0] = '\0';
91      if (print_names)
92	unw_get_proc_name (&c, buf, sizeof (buf), NULL);
93
94      if (verbose)
95	printf ("%016lx %-32s (sp=%016lx)\n", (long) ip, buf, (long) sp);
96
97      if ((ret = unw_get_proc_info (&c, &pi)) < 0)
98	panic ("unw_get_proc_info() failed: ret=%d\n", ret);
99      else if (verbose)
100	printf ("\tproc=%016lx-%016lx\n\thandler=%lx lsda=%lx",
101		(long) pi.start_ip, (long) pi.end_ip,
102		(long) pi.handler, (long) pi.lsda);
103
104#if UNW_TARGET_IA64
105      {
106	unw_word_t bsp;
107
108	if ((ret = unw_get_reg (&c, UNW_IA64_BSP, &bsp)) < 0)
109	  panic ("unw_get_reg() failed: ret=%d\n", ret);
110	else if (verbose)
111	  printf (" bsp=%lx", bsp);
112      }
113#endif
114      if (verbose)
115	printf ("\n");
116
117      ret = unw_step (&c);
118      if (ret < 0)
119	{
120	  unw_get_reg (&c, UNW_REG_IP, &ip);
121	  panic ("FAILURE: unw_step() returned %d for ip=%lx (start ip=%lx)\n",
122		 ret, (long) ip, (long) start_ip);
123	}
124
125      if (++n > 32)
126	{
127	  /* guard against bad unwind info in old libraries... */
128	  panic ("too deeply nested---assuming bogus unwind\n");
129	  break;
130	}
131    }
132  while (ret > 0);
133
134  if (ret < 0)
135    panic ("unwind failed with ret=%d\n", ret);
136
137  if (verbose)
138    printf ("================\n\n");
139}
140
141int
142main (int argc, char **argv)
143{
144  int status, pid, pending_sig, optind = 1, state = 1;
145  pid_t target_pid;
146
147  as = unw_create_addr_space (&_UPT_accessors, 0);
148  if (!as)
149    panic ("unw_create_addr_space() failed");
150
151  if (argc == 1)
152    {
153      char *args[] = { "self", "/bin/ls", "/usr", NULL };
154
155      /* automated test case */
156      argv = args;
157    }
158  else if (argc > 1)
159    while (argv[optind][0] == '-')
160      {
161	if (strcmp (argv[optind], "-v") == 0)
162	  ++optind, verbose = 1;
163	else if (strcmp (argv[optind], "-i") == 0)
164	  ++optind, trace_mode = INSTRUCTION;	/* backtrace at each insn */
165	else if (strcmp (argv[optind], "-s") == 0)
166	  ++optind, trace_mode = SYSCALL;	/* backtrace at each syscall */
167	else if (strcmp (argv[optind], "-t") == 0)
168	  /* Execute until raise(SIGUSR1), then backtrace at each insn
169	     until raise(SIGUSR2).  */
170	  ++optind, trace_mode = TRIGGER;
171	else if (strcmp (argv[optind], "-c") == 0)
172	  /* Enable caching of unwind-info.  */
173	  ++optind, unw_set_caching_policy (as, UNW_CACHE_GLOBAL);
174	else if (strcmp (argv[optind], "-n") == 0)
175	  /* Don't look-up and print symbol names.  */
176	  ++optind, print_names = 0;
177      }
178
179  target_pid = fork ();
180  if (!target_pid)
181    {
182      /* child */
183
184      if (!verbose)
185	dup2 (open ("/dev/null", O_WRONLY), 1);
186
187      ptrace (PTRACE_TRACEME, 0, 0, 0);
188      execve (argv[optind], argv + optind, environ);
189      _exit (-1);
190    }
191
192  ui = _UPT_create (target_pid);
193
194  while (1)
195    {
196      pid = wait4 (-1, &status,  0, 0);
197      if (pid == -1)
198	{
199	  if (errno == EINTR)
200	    continue;
201
202	  panic ("wait4() failed (errno=%d)\n", errno);
203	}
204      pending_sig = 0;
205      if (WIFSIGNALED (status) || WIFEXITED (status)
206	  || (WIFSTOPPED (status) && WSTOPSIG (status) != SIGTRAP))
207	{
208	  if (WIFEXITED (status))
209	    {
210	      if (WEXITSTATUS (status) != 0)
211		panic ("child's exit status %d\n", WEXITSTATUS (status));
212	      break;
213	    }
214	  else if (WIFSIGNALED (status))
215	    {
216	      panic ("child terminated by signal %d\n", WTERMSIG (status));
217	      break;
218	    }
219	  else
220	    {
221	      pending_sig = WSTOPSIG (status);
222	      if (trace_mode == TRIGGER)
223		{
224		  if (WSTOPSIG (status) == SIGUSR1)
225		    state = 0;
226		  else if  (WSTOPSIG (status) == SIGUSR2)
227		    state = 1;
228		}
229	    }
230	}
231
232      switch (trace_mode)
233	{
234	case TRIGGER:
235	  if (state)
236	    ptrace (PTRACE_CONT, target_pid, 0, 0);
237	  else
238	    {
239	      do_backtrace (target_pid);
240	      ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
241	    }
242	  break;
243
244	case SYSCALL:
245	  if (!state)
246	    do_backtrace (target_pid);
247	  state ^= 1;
248	  ptrace (PTRACE_SYSCALL, target_pid, 0, pending_sig);
249	  break;
250
251	case INSTRUCTION:
252	  do_backtrace (target_pid);
253	  ptrace (PTRACE_SINGLESTEP, target_pid, 0, pending_sig);
254	  break;
255	}
256    }
257
258  _UPT_destroy (ui);
259
260  if (nerrors)
261    {
262      printf ("FAILURE: detected %d errors\n", nerrors);
263      exit (-1);
264    }
265  if (verbose)
266    printf ("SUCCESS\n");
267
268  return 0;
269}
270
271#endif /* !HAVE_TTRACE */
272