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