blob: b9195355075ab3e9540dd0e880be38b1cd08556a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <stdarg.h>
9#include <unistd.h>
10#include <signal.h>
11#include <errno.h>
12#include <sched.h>
13#include <string.h>
14#include <sys/mman.h>
15#include <sys/time.h>
16#include <sys/wait.h>
17#include "user.h"
18#include "sysdep/ptrace.h"
19#include "sigcontext.h"
20#include "sysdep/sigcontext.h"
21#include "os.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include "user_util.h"
23#include "mem_user.h"
24#include "process.h"
25#include "kern_util.h"
26#include "chan_user.h"
27#include "ptrace_user.h"
Jeff Dikecd2ee4a2005-05-05 16:15:32 -070028#include "irq_user.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include "mode.h"
30#include "tt.h"
31
32static int tracer_winch[2];
33
34int is_tracer_winch(int pid, int fd, void *data)
35{
Jeff Dikecd2ee4a2005-05-05 16:15:32 -070036 if(pid != os_getpgrp())
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 return(0);
38
39 register_winch_irq(tracer_winch[0], fd, -1, data);
40 return(1);
41}
42
43static void tracer_winch_handler(int sig)
44{
45 int n;
46 char c = 1;
47
48 n = os_write_file(tracer_winch[1], &c, sizeof(c));
49 if(n != sizeof(c))
50 printk("tracer_winch_handler - write failed, err = %d\n", -n);
51}
52
53/* Called only by the tracing thread during initialization */
54
55static void setup_tracer_winch(void)
56{
57 int err;
58
59 err = os_pipe(tracer_winch, 1, 1);
60 if(err < 0){
61 printk("setup_tracer_winch : os_pipe failed, err = %d\n", -err);
62 return;
63 }
64 signal(SIGWINCH, tracer_winch_handler);
65}
66
67void attach_process(int pid)
68{
69 if((ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) ||
70 (ptrace(PTRACE_CONT, pid, 0, 0) < 0))
71 tracer_panic("OP_FORK failed to attach pid");
72 wait_for_stop(pid, SIGSTOP, PTRACE_CONT, NULL);
73 if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0)
74 tracer_panic("OP_FORK: PTRACE_SETOPTIONS failed, errno = %d", errno);
75 if(ptrace(PTRACE_CONT, pid, 0, 0) < 0)
76 tracer_panic("OP_FORK failed to continue process");
77}
78
79void tracer_panic(char *format, ...)
80{
81 va_list ap;
82
83 va_start(ap, format);
84 vprintf(format, ap);
85 va_end(ap);
86 printf("\n");
87 while(1) pause();
88}
89
90static void tracer_segv(int sig, struct sigcontext sc)
91{
Bodo Stroesserc5784552005-05-05 16:15:31 -070092 struct faultinfo fi;
93 GET_FAULTINFO_FROM_SC(fi, &sc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 printf("Tracing thread segfault at address 0x%lx, ip 0x%lx\n",
Bodo Stroesserc5784552005-05-05 16:15:31 -070095 FAULT_ADDRESS(fi), SC_IP(&sc));
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 while(1)
97 pause();
98}
99
100/* Changed early in boot, and then only read */
101int debug = 0;
102int debug_stop = 1;
103int debug_parent = 0;
104int honeypot = 0;
105
106static int signal_tramp(void *arg)
107{
108 int (*proc)(void *);
109
110 if(honeypot && munmap((void *) (host_task_size - 0x10000000),
111 0x10000000))
112 panic("Unmapping stack failed");
113 if(ptrace(PTRACE_TRACEME, 0, 0, 0) < 0)
114 panic("ptrace PTRACE_TRACEME failed");
115 os_stop_process(os_getpid());
116 change_sig(SIGWINCH, 0);
117 signal(SIGUSR1, SIG_IGN);
118 change_sig(SIGCHLD, 0);
119 signal(SIGSEGV, (__sighandler_t) sig_handler);
120 set_cmdline("(idle thread)");
121 set_init_pid(os_getpid());
Jeff Dikecd2ee4a2005-05-05 16:15:32 -0700122 init_irq_signals(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 proc = arg;
124 return((*proc)(NULL));
125}
126
127static void sleeping_process_signal(int pid, int sig)
128{
129 switch(sig){
130 /* These two result from UML being ^Z-ed and bg-ed. PTRACE_CONT is
131 * right because the process must be in the kernel already.
132 */
133 case SIGCONT:
134 case SIGTSTP:
135 if(ptrace(PTRACE_CONT, pid, 0, sig) < 0)
136 tracer_panic("sleeping_process_signal : Failed to "
137 "continue pid %d, signal = %d, "
138 "errno = %d\n", pid, sig, errno);
139 break;
140
141 /* This happens when the debugger (e.g. strace) is doing system call
142 * tracing on the kernel. During a context switch, the current task
143 * will be set to the incoming process and the outgoing process will
144 * hop into write and then read. Since it's not the current process
145 * any more, the trace of those will land here. So, we need to just
146 * PTRACE_SYSCALL it.
147 */
148 case (SIGTRAP + 0x80):
149 if(ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0)
150 tracer_panic("sleeping_process_signal : Failed to "
151 "PTRACE_SYSCALL pid %d, errno = %d\n",
152 pid, errno);
153 break;
154 case SIGSTOP:
155 break;
156 default:
157 tracer_panic("sleeping process %d got unexpected "
158 "signal : %d\n", pid, sig);
159 break;
160 }
161}
162
163/* Accessed only by the tracing thread */
164int debugger_pid = -1;
165int debugger_parent = -1;
166int debugger_fd = -1;
167int gdb_pid = -1;
168
169struct {
170 int pid;
171 int signal;
172 unsigned long addr;
173 struct timeval time;
174} signal_record[1024][32];
175
176int signal_index[32];
177int nsignals = 0;
178int debug_trace = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180extern void signal_usr1(int sig);
181
182int tracing_pid = -1;
183
184int tracer(int (*init_proc)(void *), void *sp)
185{
186 void *task = NULL;
187 int status, pid = 0, sig = 0, cont_type, tracing = 0, op = 0;
188 int proc_id = 0, n, err, old_tracing = 0, strace = 0;
189 int local_using_sysemu = 0;
Jeff Dike469226a2006-07-10 04:45:13 -0700190
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 signal(SIGPIPE, SIG_IGN);
192 setup_tracer_winch();
193 tracing_pid = os_getpid();
194 printf("tracing thread pid = %d\n", tracing_pid);
195
196 pid = clone(signal_tramp, sp, CLONE_FILES | SIGCHLD, init_proc);
197 CATCH_EINTR(n = waitpid(pid, &status, WUNTRACED));
198 if(n < 0){
199 printf("waitpid on idle thread failed, errno = %d\n", errno);
200 exit(1);
201 }
202 if (ptrace(PTRACE_OLDSETOPTIONS, pid, 0, (void *)PTRACE_O_TRACESYSGOOD) < 0) {
203 printf("Failed to PTRACE_SETOPTIONS for idle thread, errno = %d\n", errno);
204 exit(1);
205 }
206 if((ptrace(PTRACE_CONT, pid, 0, 0) < 0)){
207 printf("Failed to continue idle thread, errno = %d\n", errno);
208 exit(1);
209 }
210
211 signal(SIGSEGV, (sighandler_t) tracer_segv);
212 signal(SIGUSR1, signal_usr1);
213 if(debug_trace){
214 printf("Tracing thread pausing to be attached\n");
215 stop();
216 }
217 if(debug){
218 if(gdb_pid != -1)
219 debugger_pid = attach_debugger(pid, gdb_pid, 1);
220 else debugger_pid = init_ptrace_proxy(pid, 1, debug_stop);
221 if(debug_parent){
222 debugger_parent = os_process_parent(debugger_pid);
223 init_parent_proxy(debugger_parent);
224 err = attach(debugger_parent);
225 if(err){
226 printf("Failed to attach debugger parent %d, "
227 "errno = %d\n", debugger_parent, -err);
228 debugger_parent = -1;
229 }
230 else {
231 if(ptrace(PTRACE_SYSCALL, debugger_parent,
232 0, 0) < 0){
233 printf("Failed to continue debugger "
234 "parent, errno = %d\n", errno);
235 debugger_parent = -1;
236 }
237 }
238 }
239 }
240 set_cmdline("(tracing thread)");
241 while(1){
242 CATCH_EINTR(pid = waitpid(-1, &status, WUNTRACED));
243 if(pid <= 0){
244 if(errno != ECHILD){
245 printf("wait failed - errno = %d\n", errno);
246 }
247 continue;
248 }
249 if(pid == debugger_pid){
250 int cont = 0;
251
252 if(WIFEXITED(status) || WIFSIGNALED(status))
253 debugger_pid = -1;
254 /* XXX Figure out how to deal with gdb and SMP */
255 else cont = debugger_signal(status, cpu_tasks[0].pid);
256 if(cont == PTRACE_SYSCALL) strace = 1;
257 continue;
258 }
259 else if(pid == debugger_parent){
260 debugger_parent_signal(status, pid);
261 continue;
262 }
263 nsignals++;
264 if(WIFEXITED(status)) ;
265#ifdef notdef
266 {
267 printf("Child %d exited with status %d\n", pid,
268 WEXITSTATUS(status));
269 }
270#endif
271 else if(WIFSIGNALED(status)){
272 sig = WTERMSIG(status);
273 if(sig != 9){
274 printf("Child %d exited with signal %d\n", pid,
275 sig);
276 }
277 }
278 else if(WIFSTOPPED(status)){
279 proc_id = pid_to_processor_id(pid);
280 sig = WSTOPSIG(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 if(proc_id == -1){
282 sleeping_process_signal(pid, sig);
283 continue;
284 }
285
286 task = cpu_tasks[proc_id].task;
287 tracing = is_tracing(task);
288 old_tracing = tracing;
289
290 /* Assume: no syscall, when coming from user */
291 if ( tracing )
292 do_sigtrap(task);
293
294 switch(sig){
295 case SIGUSR1:
296 sig = 0;
297 op = do_proc_op(task, proc_id);
298 switch(op){
299 /*
300 * This is called when entering user mode; after
301 * this, we start intercepting syscalls.
302 *
303 * In fact, a process is started in kernel mode,
304 * so with is_tracing() == 0 (and that is reset
305 * when executing syscalls, since UML kernel has
306 * the right to do syscalls);
307 */
308 case OP_TRACE_ON:
309 arch_leave_kernel(task, pid);
310 tracing = 1;
311 break;
312 case OP_REBOOT:
313 case OP_HALT:
314 unmap_physmem();
315 kmalloc_ok = 0;
316 os_kill_ptraced_process(pid, 0);
317 /* Now let's reap remaining zombies */
318 errno = 0;
319 do {
320 waitpid(-1, &status,
321 WUNTRACED);
322 } while (errno != ECHILD);
323 return(op == OP_REBOOT);
324 case OP_NONE:
325 printf("Detaching pid %d\n", pid);
326 detach(pid, SIGSTOP);
327 continue;
328 default:
329 break;
330 }
331 /* OP_EXEC switches host processes on us,
332 * we want to continue the new one.
333 */
334 pid = cpu_tasks[proc_id].pid;
335 break;
336 case (SIGTRAP + 0x80):
337 if(!tracing && (debugger_pid != -1)){
338 child_signal(pid, status & 0x7fff);
339 continue;
340 }
341 tracing = 0;
342 /* local_using_sysemu has been already set
343 * below, since if we are here, is_tracing() on
344 * the traced task was 1, i.e. the process had
345 * already run through one iteration of the
346 * loop which executed a OP_TRACE_ON request.*/
347 do_syscall(task, pid, local_using_sysemu);
348 sig = SIGUSR2;
349 break;
350 case SIGTRAP:
351 if(!tracing && (debugger_pid != -1)){
352 child_signal(pid, status);
353 continue;
354 }
355 tracing = 0;
356 break;
357 case SIGPROF:
358 if(tracing) sig = 0;
359 break;
360 case SIGCHLD:
361 case SIGHUP:
362 sig = 0;
363 break;
364 case SIGSEGV:
365 case SIGIO:
366 case SIGALRM:
367 case SIGVTALRM:
368 case SIGFPE:
369 case SIGBUS:
370 case SIGILL:
371 case SIGWINCH:
372
373 default:
374 tracing = 0;
375 break;
376 }
377 set_tracing(task, tracing);
378
379 if(!tracing && old_tracing)
380 arch_enter_kernel(task, pid);
381
382 if(!tracing && (debugger_pid != -1) && (sig != 0) &&
383 (sig != SIGALRM) && (sig != SIGVTALRM) &&
384 (sig != SIGSEGV) && (sig != SIGTRAP) &&
385 (sig != SIGUSR2) && (sig != SIGIO) &&
386 (sig != SIGFPE)){
387 child_signal(pid, status);
388 continue;
389 }
390
391 local_using_sysemu = get_using_sysemu();
392
393 if(tracing)
394 cont_type = SELECT_PTRACE_OPERATION(local_using_sysemu,
395 singlestepping(task));
396 else if((debugger_pid != -1) && strace)
397 cont_type = PTRACE_SYSCALL;
398 else
399 cont_type = PTRACE_CONT;
400
401 if(ptrace(cont_type, pid, 0, sig) != 0){
402 tracer_panic("ptrace failed to continue "
403 "process - errno = %d\n",
404 errno);
405 }
406 }
407 }
408 return(0);
409}
410
411static int __init uml_debug_setup(char *line, int *add)
412{
413 char *next;
414
415 debug = 1;
416 *add = 0;
417 if(*line != '=') return(0);
418 line++;
419
420 while(line != NULL){
421 next = strchr(line, ',');
422 if(next) *next++ = '\0';
423
424 if(!strcmp(line, "go")) debug_stop = 0;
425 else if(!strcmp(line, "parent")) debug_parent = 1;
426 else printf("Unknown debug option : '%s'\n", line);
427
428 line = next;
429 }
430 return(0);
431}
432
433__uml_setup("debug", uml_debug_setup,
434"debug\n"
435" Starts up the kernel under the control of gdb. See the \n"
436" kernel debugging tutorial and the debugging session pages\n"
437" at http://user-mode-linux.sourceforge.net/ for more information.\n\n"
438);
439
440static int __init uml_debugtrace_setup(char *line, int *add)
441{
442 debug_trace = 1;
443 return 0;
444}
445__uml_setup("debugtrace", uml_debugtrace_setup,
446"debugtrace\n"
447" Causes the tracing thread to pause until it is attached by a\n"
448" debugger and continued. This is mostly for debugging crashes\n"
449" early during boot, and should be pretty much obsoleted by\n"
450" the debug switch.\n\n"
451);
452
453/*
454 * Overrides for Emacs so that we follow Linus's tabbing style.
455 * Emacs will notice this stuff at the end of the file and automatically
456 * adjust the settings for this buffer only. This must remain at the end
457 * of the file.
458 * ---------------------------------------------------------------------------
459 * Local variables:
460 * c-file-style: "linux"
461 * End:
462 */