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