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