8313f781af4c61f71b08bb3f83812d72cb17654d
[debugger.git] / debugger.c
1 /* Copyright 2007, Red Hat Inc.  */
2
3 #include <unistd.h>
4 #include <assert.h>
5 #include <stdlib.h>
6 #include <signal.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <sys/ptrace.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <limits.h>
13 #include <string.h>
14 #include <time.h>
15
16 #include "debugger.h"
17
18
19 #if 0
20 #define USLEEP (1000000 / 2)
21 #endif
22 #define TIMEOUT_SECS 20
23
24
25 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
26
27 void delay (void)
28 {
29 #ifdef USLEEP
30   int i;
31
32   i = usleep (USLEEP);
33   assert (i == 0);
34 #endif
35 }
36
37 static __attribute__((__noreturn__)) void crash (void)
38 {
39 #if 0
40   void *array[0x100];
41   int count;
42 #endif
43   char command[256];
44
45   fputs (">>> CRASH START\n", stderr);
46 #if 0
47   count = backtrace (array, ARRAY_SIZE (array));
48   backtrace_symbols_fd (array, count, STDERR_FILENO);
49 #endif
50   snprintf (command, sizeof (command), "echo -e \"bt\\nquit\""
51             " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d"
52             " /proc/%d/exe %d </dev/null;rm -f /tmp/debugger.%d",
53             (int) getpid(), (int) getpid(), (int) getpid(), (int) getpid(),
54             (int) getpid());
55   system (command);
56   fputs (">>> CRASH FINISH\n", stderr);
57   abort ();
58 }
59
60 enum state
61   {
62     /* Separate the valid `1 << enum state' and `enum state' ranges.  */
63     STATE_FIRST = 4,
64     STATE_INSTABLE,
65     STATE_ENOENT,
66     STATE_SLEEPING,
67     STATE_RUNNING,
68     STATE_STOPPED,
69     STATE_PTRACED,
70     STATE_ZOMBIE,
71     STATE_DEAD,
72     STATE_LAST
73   };
74
75 static const char *state_to_name (enum state state)
76 {
77   switch (state)
78     {
79       case STATE_INSTABLE: return "STATE_INSTABLE";
80       case STATE_ENOENT:   return "STATE_ENOENT";
81       case STATE_SLEEPING: return "STATE_SLEEPING";
82       case STATE_RUNNING:  return "STATE_RUNNING";
83       case STATE_STOPPED:  return "STATE_STOPPED";
84       case STATE_PTRACED:  return "STATE_PTRACED";
85       case STATE_ZOMBIE:   return "STATE_ZOMBIE";
86       case STATE_DEAD:     return "STATE_DEAD";
87       default: crash ();
88     }
89   /* NOTREACHED */
90   crash ();
91 }
92
93 static enum state state_get (pid_t pid)
94 {
95   char status_name[32];
96   char line[LINE_MAX];
97   FILE *f;
98   enum state found;
99
100   delay ();
101
102   snprintf (status_name, sizeof (status_name), "/proc/%d/status", (int) pid);
103   f = fopen (status_name, "r");
104   if (f == NULL && errno == ENOENT)
105     found = STATE_ENOENT;
106   else if (f == NULL)
107     {
108       fprintf (stderr, "errno = %d\n", errno);
109       crash ();
110     }
111   else
112     {
113       int i;
114
115       found = STATE_INSTABLE;
116       while (errno = 0, fgets (line, sizeof (line), f) != NULL)
117         {
118           const char *const string = "State:\t";
119           const size_t length = sizeof "State:\t" - 1;
120
121           if (strncmp (line, string, length) != 0)
122             continue;
123           if (strcmp (line + length, "S (sleeping)\n") == 0)
124             found = STATE_SLEEPING;
125           else if (strcmp (line + length, "R (running)\n") == 0)
126             found = STATE_RUNNING;
127           else if (strcmp (line + length, "T (stopped)\n") == 0)
128             found = STATE_STOPPED;
129           else if (strcmp (line + length, "T (tracing stop)\n") == 0)
130             found = STATE_PTRACED;
131           else if (strcmp (line + length, "Z (zombie)\n") == 0)
132             found = STATE_ZOMBIE;
133           /* FIXME: What does it mean?  */
134           else if (strcmp (line + length, "X (dead)\n") == 0)
135             found = STATE_DEAD;
136           else
137             {
138               fprintf (stderr, "Found an unknown state: %s", line + length);
139               crash ();
140             }
141         }
142       assert (found != STATE_INSTABLE || errno == ESRCH);
143       i = fclose (f);
144       assert (i == 0);
145     }
146   return found;
147 }
148
149 #define STATE(pid, expect_mask) state ((pid), (expect_mask), #expect_mask )
150
151 static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mask_string)
152 {
153   enum state found;
154   time_t timeout = time (NULL) + TIMEOUT_SECS;
155
156   /* Sanity check `1 << enum state' was not misplaced with `enum state'.  */
157   assert (1 << (STATE_FIRST + 1) >= STATE_LAST);
158   assert (expect_mask != 0);
159 #define MASK_UNDER_EXCLUSIVE(bit) ((1 << (bit)) - 1)
160 #define MASK_ABOVE_INCLUSIVE(bit) (~MASK_UNDER_EXCLUSIVE (bit))
161   assert ((expect_mask & MASK_UNDER_EXCLUSIVE (STATE_FIRST + 1)) == 0);
162   assert ((expect_mask & MASK_ABOVE_INCLUSIVE (STATE_LAST)) == 0);
163 #undef MASK_ABOVE_INCLUSIVE
164 #undef MASK_UNDER_EXCLUSIVE
165
166   do
167     {
168       found = state_get (pid);
169
170       if (((1 << found) & expect_mask) != 0)
171         return found;
172     }
173   while (time (NULL) < timeout);
174
175   fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n",
176            (int) pid, state_to_name (found), expect_mask_string);
177   crash ();
178 }
179
180 /* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH.  */
181 int attach_redelivered;
182
183 int attach (pid_t pid)
184 {
185   int i;
186   int status;
187   int stopped;
188
189   attach_redelivered = 0;
190
191   delay ();
192
193 #if 1
194   stopped = (STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)
195                          | (1 << STATE_STOPPED)) == STATE_STOPPED);
196 #endif
197
198   i = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
199   assert (i == 0);
200
201   /* FIXME: Why it does not work?
202      Disable also the STATE () call above.  */
203 #if 0
204   delay();
205
206   i = ptrace (PTRACE_CONT, pid, (void *) 1, (void *) SIGSTOP);
207   /* `STOPPED == 1' may be false, even if the process was not stopped.  */
208   if (i == 0)
209     stopped = 1;
210   else if (errno == ESRCH)
211     stopped = 0;
212   else
213     crash ();
214 #endif
215
216   for (;;)
217     {
218       delay ();
219
220       i = waitpid (pid, &status, 0);
221       assert (i == pid);
222       if (!WIFSTOPPED (status))
223         {
224           /* Process may have exited.  */
225           fprintf (stderr, "PID %d waitpid(2) status %d\n", (int) pid,
226                    status);
227           exit (EXIT_FAILURE);
228         }
229       if (WSTOPSIG (status) == SIGSTOP)
230         break;
231
232       if (attach_redelivered == 0)
233         attach_redelivered = WSTOPSIG (status);
234       else
235         attach_redelivered = -1;
236
237       delay ();
238
239       /* Re-deliver the signal received before SIGSTOP.
240          It happens with about only 1:200000 probability.  */
241       i = ptrace (PTRACE_CONT, pid, (void *) 1,
242                   (void *) (unsigned long) WSTOPSIG (status));
243       assert (i == 0);
244     }
245
246   return stopped;
247 }
248
249 void detach (pid_t pid, int stopped)
250 {
251   int i;
252
253   delay ();
254
255   i = ptrace (PTRACE_DETACH, pid, NULL,
256               (void *) (unsigned long) (stopped ? SIGSTOP : 0));
257   assert (i == 0);
258
259   delay ();
260 }
261
262 #ifndef LIBRARY
263
264 int main (int argc, char **argv)
265 {
266   pid_t pid;
267   int stopped;
268
269   if (argc != 2)
270     {
271       fprintf (stderr, "Usage: %s <PID>\n", argv[0]);
272       exit (EXIT_FAILURE);
273     }
274
275   pid = atoi (argv[1]);
276
277   stopped = attach (pid);
278
279   detach (pid, stopped);
280
281   return EXIT_SUCCESS;
282 }
283
284 #endif /* !LIBRARY */