1 /* Copyright 2007, Red Hat Inc. */
9 #include <sys/ptrace.h>
10 #include <sys/types.h>
20 #define USLEEP (1000000 / 2)
22 #define TIMEOUT_SECS 20
23 /* LOOPS_MIN is a safety as QEMU clock time sucks.
24 100000 is 4s natively and 53s in QEMU. */
25 #define LOOPS_MIN 500000
28 #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
40 static __attribute__((__noreturn__)) void crash (void)
48 fputs (">>> CRASH START\n", stderr);
50 count = backtrace (array, ARRAY_SIZE (array));
51 backtrace_symbols_fd (array, count, STDERR_FILENO);
53 snprintf (command, sizeof (command), "echo -e \"bt\\nquit\""
54 " >/tmp/debugger.%d; gdb --batch -nx --command=/tmp/debugger.%d"
55 " /proc/%d/exe %d </dev/null;rm -f /tmp/debugger.%d",
56 (int) getpid(), (int) getpid(), (int) getpid(), (int) getpid(),
59 fputs (">>> CRASH FINISH\n", stderr);
65 /* Separate the valid `1 << enum state' and `enum state' ranges. */
78 static const char *state_to_name (enum state state)
82 case STATE_INSTABLE: return "STATE_INSTABLE";
83 case STATE_ENOENT: return "STATE_ENOENT";
84 case STATE_SLEEPING: return "STATE_SLEEPING";
85 case STATE_RUNNING: return "STATE_RUNNING";
86 case STATE_STOPPED: return "STATE_STOPPED";
87 case STATE_PTRACED: return "STATE_PTRACED";
88 case STATE_ZOMBIE: return "STATE_ZOMBIE";
89 case STATE_DEAD: return "STATE_DEAD";
96 static enum state state_get (pid_t pid)
105 snprintf (status_name, sizeof (status_name), "/proc/%d/status", (int) pid);
106 f = fopen (status_name, "r");
107 if (f == NULL && errno == ENOENT)
108 found = STATE_ENOENT;
111 fprintf (stderr, "errno = %d\n", errno);
118 found = STATE_INSTABLE;
119 while (errno = 0, fgets (line, sizeof (line), f) != NULL)
121 const char *const string = "State:\t";
122 const size_t length = sizeof "State:\t" - 1;
124 if (strncmp (line, string, length) != 0)
126 if (strcmp (line + length, "S (sleeping)\n") == 0)
127 found = STATE_SLEEPING;
128 else if (strcmp (line + length, "R (running)\n") == 0)
129 found = STATE_RUNNING;
130 else if (strcmp (line + length, "T (stopped)\n") == 0)
131 found = STATE_STOPPED;
132 else if (strcmp (line + length, "T (tracing stop)\n") == 0)
133 found = STATE_PTRACED;
134 else if (strcmp (line + length, "Z (zombie)\n") == 0)
135 found = STATE_ZOMBIE;
136 /* FIXME: What does it mean? */
137 else if (strcmp (line + length, "X (dead)\n") == 0)
141 fprintf (stderr, "Found an unknown state: %s", line + length);
145 assert (found != STATE_INSTABLE || errno == ESRCH);
152 #define STATE(pid, expect_mask) state ((pid), (expect_mask), #expect_mask )
154 static enum state state (pid_t pid, unsigned expect_mask, const char *expect_mask_string)
157 time_t timeout = time (NULL) + TIMEOUT_SECS;
160 /* Sanity check `1 << enum state' was not misplaced with `enum state'. */
161 assert (1 << (STATE_FIRST + 1) >= STATE_LAST);
162 assert (expect_mask != 0);
163 #define MASK_UNDER_EXCLUSIVE(bit) ((1 << (bit)) - 1)
164 #define MASK_ABOVE_INCLUSIVE(bit) (~MASK_UNDER_EXCLUSIVE (bit))
165 assert ((expect_mask & MASK_UNDER_EXCLUSIVE (STATE_FIRST + 1)) == 0);
166 assert ((expect_mask & MASK_ABOVE_INCLUSIVE (STATE_LAST)) == 0);
167 #undef MASK_ABOVE_INCLUSIVE
168 #undef MASK_UNDER_EXCLUSIVE
172 found = state_get (pid);
174 if (((1 << found) & expect_mask) != 0)
177 while (loops++ < LOOPS_MIN || time (NULL) < timeout);
179 fprintf (stderr, "Found for PID %d state %s but expecting (%s)\n",
180 (int) pid, state_to_name (found), expect_mask_string);
184 /* Debugging only: Number of signal needing redelivery on PTRACE_ATTACH. */
185 int attach_redelivered;
187 int attach (pid_t pid)
193 attach_redelivered = 0;
198 stopped = (STATE (pid, (1 << STATE_SLEEPING) | (1 << STATE_RUNNING)
199 | (1 << STATE_STOPPED)) == STATE_STOPPED);
202 i = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
205 /* FIXME: Why it does not work?
206 Disable also the STATE () call above. */
210 i = ptrace (PTRACE_CONT, pid, (void *) 1, (void *) SIGSTOP);
211 /* `STOPPED == 1' may be false, even if the process was not stopped. */
214 else if (errno == ESRCH)
224 i = waitpid (pid, &status, 0);
226 if (!WIFSTOPPED (status))
228 /* Process may have exited. */
229 fprintf (stderr, "PID %d waitpid(2) status %d\n", (int) pid,
233 if (WSTOPSIG (status) == SIGSTOP)
236 if (attach_redelivered == 0)
237 attach_redelivered = WSTOPSIG (status);
239 attach_redelivered = -1;
243 /* Re-deliver the signal received before SIGSTOP.
244 It happens with about only 1:200000 probability. */
245 i = ptrace (PTRACE_CONT, pid, (void *) 1,
246 (void *) (unsigned long) WSTOPSIG (status));
253 void detach (pid_t pid, int stopped)
259 i = ptrace (PTRACE_DETACH, pid, NULL,
260 (void *) (unsigned long) (stopped ? SIGSTOP : 0));
268 int main (int argc, char **argv)
275 fprintf (stderr, "Usage: %s <PID>\n", argv[0]);
279 pid = atoi (argv[1]);
281 stopped = attach (pid);
283 detach (pid, stopped);
288 #endif /* !LIBRARY */